0x0 前言
QQ截图是我用过的最好用的截图工具, 由于基本不在电脑上登QQ了, 于是就想将其提取出独立版
目前除了屏幕录制功能其他都逆出来了, 在此分享一下, 源码见附件, 成品太大了放网盘了
链接:https://pan.baidu.com/s/1yENiFF3KDdZTDfqig6X98A
提取码:oa7c
(其他功能的效果展示请移步下一个帖子 逆向并提取QQ截图--OCR和其他功能)
0x1 环境
版本: QQ 9.5.4.28063
0x2 从COM入手
网上搜一搜就知道QQ历年版本的截图功能都放在了Camera.dll里, IDA打开Camera.dll看一下导出表:
(图 2-1)
大概率是通过COM组件的方式去调用截图功能, 那么必然会去调用CoCreateInstance获取类的实例, 但是在CoCreateInstance下断是断不下来的, 后面可以知道QQ在Common.dll中自己实现了CoCreateInstance函数, 也就是无注册表的COM调用
CoCreateInstance的内部实现是这样的:
1 2 3 4 5 6 7 8 9 10 | CoCreateInstance(....)
{
/ / .......
IClassFactory * pClassFactory = NULL;
/ / ↓内部调用DllGetClassObject
CoGetClassObject(CLSID_Object, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void * * )&pClassFactory);
pClassFactory - >CreateInstance(NULL, IID_IUnknown, (void * * )&pUnk);
pClassFactory - >Release();
/ / ........
}
|
无论如何都会调用DllGetClassObject获取类厂指针
于是在Camera的DllGetClassObject下断看一下调用栈:
(图2-2)
显然截图的命令是被asynctask异步分发过来的, 那么appframework模块可能就是调用源头了
0x3 分析调用栈
基址:camera=67CA0000, common=71860000, appframework=6BE40000
看到common.7183713那个调用栈函数, IDA分析一下:
(图 3-1)
动态调试一下就是知道这个函数就是QQ自己实现的CoCreateInstance
common.719DB0A9:
(图 3-2)
一直分析到appframework.68EEE54A:
(图 3-3)
所以直接call (AppFramework+AE46D)函数(也就是图3-3), 就可以调用截图了, 当然应该也可以去调图3-2中的v33(v21, v25, v4, v20), 但是调appframework的话, 一些初始化啊, 析构的方法就不需要我们去操心再模拟调用了
我采用的方式是模拟整个(AppFramework+AE46D)函数, 当然直接call应该也可以, 但是我一开始直接call的时候发现sub_5081B982(&v17)那个函数总是返回NULL, 下面自然也就不会执行了, 后来才发现需要初始化PlatformCore
0x4 初始化PlatformCore
在逆向过程中经常会发现程序会去调用类似下图的代码:
(图 4-1)
看一下Common.dll的导出表:
(图 4-2)
因为几乎所有函数的调用栈都会经HummerEngine.dll所以去这个dll里看了看, 还真就发现了初始化过程:
(图 4-3)
直接仿着反编译结果写一份!:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 其他初始化工作
这样跑其实还是会崩溃, 调试一下发现需要SetMainAndLogicThreadId, 还是模拟一下:
1 2 3 4 5 6 7 8 9 | / * *
* @brief 设置主线程和逻辑线程 ID
* /
void InitMainAndLogicThreadId()
{
LPFN_Util_Misc_SetMainAndLogicThreadId
lpfnSetMainAndLogicThreadId = (LPFN_Util_Misc_SetMainAndLogicThreadId)CommonMgr.GetFuncAddr( "?SetMainAndLogicThreadId@Misc@Util@@YAXKK@Z" );
lpfnSetMainAndLogicThreadId(GetCurrentThreadId(), 0 );
}
|
0x5 模拟调用截图!
其实还是直接仿着(AppFramework+AE46D)函数的反编译结果写一份就好了, 贴一下部分代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | / / 模拟AppFramework的调用函数
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 细节处理
保存图片的时候会崩溃掉, 逆向调试一下发现, 是在保存的时候:
大概是v50+1388那里, 猜测一下这个函数是用来写入上次保存文件路径的, 直接patch掉好了
还有程序退出的时候也会崩溃, 我进行了这样的处理:
1 2 3 4 | / / SetCoreCenterStopState
DWORD lpCC_CreateObjectFactory = (CommonMgr.GetDllBaseAddr() + 0x248A3C );
* (DWORD * )(lpCC_CreateObjectFactory - 0x4 ) = 0 ;
* (DWORD * )lpCC_CreateObjectFactory = 1 ;
|
就是让调用Util::CoreCenter::IsStopped()的时候返回TRUE
0x7 效果
0x8 总结
这个markdown的图片都是用提取出来QQScreenShot截图的, 也没发现内存泄漏
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界
最后于 2022-4-17 18:11
被0xEEEE编辑
,原因: 修改介绍