首页
社区
课程
招聘
[原创]逆向调用QQ截图NT与WeChatOCR
2023-7-26 13:07 33219

[原创]逆向调用QQ截图NT与WeChatOCR

2023-7-26 13:07
33219

逆向调用QQ截图NT与WeChatOCR

已逆出64位的QQ Mojo Ipc通信过程, 请点击下方链接下载QQImpl项目, 此项目将QQ的Mojo Ipc和WeChatOCR独立了出来, 可以在任何项目中使用:
https://github.com/EEEEhex/QQImpl

采用QQImpl的成品在下方链接下载:
链接:QQScreenShotNT-Plus
提取码:ctjs

QQ出Electron版了, 将截图独立出来了(应该是个过渡, 之后感觉会采用Electron重写), 并且使用的OCR改成了TencentOCR(低版本的WeChatOCR)

本文将重点介绍如何逆向调用WeChatOCR

0x00 逆向调用QQ截图NT

0x01 前期调研

先把QQ运行起来, 看看QQ NT的截图是什么情况的:
QQRUN
注意到 有一个--pcqq-platform-channel-handle的命令行参数, 并且存在一个有mojo字样的命名管道
那就搜索一下, 可以在 这篇文章 里发现, mojo是Chrome实现的一套IPC机制, 并且想要简单使用的话, mojo IPC有一套Invitation机制。
Invitation简单来说就是创建子进程, 并给它传命令行--mojo-platform-channel-handle=xxx。
显然QQ就是从这个基础上改了一个命令行参数, 本质就是采用Mojo IPC进行通信。
那就开逆! (一开始分析的是QQNT 64位版本的, 后面为了方便分析用的是9.9.0.14619 x86版本的, 可能地址会变化)

0x02 分析QQScreenshot.exe

先从被调用端分析, 直接分析QQ太大了, 先小看一下main函数:
QQScreenShot_main
这个main函数跟我以前逆的结果差不多哇, 总的来说就是启动QQ截图的环境, 图上画框的函数是启动IPC的, 进去看看:
childipc_start
还是COM的形式, 这里碍于篇幅不再介绍child ipc的过程, 直接看parent ipc的过程
(其实可以发现parent-ipc-core-x86.dll和child-ipc-core-x86.dll是同一个代码编译的DLL)

既然QQScreenshot是通过加载child-ipc-core-x86.dll再通过DllGetClassObject获取COM对象.
那QQ也很有可能是通过加载parent-ipc-core再动态获取COM对象实现的

那就动调一下QQ.exe, 搜索一下这种情况的代码:

1
DllGetClassObject = GetProcAddress(LibraryA, "DllGetClassObject");

0x03 分析parent-ipc-core-x86.dll

定位到DllGetClassObject字符串:
wrapper_init_ipc
可以看到和QQScreenshot.exe里初始化IPC的过程一模一样, 从parent-ipc-core-x86.dll中获取了一个COM对象
看一下这个COM对象的实现了哪些方法:
parent_ipc_vtable
前三个函数是实现的IUnknown接口不用管, 其他的方法都可以通过字符串猜出来, 只要确定他们的参数类型就好了, 这里碍于篇幅省略掉(动调能直接看出来)
只介绍一下pCOM->LaunchChildProcess(...)方法:

0x04 LaunchChildProcess方法

通过动调可以发现LaunchChildProcess方法的第5个参数是用来设置接收IPC MSG函数的

在LaunchChildProcess方法里, 会调用base::Thread::task_runner新起一个线程来创建截图子进程↓
LaunchChildProcess

怎么确定的这些函数呢?, 因为新版QQ本身就是一个Chrome嘛, 直接通过字符串去搜Chrome的源代码就OK了。
下面进LaunchQQScreenShot函数看看:
LaunchQQScreenShot
整个函数完全就是参考文章[1]里面介绍的Invitation机制。
这个函数其实是LaunchChildProcessInternal, 是LaunchChildProcess的内部函数,用来创建子进程:

1
pIMojoIpc->LaunchChildProcess(wchar_t *path, (char*)[] cmdlines, DWORD64 cmdlines_num, LPVOID callback, LPVOID lpClass);//最后一个参数是callback的第一个参数 是一个类指针

而LaunchChildProcess的最后一个参数是接收IPC消息回调函数的第一个参数。
之后就是仿写一遍就能调用了, 详细信息还是请见QQImpl64.cpp:
code

0x10 逆向调用WeChatOCR

QQ采用的TencetnOCR就是低版本的WeChatOCR, 他们也是采用Mojo IPC进行通信

0x11 前期调研

通过 这篇文章 可以知道, 在Mojo中要想调用其他服务要通过Services Manager的转发, 也就是说QQScreenshot.exe是ServiceA, TencentOCR.exe是Service B, QQ.exe则是Service Manager(我猜的, 对不对也无伤大雅):
mojo_service

