QQScreenShot之逆向并提取QQ截圖--基本功能

0x0 前言
QQ截圖是我用過的最好用的截圖工具, 由于基本不在電腦上登QQ了, 于是就想將其提取出獨立版
目前除了屏幕錄制功能其他都逆出來了, 在此分享一下, 源碼見附件, 成品太大了放網(wǎng)盤了
(其他功能的效果展示請移步下一個帖子 逆向并提取QQ截圖--OCR和其他功能)
0x1 環(huán)境
版本: QQ 9.5.4.28063
0x2 從COM入手
網(wǎng)上搜一搜就知道QQ歷年版本的截圖功能都放在了Camera.dll里, IDA打開Camera.dll看一下導(dǎo)出表:
(圖 2-1)
大概率是通過COM組件的方式去調(diào)用截圖功能, 那么必然會去調(diào)用CoCreateInstance獲取類的實例, 但是在CoCreateInstance下斷是斷不下來的, 后面可以知道QQ在Common.dll中自己實現(xiàn)了CoCreateInstance函數(shù), 也就是無注冊表的COM調(diào)用
CoCreateInstance的內(nèi)部實現(xiàn)是這樣的:
CoCreateInstance(....) { //....... IClassFactory *pClassFactory=NULL; //↓內(nèi)部調(diào)用DllGetClassObject CoGetClassObject(CLSID_Object, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void **)&pClassFactory); pClassFactory->CreateInstance(NULL, IID_IUnknown, (void**)&pUnk); pClassFactory->Release(); //........ }
無論如何都會調(diào)用DllGetClassObject獲取類廠指針
于是在Camera的DllGetClassObject下斷看一下調(diào)用棧:
(圖2-2)
顯然截圖的命令是被asynctask異步分發(fā)過來的, 那么appframework模塊可能就是調(diào)用源頭了
0x3 分析調(diào)用棧
基址:camera=67CA0000, common=71860000, appframework=6BE40000
看到common.7183713那個調(diào)用棧函數(shù), IDA分析一下:
(圖 3-1)
動態(tài)調(diào)試一下就是知道這個函數(shù)就是QQ自己實現(xiàn)的CoCreateInstance
(圖 3-2)
一直分析到appframework.68EEE54A:
(圖 3-3)
所以直接call (AppFramework+AE46D)函數(shù)(也就是圖3-3), 就可以調(diào)用截圖了, 當(dāng)然應(yīng)該也可以去調(diào)圖3-2中的v33(v21, v25, v4, v20), 但是調(diào)appframework的話, 一些初始化啊, 析構(gòu)的方法就不需要我們?nèi)ゲ傩脑倌M調(diào)用了
我采用的方式是模擬整個(AppFramework+AE46D)函數(shù), 當(dāng)然直接call應(yīng)該也可以, 但是我一開始直接call的時候發(fā)現(xiàn)sub_5081B982(&v17)那個函數(shù)總是返回NULL, 下面自然也就不會執(zhí)行了, 后來才發(fā)現(xiàn)需要初始化PlatformCore
0x4 初始化PlatformCore
在逆向過程中經(jīng)常會發(fā)現(xiàn)程序會去調(diào)用類似下圖的代碼:
(圖 4-1)
看一下Common.dll的導(dǎo)出表:
(圖 4-2)
因為幾乎所有函數(shù)的調(diào)用棧都會經(jīng)HummerEngine.dll所以去這個dll里看了看, 還真就發(fā)現(xiàn)了初始化過程:
(圖 4-3)
直接仿著反編譯結(jié)果寫一份!:
typedef long (WINAPI *LPFN_Util_Com_CreateObjectFromDllFile)(wchar_t const *, struct _GUID const &, struct _GUID const &, void * *, struct IUnknown *); typedef int (__cdecl *LPFN_Util_CoreCenter_InitPlatform)(wchar_t *); typedef int (__cdecl *LPFN_Util_Boot_InitPlatformI18NConfig)(void); HRESULT res = 0; LPFN_Util_Com_CreateObjectFromDllFile lpfnCreateObjectFromDllFile = (LPFN_Util_Com_CreateObjectFromDllFile)CommonMgr.GetFuncAddr("?CreateObjectFromDllFile@Com@Util@@YGJPB_WABU_GUID@@1PAPAXPAUIUnknown@@@Z"); res = lpfnCreateObjectFromDllFile(L"Common.dll", CLSID_Common_Init, IID_ICommon_Init, (void**)(&testPtr), NULL); LPFN_Util_Boot_InitPlatformI18NConfig lpfnInitPlatformI18NConfig = (LPFN_Util_Boot_InitPlatformI18NConfig)CommonMgr.GetFuncAddr("?InitPlatformI18NConfig@Boot@Util@@YAHXZ"); res = lpfnInitPlatformI18NConfig(); LPFN_Util_Boot_InitPlatformFileSystem lpfnInitPlatformFileSystem = (LPFN_Util_Boot_InitPlatformFileSystem)CommonMgr.GetFuncAddr("?InitPlatformFileSystem@Boot@Util@@YAHXZ"); res = lpfnInitPlatformFileSystem(); //... //完整代碼見附件
0x41 其他初始化工作
這樣跑其實還是會崩潰, 調(diào)試一下發(fā)現(xiàn)需要SetMainAndLogicThreadId, 還是模擬一下:
/** * @brief 設(shè)置主線程和邏輯線程ID */ void InitMainAndLogicThreadId() { LPFN_Util_Misc_SetMainAndLogicThreadId lpfnSetMainAndLogicThreadId = (LPFN_Util_Misc_SetMainAndLogicThreadId)CommonMgr.GetFuncAddr("?SetMainAndLogicThreadId@Misc@Util@@YAXKK@Z"); lpfnSetMainAndLogicThreadId(GetCurrentThreadId(), 0); }
0x5 模擬調(diào)用截圖!
其實還是直接仿著(AppFramework+AE46D)函數(shù)的反編譯結(jié)果寫一份就好了, 貼一下部分代碼:
// 模擬AppFramework的調(diào)用函數(shù) DWORD *ITXData1 = NULL, *ITXData2 = NULL; lpfnCreateTXData((void**)&ITXData1); DWORD lpfnITXData_vtbl_func1 = *(DWORD*)(*ITXData1 + 320); char *IDvf1_Arg2 = "type"; OLECHAR *IDvf1_Arg3_c = L"Camera"; BSTR IDvf1_Arg3 = SysAllocString(IDvf1_Arg3_c); __asm { push IDvf1_Arg3 push IDvf1_Arg2 push ITXData1 call lpfnITXData_vtbl_func1 } lpfnCreateTXData((void**)&ITXData2); DWORD lpfnITXData_vtbl_func2 = *(DWORD*)(*ITXData2 + 240); char *IDvf2_arg2_1 = "_Handled_", *IDvf2_arg2_2 = "_SelfHotKey_",*IDvf2_arg2_3 = "_KeyDown_"; __asm { push 0 push IDvf2_arg2_1 push ITXData2 call lpfnITXData_vtbl_func2 push 1 push IDvf2_arg2_2 push ITXData2 call lpfnITXData_vtbl_func2 push 0 push IDvf2_arg2_3 push ITXData2 call lpfnITXData_vtbl_func2 } DWORD lpfnScreenShot = CommonMgr.GetDllBaseAddr() + 0x17CA30; OLECHAR *arg2_c = L"hotkey"; BSTR arg2 = SysAllocString(arg2_c); __asm { push 0 push ITXData2 push ITXData1 push arg2 push 0 call lpfnScreenShot } //printf("Call ScreenShot Func Over\n");
0x6 細(xì)節(jié)處理
保存圖片的時候會崩潰掉, 逆向調(diào)試一下發(fā)現(xiàn), 是在保存的時候:
大概是v50+1388那里, 猜測一下這個函數(shù)是用來寫入上次保存文件路徑的, 直接patch掉好了
還有程序退出的時候也會崩潰, 我進(jìn)行了這樣的處理:
//SetCoreCenterStopState DWORD lpCC_CreateObjectFactory = (CommonMgr.GetDllBaseAddr() + 0x248A3C); *(DWORD*)(lpCC_CreateObjectFactory - 0x4) = 0; *(DWORD*)lpCC_CreateObjectFactory = 1;
就是讓調(diào)用Util::CoreCenter::IsStopped()的時候返回TRUE
0x7 效果
0x8 總結(jié)
這個markdown的圖片都是用提取出來QQScreenShot截圖的, 也沒發(fā)現(xiàn)內(nèi)存泄漏
相關(guān)文章
- “CMOS密碼”就是通常所說的“開機密碼”,主要是為了防止別人使用自已的計算機,設(shè)置的一個屏障2023-08-01
QQScreenShot之逆向并提取QQ截圖--OCR和其他功能
上一篇文章逆向并提取QQ截圖沒有提取OCR功能, 再次逆向我發(fā)現(xiàn)是可以本地調(diào)用QQ的OCR的,但翻譯按鈕確實沒啥用, 于是Patch了翻譯按鈕事件, 改為了將截圖用百度以圖搜圖搜索.2023-02-04- QQ截圖是我用過的最好用的截圖工具, 由于基本不在電腦上登QQ了, 于是就想將其提取出獨立版目前除了屏幕錄制功能其他都逆出來了, 在此分享一下2023-02-04
非系統(tǒng)分區(qū)使用BitLocker加密導(dǎo)致軟件無法安裝的解決方法
很多電腦用戶在考慮自己電腦磁盤分區(qū)安全時會采用 Windows 自帶的 BitLocker 加密工具對電腦磁盤分區(qū)進(jìn)行加密。但有些人加密后就會忘記自己設(shè)置的密碼從而導(dǎo)致在安裝其它軟2020-11-25防止離職員工帶走客戶、防止內(nèi)部員工泄密、避免華為員工泄密事件的發(fā)生
這篇文章為大家詳細(xì)介紹了如何才能防止離職員工帶走客戶、防止內(nèi)部員工泄密、避免華為員工泄密事件的發(fā)生,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-27徹底防止計算機泄密、重要涉密人員離職泄密、涉密人員離崗離職前防范舉
近些年企業(yè)商業(yè)機密泄漏的事件屢有發(fā)生,這篇文章主要教大家如何徹底防止計算機泄密、重要涉密人員離職泄密、告訴大家涉密人員離崗離職前的防范舉措,具有一定的參考價值,2017-06-27- 最近有電腦用戶反應(yīng)量子計算機可以破解下載的所有的加密算法嗎?其實也不是不可以,下面虛擬就為大家講解買臺量子計算機,如何分分鐘破解加密算法2016-09-26
怎么破解Webshell密碼 Burpsuite破解Webshell密碼圖文教程
webshell是以asp、php、jsp或者cgi等網(wǎng)頁文件形式存在的一種命令執(zhí)行環(huán)境,一種網(wǎng)頁后門。黑客通常會通過它控制別人網(wǎng)絡(luò)服務(wù)器,那么怎么破解webshell密碼呢?一起來看看吧2016-09-19- 本文討論了針對Linux系統(tǒng)全盤加密的冷啟動攻擊,大家都認(rèn)為這種攻擊是可行的,但執(zhí)行這么一次攻擊有多難?攻擊的可行性有多少呢?需要的朋友可以參考下2015-12-28
防止泄露公司機密、企業(yè)數(shù)據(jù)防泄密軟件排名、電腦文件加密軟件排行
面對日漸嚴(yán)重的內(nèi)部泄密事件,我們?nèi)绾问刈o企業(yè)的核心信息,如何防止內(nèi)部泄密也就成了擺在各個企業(yè)領(lǐng)導(dǎo)面前的一大問題。其實,針對內(nèi)網(wǎng)安全,防止內(nèi)部信息泄漏早已有了比較2015-12-17