QQScreenShot之逆向并提取QQ截图--基本功能 - 网站

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

分类:加密解密 · 发布时间:2023-04-13 21:53 · 阅读:3211

QQ截图是我用过的最好用的截图工具, 由于基本不在电脑上登QQ了, 于是就想将其提取出独立版目前除了屏幕录制功能其他都逆出来了, 在此分享一下

0x0 前言

QQ截图是我用过的最好用的截图工具, 由于基本不在电脑上登QQ了, 于是就想将其提取出独立版
目前除了屏幕录制功能其他都逆出来了, 在此分享一下, 源码见附件, 成品太大了放网盘了

(其他功能的效果展示请移步下一个帖子 逆向并提取QQ截图--OCR和其他功能)

0x1 环境

版本: QQ 9.5.4.28063

0x2 从COM入手

网上搜一搜就知道QQ历年版本的截图功能都放在了Camera.dll里, IDA打开Camera.dll看一下导出表:

Camera_Export

(图 2-1)

大概率是通过COM组件的方式去调用截图功能, 那么必然会去调用CoCreateInstance获取类的实例, 但是在CoCreateInstance下断是断不下来的, 后面可以知道QQ在Common.dll中自己实现了CoCreateInstance函数, 也就是无注册表的COM调用

CoCreateInstance的内部实现是这样的:

 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下断看一下调用栈:

callstack1

(图2-2)

显然截图的命令是被asynctask异步分发过来的, 那么appframework模块可能就是调用源头了

0x3 分析调用栈

基址:camera=67CA0000, common=71860000, appframework=6BE40000

看到common.7183713那个调用栈函数, IDA分析一下:

CoCreateInstanceWithDllname

(图 3-1)
动态调试一下就是知道这个函数就是QQ自己实现的CoCreateInstance

 common.719DB0A9:

sc_call
(图 3-2) 

一直分析到appframework.68EEE54A:

call_source
(图 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

在逆向过程中经常会发现程序会去调用类似下图的代码:
getplatformcore
(图 4-1)
看一下Common.dll的导出表:
common_platform
(图 4-2) 

因为几乎所有函数的调用栈都会经HummerEngine.dll所以去这个dll里看了看, 还真就发现了初始化过程:

hummer_init
(图 4-3)

直接仿着反编译结果写一份!:

 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, 还是模拟一下:

 /** * @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)函数的反编译结果写一份就好了, 贴一下部分代码:

 // 模拟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 细节处理

保存图片的时候会崩溃掉, 逆向调试一下发现, 是在保存的时候:

save_crash

大概是v50+1388那里, 猜测一下这个函数是用来写入上次保存文件路径的, 直接patch掉好了
还有程序退出的时候也会崩溃, 我进行了这样的处理:

 //SetCoreCenterStopState DWORD lpCC_CreateObjectFactory = (CommonMgr.GetDllBaseAddr() + 0x248A3C); *(DWORD*)(lpCC_CreateObjectFactory - 0x4) = 0; *(DWORD*)lpCC_CreateObjectFactory = 1;

就是让调用Util::CoreCenter::IsStopped()的时候返回TRUE

0x7 效果

demo

0x8 总结

这个markdown的图片都是用提取出来QQScreenShot截图的, 也没发现内存泄漏

标签:
qq截图

相关文章

手动脱壳入门第十五篇 FSG 1.33

【脱文标题】 手动脱壳入门第十五篇 FSG 1.33 【脱文作者】 weiyi75[Dfcg] 【作者邮箱】 weiyi75@sohu.com 【作者主页】 Dfcg官方大本营 【使用工具】 Peid,Ollydbg,ImportREC,Loadpe 【脱壳平台】 Win2K/XP 【软件名称】 Unpackme 【软件简介】

OllyDBG反汇编破解Radmin密码

Radmin 是一款很不错的服务器管理 无论是 远程桌面控制 还是 文件传输 速度都很快 很方便 这样也形成了 很多服务器都装了 radmin这样的 现在你说 4899默认端口 没密码的 服务器你上哪找? 大家都知道radmin的密码都是32位md5加密后 存放在注册表里的

Skype的数字身份识别和加密

为了实现这些目标,Skype向每位Skype用户发放了一份“数字证书”,任何Skype用户可凭借此证书确定发出或接受Skype通话或聊天请求的人员身份……   Skype主要目标之一是防止我们用户受到未授权的窃听。 出于这一考虑,我们希望防止行骗者通过电

QQScreenShot之逆向并提取QQ截图--OCR和其他功能

上一篇文章逆向并提取QQ截图没有提取OCR功能, 再次逆向我发现是可以本地调用QQ的OCR的,但翻译按钮确实没啥用, 于是Patch了翻译按钮事件, 改为了将截图用百度以图搜图搜索. 并Patch掉了长截图的标签显示, 这样win10下就也可以用长截图功能了

七种武器 让你轻松破解“还原精灵”

有些时候你是不是有些烦还原精灵,刚对系统进行的配置,重启后又恢复原貌了,本文就给大家讲了还原精灵的七大破解方法……   方法一:临时解决办法   如果在忘记还原精灵密码后,你安装了一个新程序,该软件提示要求重启计算机,此时不要点击“

返回分类 返回首页