那想要分析OCR调用过程就不得不用IDA分析一下wrapper.node了(QQ的一个DLL, 主要逻辑都在这个DLL里)

0x12 定位OCR调用函数

通过搜索字符串可以发现很多个感兴趣的:
ocr_string
直接进DoOCRTask函数看看:
在DoOCRTask函数里进行了OCR的初始化以及OCR任务的发送, 即OCRdoInit和SendOCRTask函数
DoOCRTask
那我们需要分析一下是怎么实现的, 毕竟过程是QQScreenshot把IPC消息发给了我们的程序, 我们的程序要去调用OCR

0x13 分析OCRdoInit函数

首先初始化了TencentOCR.exe和mmmojo.dll的路径, 并很明显赋值给了一个类的成员变量:
OCRdoInit_path
之后就是进行了Mojo环境的初始化:在这里设置了命令行参数与回调函数
OCRdoInit_mmmojo
其中(*v11+4)这个函数是这样的:
mmmojo_func
就是获取mmmojo.dll的导出函数并保存起来供后面调用
在SetMMMojoEnvironmentCallbacks函数里设置了接收IPC消息的函数这个后面分析

0x14 分析SendOCRTask函数

在SendOCRTask函数里调用了很多方法, 一开始确实看蒙了, 但是通过后面分析可以发现其实逻辑很简单
IPB_CPB
函数一开始初始化了一些IPbMsg, CPbMsg什么的类, 一开始确实分析不出来这是啥玩意儿, 难不成为了给OCR发个IPC消息还得自己去实现这么多类? 那工作量可太大了(但其实不用, 通过后面的分析就会豁然开朗)
SendOCRTask
之后SendOCRTask函数调用CreateMMMojoWriteInfo创建了一个write_info这个write_info具体是啥现在也是不清楚, 然后拿到了WriteInfoRequest, 并调用memmove往里写东西, 最后调用了SendMMMojoWriteInfo发送出去, 那我们看看往里写了啥:
write_info
wow 龙头出来了! 发现图片路径了, 但问题是前八个字节是啥?
先不管了 既然发送函数分析不出来那就先去看接收IPC MSG的函数

0x15 分析接收IPC MSG的函数

通过分析SetMMMojoEnvironmentCallbacks的参数, 可以发现, 它设置的回调函数是一个虚表里的函数:
vTable_OCRMANAGER
看看OnReadPush:
OnReadPush
在这个函数里发现了protobuf的字样! (具体在哪发现的我忘了,反正确实有)
我们直接大胆猜测writeinfo、readinfo全是protobuf! 把数据扣下来测试一下(用protobuf-inspector这个工具):
protobuf
成功了! 同理, readinfo里都是OCR结果的数据包括识别的坐标、UTF8文字、识别率啥的

0x16 分析protobuf格式

数据格式其实很容易看出来, 大体都能猜出来
比如发送的格式:

1
2
3
4
5
6
7
8
9
10
message OcrRequest {
    int32 unknow = 1;                               //必定为0
    int32 task_id = 2;
 
    message PicPaths {
        repeated string pic_path = 1;
    }
 
    PicPaths pic_path = 3;
}

接收OCR结果的数据格式:

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
message OcrResponse {
    int32 type = 1;                                 //第一次运行OCR会有push一次type1, 正常OCR结束type0
    int32 task_id = 2;
    int32 err_code = 3;
 
    message OcrResult {
        message ResultPos {                         //四个角的坐标 左上 右上 右下 左下
            message PosXY {
                float x = 1;
                float y = 2;
            }
 
            repeated PosXY pos = 1;
        }
 
        message SingleResult {                      //SingleResult是一行结果 OneResult是单字的
            ResultPos single_pos = 1;
            bytes single_str_utf8 = 2;              //UTF8格式的字符串
            float single_rate = 3;                  //单行的识别率
 
            message OneResult {
                ResultPos one_pos = 1;
                bytes one_str_utf8 = 2;
            }
 
            repeated OneResult one_result = 4;
            float lx = 5;                           //识别矩形的左上和右下的坐标? 可能是
            float ly = 6;
            float rx = 7;
            float ry = 8;
            int32 unknown_0 = 9;                    //未知
            ResultPos unknown_pos = 10;             //未知
        }
 
        repeated SingleResult single_result = 1;    //repeated 每行的结果
        int32 unknown_1 = 2;
        int32 unknown_2 = 3;
    }
 
    OcrResult ocr_result = 4;
}

编译一下google的protobuf库, 再拿protoc生成一下头文件和.cc就能用了

WeChatOCR同理, 调用方法是一样的

成品展示

demo

结束语

其实有很多细节没有写出来, 比如QQ设置的接收IPC消息的函数的分析, 比如SendOCRTask第一次调用其实是在OnRemoteConnect回调函数里新起了一个线程异步调用的, 再比如OCR初始化的时候需要SetMMMojoEnvirenmentInitParam来设置--usr-lib,就是mmmojo_64.dll所在的目录。
具体的细节都可以去看QQImpl的代码, 文章中没写出来的代码里都有哦。

