Nisy 发表于 2018-1-9 11:07:26

010 Editor v8.x 启动时网络验证绕过方案(多.维度击破)



早上打开之后提示网验失败并且已过期。动手分析下如何快速绕过:

有很多字符串可用,于是定位到如果 == 0 则认为是注册版(这里其实对破解上用处不大,只是作为一个定位后堆栈回溯的点)。


00007FF7F58938EC | FF 15 6E 50 BB 01| call qword ptr ds:[<&QLoggingRule::~QLogging |
00007FF7F58938F2 | 8B 87 8C 01 00 00| mov eax,dword ptr ds:               |
00007FF7F58938F8 | 3D DB 00 00 00   | cmp eax,DB                                 | 返回了 113
00007FF7F58938FD | 0F 85 F7 00 00 00| jne 010editor.7FF7F58939FA                   | 不可以跳转
00007FF7F5893903 | 48 8B 0D 8E 92 9A| mov rcx,qword ptr ds:          |
00007FF7F589390A | 83 79 30 01      | cmp dword ptr ds:,1                  | 是否注册的标志位
00007FF7F589390E | 75 2D            | jne 010editor.7FF7F589393D                   |
00007FF7F5893910 | 48 8D 15 D9 25 EE| lea rdx,qword ptr ds:          | 00007FF7F6775EF0:"Registered - Single User License"
00007FF7F5893917 | 48 8D 4D 30      | lea rcx,qword ptr ss:                |
00007FF7F589391B | FF 15 E7 60 BB 01| call qword ptr ds:[<&QString::QString>]      |


通过堆栈回溯几层,来到这里,只要这里最后再跳即可进入程序界面:


00007FF7F5E0135B | 81 FB 77 01 00 00| cmp ebx,177                                 |
00007FF7F5E01361 | 74 35            | je 010editor.7FF7F5E01398                     |
00007FF7F5E01363 | 83 FB 2F         | cmp ebx,2F                                    | 2F:'/'
00007FF7F5E01366 | 74 30            | je 010editor.7FF7F5E01398                     |
00007FF7F5E01368 | 81 FB ED 00 00 00| cmp ebx,ED                                    |
00007FF7F5E0136E | 74 28            | je 010editor.7FF7F5E01398                     |
00007FF7F5E01370 | 81 FB 13 01 00 00| cmp ebx,113                                 |
00007FF7F5E01376 | 74 20            | je 010editor.7FF7F5E01398                     |
00007FF7F5E01378 | 81 FB 0C 02 00 00| cmp ebx,20C                                 |
00007FF7F5E0137E | 0F 85 DF 00 00 00| jne 010editor.7FF7F5E01463                  | 要从这里跳走 即可绕过启动时的验证弹框
00007FF7F5E01384 | 48 8B 0D 0D B8 43| mov rcx,qword ptr ds:         |
00007FF7F5E0138B | E8 30 1E 3B FF   | call 010editor.7FF7F51B31C0                   |
00007FF7F5E01390 | 85 C0            | test eax,eax                                  |
00007FF7F5E01392 | 0F 84 C4 00 00 00| je 010editor.7FF7F5E0145C                     |


这样修改后程序就可以进入主界面,但是显示已经过期。

我们同时发现EBX的值为核心所在,搜索常量113,来到这里:


00007FF7F5CA61F0 | 48 89 5C 24 08   | mov qword ptr ss:,rbx                  | :QArrayData::shared_null
00007FF7F5CA61F5 | 57               | push rdi                                    |
00007FF7F5CA61F6 | 48 83 EC 20      | sub rsp,20                                    |
00007FF7F5CA61FA | 83 79 3C 00      | cmp dword ptr ds:,0                   | IsRegister ... must zero   (通过验证 or 过期)
00007FF7F5CA61FE | 8B FA            | mov edi,edx                                 |
00007FF7F5CA6200 | 48 8B D9         | mov rbx,rcx                                 |
00007FF7F5CA6203 | 74 10            | je 010editor.7FF7F5CA6215                     | JMP
00007FF7F5CA6205 | B8 13 01 00 00   | mov eax,113                                 | remember 113
00007FF7F5CA620A | 48 8B 5C 24 30   | mov rbx,qword ptr ss:               |
00007FF7F5CA620F | 48 83 C4 20      | add rsp,20                                    |
00007FF7F5CA6213 | 5F               | pop rdi                                       |
00007FF7F5CA6214 | C3               | ret                                           |
00007FF7F5CA6215 | E8 2C DB 50 FF   | call 010editor.7FF7F51B3D46                   |
00007FF7F5CA621A | 83 F8 2D         | cmp eax,2D                                    | 2D:'-'
00007FF7F5CA621D | 0F 84 CB 00 00 00| je 010editor.7FF7F5CA62EE                     |
00007FF7F5CA6223 | 83 F8 4E         | cmp eax,4E                                    | 4E:'N'
00007FF7F5CA6226 | 0F 84 90 00 00 00| je 010editor.7FF7F5CA62BC                     |


我们发现这里默认不跳直接赋值113了,于是将赋值为0(这里可能是判断是否已过期,过期将走网络验证),让其走后续流程。
(有兴趣的话大家可以自己定位下该数据是哪里赋值的,可能是网络验证的返回值,如果启动的时候把网络验证直接retn可能启动速度会更快一些)



由于我本机保存了注册信息,所以修改后我们不仅绕过了启动验证,也可以在程序界面显示注册信息。



===============================================

方法2:

我们看一下网络发包:
http://www.sweetscape.com/cgibin ... d=0&chk=33086&typ=0



未通过网络验证的情况下返回 <ss>invalid</ss>, 而随意输入的key返回值是"error":



我们在程序里搜索该字符串:



然后我们将字符串比较处修改,将字符串指向 invalid+2 的地址,



修改并保存(将 error 和 invalid 字符串 都修改为 invalid+2 的地址):




至此,被封掉的KEY返回的错误值已被程序定义为通过了网络验证(将 error 修改为 invalid 字符串是不OK的 !)。


00007FF6EF597621 | 4C 8D 05 34 AC E2 00      | lea r8,qword ptr ds:         | 00007FF6F03C225C:"error"
00007FF6EF597628 | 0F 1F 84 00 00 00 00 00   | nop dword ptr ds:               | 将上方的字符串地址修改为 invalid 地址
00007FF6EF597630 | 0F B6 04 0A               | movzx eax,byte ptr ds:            |
00007FF6EF597634 | 48 FF C1                  | inc rcx                                    |
00007FF6EF597637 | 41 3A 44 08 FF            | cmp al,byte ptr ds:            |
00007FF6EF59763C | 75 0E                     | jne 010editor.7FF6EF59764C               |
00007FF6EF59763E | 48 83 F9 06               | cmp rcx,6                                  |
00007FF6EF597642 | 75 EC                     | jne 010editor.7FF6EF597630               |
00007FF6EF597644 | 8D 41 F4                  | lea eax,qword ptr ds:               |
00007FF6EF597647 | E9 AC 00 00 00            | jmp 010editor.7FF6EF5976F8               | 若为 error 则直接失败
00007FF6EF59764C | 33 C0                     | xor eax,eax                              |
00007FF6EF59764E | 48 8D 54 24 30            | lea rdx,qword ptr ss:            |
00007FF6EF597653 | 4C 8D 05 06 38 F9 00      | lea r8,qword ptr ds:         | 00007FF6F052AE60:"invalid"
00007FF6EF59765A | 66 0F 1F 44 00 00         | nop word ptr ds:                  |
00007FF6EF597660 | 0F B6 0C 02               | movzx ecx,byte ptr ds:            |
00007FF6EF597664 | 48 FF C0                  | inc rax                                    |
00007FF6EF597667 | 41 3A 4C 00 FF            | cmp cl,byte ptr ds:            | 比较字符串
00007FF6EF59766C | 75 19                     | jne 010editor.7FF6EF597687               |
00007FF6EF59766E | 48 83 F8 08               | cmp rax,8                                  |
00007FF6EF597672 | 75 EC                     | jne 010editor.7FF6EF597660               |
00007FF6EF597674 | C7 43 3C 01 00 00 00      | mov dword ptr ds:,1                | 若为 invalid 则赋值为1 验证失败
00007FF6EF59767B | E8 3A 2A 51 FF            | call 010editor.7FF6EEAAA0BA                |
00007FF6EF597680 | BA 01 00 00 00            | mov edx,1                                  |
00007FF6EF597685 | EB 0E                     | jmp 010editor.7FF6EF597695               |
00007FF6EF597687 | C7 43 3C 00 00 00 00      | mov dword ptr ds:,0                | 跳向网络验证成功



BTW: 或者将 invalid 字符串修改掉(修改任意字符即可)即可过网络验证。 感谢 不破不立 分享的改方案。

00007FF7F6C3AE5000 00 00 00 00 00 00 00 73 73 00 00 00 00 00 00........ss......
00007FF7F6C3AE6069 6E 76 61 6C 69 64 00 00 00 00 00 3C 25 73 3Einvalid.....<%s>
00007FF7F6C3AE7000 00 00 00 3C 2F 25 73 3E 00 00 00 00 00 00 00....</%s>.......


===============================================

补充(方法三):
上文提到了 cmp dword ptr ds:,0 该值为是否走网络验证的一个关键值(可能是是否过期也可能是是否联网验证通过)。我们分析一下该数值的由来,第一次调用处堆栈回溯:


00007FF7F5E011F2 | E8 4B 25 0D 00               | call <010editor.operator new>                      |
00007FF7F5E011F7 | 48 89 45 77                  | mov qword ptr ss:,rax                      |
00007FF7F5E011FB | 48 85 C0                     | test rax,rax                                       |
00007FF7F5E011FE | 74 0A                        | je 010editor.7FF7F5E0120A                        |
00007FF7F5E01200 | 48 8B C8                     | mov rcx,rax                                        | 申请对象
00007FF7F5E01203 | E8 68 6F 3B FF               | call 010editor.7FF7F51B8170                        | 初始化对象
00007FF7F5E01208 | EB 03                        | jmp 010editor.7FF7F5E0120D                         |
00007FF7F5E0120A | 48 8B C7                     | mov rax,rdi                                        |
00007FF7F5E0120D | 48 89 05 84 B9 43 01         | mov qword ptr ds:,rax                |
00007FF7F5E01214 | 48 8B C8                     | mov rcx,rax                                        |
00007FF7F5E01217 | E8 3F 75 3B FF               | call 010editor.7FF7F51B875B                        | 这里为对象的成员赋值
00007FF7F5E0121C | 48 8B 0D 75 B9 43 01         | mov rcx,qword ptr ds:                |


首先从注册表读取一个DWORD




00007FF7F5CA8A13 | 48 8D 15 DE 29 F9 00         | lea rdx,qword ptr ds:         | 00007FF7F6C3B3F8:"ThreadingModel"
00007FF7F5CA8A1A | 48 8D 4D B0                  | lea rcx,qword ptr ss:               |
00007FF7F5CA8A1E | FF 15 E4 0F 7A 01            | call qword ptr ds:[<&QString::QString>]   |
00007FF7F5CA8A24 | 90                           | nop                                       |
00007FF7F5CA8A25 | 4D 8B C6                     | mov r8,r14                                  | r14:"{FA1395FC-83C3-0732-7D11-0134937462A0}"
00007FF7F5CA8A28 | 48 8D 55 C8                  | lea rdx,qword ptr ss:               |
00007FF7F5CA8A2C | 48 8D 4D D0                  | lea rcx,qword ptr ss:               | :QArrayData::shared_null
00007FF7F5CA8A30 | E8 44 16 51 FF               | call 010editor.7FF7F51BA079               |
00007FF7F5CA8A35 | 90                           | nop                                       |
00007FF7F5CA8A36 | 4C 8D 05 D3 29 F9 00         | lea r8,qword ptr ds:          | 00007FF7F6C3B410:"\\InProcServer32A\\"
... ...
00007FF7F5CA8AD2 | FF 15 98 02 7A 01            | call qword ptr ds:[<&QString::toUInt>]      |
00007FF7F5CA8AD8 | 89 07                        | mov dword ptr ds:,eax                  | 这里将 ThreadingModel 的 hex 值写入内存(!**将这里修改为0即可!)
00007FF7F5CA8ADA | B3 01                        | mov bl,1                                    |




然后对其运算(该函数的返回值决定是否走网络验证分支):

00007FF7F5CA4A70 | 81 F1 D4 9C 32 C7            | xor ecx,C7329CD4                            |
00007FF7F5CA4A76 | B8 33 43 90 34               | mov eax,34904333                            |
00007FF7F5CA4A7B | 81 E9 FF B4 49 27            | sub ecx,2749B4FF                            |
00007FF7F5CA4A81 | 81 F1 C0 80 42 7C            | xor ecx,7C4280C0                            |
00007FF7F5CA4A87 | 44 8B C1                     | mov r8d,ecx                                 |
00007FF7F5CA4A8A | F7 E1                        | mul ecx                                     |
00007FF7F5CA4A8C | 44 2B C2                     | sub r8d,edx                                 |
00007FF7F5CA4A8F | 41 D1 E8                     | shr r8d,1                                 |
00007FF7F5CA4A92 | 44 03 C2                     | add r8d,edx                                 |
00007FF7F5CA4A95 | 41 C1 E8 0D                  | shr r8d,D                                 |
00007FF7F5CA4A99 | 41 69 C0 19 35 00 00         | imul eax,r8d,3519                           |
00007FF7F5CA4AA0 | 3B C8                        | cmp ecx,eax                                 |
00007FF7F5CA4AA2 | B8 00 00 00 00               | mov eax,0                                 |
00007FF7F5CA4AA7 | 41 0F 44 C0                  | cmove eax,r8d                               |// ** 这里将 eax 清零即可绕开网络验证分支
00007FF7F5CA4AAB | C3                           | ret                                       |


F5:

__int64 __fastcall sub_140AF4A70(int a1)// 注册表键值: 1690216811 = 0x64BEA96B
{
unsigned int v1; // ecx
__int64 result; // rax

v1 = ((a1 ^ 0xC7329CD4) - 659141887) ^ 0x7C4280C0;
result = 0i64;
if ( v1 == 13593 * (v1 / 0x3519) )
    result = v1 / 0x3519;
return result;
}



运算的结果保存到偏移中,若为0则不走网络验证。这个值是什么时候被修改的呢?随机网络验证还是过期的时候走了网络验证就不得而知了 。。。
将以下信息导入注入表(或自行修改)即可完成验证:


Windows Registry Editor Version 5.00




"ThreadingModel"="1690216811"


===============================================

补充2(方法四):

我们跟踪一下网络验证过程:


00007FF6EF371CEF | FF 15 D3 75 9C 01         | call qword ptr ds:[<&QUrl::QUrl>]          | // 这里开始发送访问验证URL
...
00007FF6EF371DE5 | E8 BC 46 73 FF            | call 010editor.7FF6EEAA64A6                | // 这里获取本地时间 GetLocalTime 判定是否过期?
...
00007FF6EF371F04 | FF 15 D6 73 9C 01          | call qword ptr ds:[<&QIODevice::read>]   |// 这里获取网络验证返回值 参数在 rdx

1: rcx 0000021DBAEDD3E0
2: rdx 0000021DBAED7FD0         // buffer
3: r8 0000000000000013         // length
4: r9 000000000000001E


0000021DBAEE09003C 73 73 3E 69 6E 76 61 6C 69 64 3C 2F 73 73 3E<ss>invalid</ss>
0000021DBAEE091020 0A 0A

我们将其修改为其他值,即可绕过网络验证:

0000021DBAEE09003C 73 73 3E 73 75 63 63 65 73 73 3C 2F 73 73 3E<ss>success</ss>
0000021DBAEE091020 0A 0A

qint64 QIODevice::read(char *data, qint64 maxSize)
官方文档:http://doc.qt.io/qt-5/qiodevice.html#read

所以我们也可以直接 HOOKqt5core.dll 中的 QIODevice::read 函数,当 maxSize == 0x13 时 && 返回字符串为 “<ss>invalid</ss> ”或“<ss>error</ss>   ”
则修改返回值为“<ss>success</ss>”,即可通过网络验证!

spy007 发表于 2018-1-9 11:14:47

校长V5,分分钟搞定

theend 发表于 2018-1-9 12:26:03

本帖最后由 theend 于 2018-1-9 12:27 编辑

感谢校长分享,学习了!
自己测试了一下,触发验证并不是JE都不跳,默认跳过的次数多

xunan007 发表于 2018-1-9 13:23:19

谢谢分享,学习了。

zxxiaopi 发表于 2018-1-9 13:58:33

大神就是大神!V5

风轻云淡 发表于 2018-1-9 18:15:23

多年未见这么详细的文章了!赞

chenchengasdf 发表于 2018-1-9 18:55:33

666,大佬威武,受教育了

qinccckencn 发表于 2018-1-9 20:29:57

校长亲自操刀果然是精华学习了

不破不立 发表于 2018-1-9 21:02:07

收藏,学习,校长今天兴致高,一气呵成,相当于三个学习教程{:tongue:}

wqccj 发表于 2018-1-9 21:09:04

谢谢楼主,分享.
页: [1] 2 3
查看完整版本: 010 Editor v8.x 启动时网络验证绕过方案(多.维度击破)