参考文章

[1] Mojo IPC 设计与Invitation机制实现
[2] Chrome Code Search
[3] Chromium Mojo & IPC


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2023-9-8 11:33 被0xEEEE编辑 ,原因: 新增QQImpl代码与NTPlus版本
上传的附件:
收藏
免费 27
打赏
分享
最新回复 (40)
雪    币: 3996
活跃值: (2947)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
文西哥 2023-7-26 14:06
2
0
沙发,坐等完整的分析,谢谢大佬
雪    币: 589
活跃值: (3881)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
codeoooo 2023-7-26 16:33
3
0
tql
雪    币: 201
活跃值: (2213)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
saloyun 2023-7-26 19:16
4
0
tql,你是跟QQ截图杠上了,不过这个是真的香,哈哈。
雪    币: 771
活跃值: (577)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
我想你了 2023-7-26 19:20
5
0
学习了
雪    币: 20758
活跃值: (30127)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2023-7-27 08:58
6
1
感谢分享
雪    币: 3436
活跃值: (4145)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
青眼白龙 2023-7-27 18:53
7
0
可以的,感谢分享
雪    币: 3213
活跃值: (1884)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
935 2023-7-28 08:31
8
0
谢谢大佬分享知识!!!
雪    币: 7583
活跃值: (3531)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
十年后 2023-7-28 09:16
9
0
向大佬学习!!!
雪    币: 110
活跃值: (2868)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
maidou 2023-7-28 20:28
10
0
谢谢大佬分享知识!!!
雪    币: 1765
活跃值: (6088)
能力值: ( LV7,RANK:102 )
在线值:
发帖
回帖
粉丝
fjqisba 2023-7-29 08:19
11
0
可以白嫖OCR了
雪    币: 2881
活跃值: (3183)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
院士 2023-7-29 09:27
12
0
厉害了,来学习。
雪    币: 47
活跃值: (114)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
qianbidao 2023-8-3 18:03
13
0
厉害了 大佬
雪    币: 621
活跃值: (808)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lwykj 2023-8-3 18:56
14
0
感谢分享  
雪    币: 101
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_pdeivpxj 2023-8-9 10:29
15
0
 牛的
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
一个新人的哥 2023-8-15 18:23
16
0
你好,发现一个重大的问题,在你发布的逆向提取QQ截取--OCR和其他功能,截的图片体积很小,特别是对截图后划线或加文字后,保存的图片体积小,很模糊,这个怎么办?有设置图片质量的地方吗?
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_mndqtppt 2023-8-30 21:02
17
0
QQ附近人,大佬有吗,重谢
雪    币: 239
活跃值: (22)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
172721404 2023-9-7 04:59
18
0
这个还是好用的就是不登录QQ的时候截长图很棒
雪    币: 589
活跃值: (200)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
无善无我 2023-9-10 19:52
19
0

tql大佬,QQ的截图确实很方便,不过NT版本把录屏给分出来了,不知道后面会不会再合并起来

最后于 2023-9-10 19:55 被无善无我编辑 ,原因:
雪    币: 368
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_skcquzvw 2023-9-12 19:43
20
0

请问下"翻译"和"以图搜图"都使用不了(点击后无响应),会是什么原因?有遇到一样情况的吗?

是我的设置项哪里不对吗?

雪    币: 4163
活跃值: (4015)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
0xEEEE 2 2023-9-22 10:37
21
0
mb_skcquzvw 请问下"翻译"和"以图搜图"都使用不了(点击后无响应),会是什么原因?有遇到一样情况的吗?是我的设置项哪里不对吗?
这俩功能需要python环境,因为是C++调Python实现的,你可以查看README.md去设置一下
雪    币: 110
活跃值: (2868)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
maidou 2023-9-24 10:17
22
0
啥时候这个版本整合下录屏就完美了!您以前的那个工具也很棒!持续关注!
雪    币: 232
活跃值: (148)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
nzkboy 2023-10-17 09:33
23
0
按照readmi将pyenv放在了指定的目录下,并且在设置中开启了插件。但是软件启动后还是提示Python启动失败。这是为什么。
win7 64位操作系统。
其中1台可以OCR,2台不可以。
不知道还需要什么环境。
----------------
提示:
在win7系统下启动这个软件,提示缺少api-ms-win-core-path-l1-1-0.dll这个文件,从网上找到并放到指定目录下后,这个软件才可以使用了。
建议:
将这个dll打包到软件中。
雪    币: 3436
活跃值: (4145)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
青眼白龙 2023-10-17 19:42
24
0
感谢分享,学习了
雪    币: 77
活跃值: (54)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
chinabox 2023-10-28 14:14
25
0
感谢分享,向大佬学习
游客
登录 | 注册 方可回帖
返回