游戏安全UE4引擎之天堂W跳线程,HOOK明文收包发包并分析加密解密,实现send发包

开始我们的UE4的跳线程之旅

简单了解游戏数据

拿到一个游戏,想去分析,在什么都不了解的情况下,我们只能先盲目的找2个简单的数据熟悉一下,之后再根据需求系统的分析,这是必然的一个顺序.

那么,先来找下人物血量,再简单不过的属性了

反复改变血量,CE扫描,最终找到4个结果

4个数据直接修改都无效果的情况,追哪一个呢?

如果单纯只是为了可以读取血量,那么其实任意一个都是可以的,如果为了方便肯定是哪个数据周围有更多的其他属性就追哪一个,从这个角度 是第二个最好的,当然 后期有了更多数据以后,根据相关性可能会有不同的看法

而对于此时的我们,必然是周围属性越多的越好,那么我们追第二个.

内存中数据是这样的

$ ==> 000000000000020D =====血量

$+8 00000004FFFFFFFF

$+10 0000001000000005

$+18 00000000000000A0====最大蓝量

$+20 00000005FFFFFFFF

$+28 000001AD00000006

$+30 0000000000000080====当前蓝量

$+38 00000006FFFFFFFF

$+40 0000000800000008

$+48 0000000000000001

$+50 00000008FFFFFFFF

$+58 000001AD00000007

我们对血量 下访问断,有怪物的情况可以让怪物打击我们改变血量从而产生访问代码

无怪物的时候,我们做各种操作,发现点任务传送的情况会断下

(都是可以的)

| 42:8B4401 10 | mov eax,dword ptr ds:[rcx+r8+0x10] |

| 83F8 FF | cmp eax,0xFFFFFFFF |

| 75 E1 | jne lineager.7FF70B9F8300 |

| 33C0 | xor eax,eax |

| C3 | ret |

| 33D2 | xor edx,edx |

| 49:8BC0 | mov rax,r8 |

| 48:03C1 | add rax,rcx |

| 48:8D40 08 | lea rax,qword ptr ds:[rax+0x8] |

| 48:0F44C2 | cmove rax,rdx |

| 48:85C0 | test rax,rax |

| 74 E8 | je lineager.7FF70B9F831F |

| 48:8B00 | mov rax,qword ptr ds:[rax] | 访问血量的代码

| C3 | ret |

我们直接在该代码上下F2断

发现访问的一直是 血量地址-60的地址

那我们直接下断追就可以了,追出来的表达式最后直接+60(省着每次要在内存上下断了)

往上追来源 追到这里,过程在备注,表达式 [rcx+158]+8+60|

| 8B81 60010000 | mov eax,dword ptr ds:[rcx+0x160] |

| 3B81 8C010000 | cmp eax,dword ptr ds:[rcx+0x18C] |

| 74 51 | je lineager.7FF70B9F831F |

| 4C:6389 A0010000 | movsxd r9,dword ptr ds:[rcx+0x1A0] |

| 4C:8D91 90010000 | lea r10,qword ptr ds:[rcx+0x190] |

| 4D:8B42 08 | mov r8,qword ptr ds:[r10+0x8] |

| 49:FFC9 | dec r9 |

| 48:63C2 | movsxd rax,edx |

| 4C:23C8 | and r9,rax |

| 4D:85C0 | test r8,r8 |

| 4D:0F45D0 | cmovne r10,r8 |

| 43:8B048A | mov eax,dword ptr ds:[r10+r94] |

| 83F8 FF | cmp eax,0xFFFFFFFF |

| 74 26 | je lineager.7FF70B9F831F |

| 4C:8B81 58010000 | mov r8,qword ptr ds:[rcx+0x158] |[rcx+158]+8+60|

| 48:63C8 | movsxd rcx,eax |

| 48:8D0449 | lea rax,qword ptr ds:[rcx+rcx2] |

| 48:8D0CC5 00000000 | lea rcx,qword ptr ds:[rax*8] |

| 42:391401 | cmp dword ptr ds:[rcx+r8],edx |

| 74 0D | je lineager.7FF70B9F8322 |

| 42:8B4401 10 | mov eax,dword ptr ds:[rcx+r8+0x10] |

| 83F8 FF | cmp eax,0xFFFFFFFF |

| 75 E1 | jne lineager.7FF70B9F8300 |

| 33C0 | xor eax,eax |

| C3 | ret |

| 33D2 | xor edx,edx |

| 49:8BC0 | mov rax,r8 | r8+8+60

| 48:03C1 | add rax,rcx | rcx==0

| 48:8D40 08 | lea rax,qword ptr ds:[rax+0x8] | rax+8+60

| 48:0F44C2 | cmove rax,rdx |

| 48:85C0 | test rax,rax |

| 74 E8 | je lineager.7FF70B9F831F |

| 48:8B00 | mov rax,qword ptr ds:[rax] | 访问血量的代码

| C3 | ret |

返回上一层继续追来源

| 48:895C24 08 | mov qword ptr ss:[rsp+0x8],rbx |

| 57 | push rdi |

| 48:83EC 20 | sub rsp,0x20 |

| 41:8BF8 | mov edi,r8d |

| 8BDA | mov ebx,edx |

| E8 1C8CFCFF | call lineager.7FF70ABFB8E0 |

| 48:8BC8 | mov rcx,rax |

| E8 A4B1C801 | call lineager.7FF70C8BDE70 | rax来源于这个call

| 33D2 | xor edx,edx |

| 48:8BC8 | mov rcx,rax | [rax+158]+8+60|

| E8 EA55DC00 | call lineager.7FF70B9F82C0 |

| 3BC3 | cmp eax,ebx |

| 7C 11 | jl lineager.7FF70AC32CEB |

| 3BC7 | cmp eax,edi |

| 7F 0D | jg lineager.7FF70AC32CEB |

| B0 01 | mov al,0x1 |

| 48:8B5C24 30 | mov rbx,qword ptr ss:[rsp+0x30] |

| 48:83C4 20 | add rsp,0x20 |

| 5F | pop rdi |

| C3 | ret |

| 48:8B5C24 30 | mov rbx,qword ptr ss:[rsp+0x30] |

| 32C0 | xor al,al |

| 48:83C4 20 | add rsp,0x20 |

| 5F | pop rdi |

| C3 | ret |

表达式

[rax+158]+8+60| rax 来源于上面的call

这里rax 可以直接调call 取得,实际就是人物对象,我们选择追,因为本身就是熟悉游戏数据嘛,多追追更容易了解

call 上下面,F7 进去走一遍 分析出表达式(注意这里面要F7进去走一遍,而不是直接回车进去分析,原因很简单,这种取对象的call,访问的地方很多,其他的访问不一定是取人物对象的,也有可能是取怪物的哦)

之后点- 号键分析出表达式 和过程如下:

| 44:8B41 04 | mov r8d,dword ptr ds:[rcx+0x4] |

| 45:85C0 | test r8d,r8d |

| 74 4E | je lineager.7FF70C8BDEC7 |

| 8B01 | mov eax,dword ptr ds:[rcx] |

| 85C0 | test eax,eax |

| 78 48 | js lineager.7FF70C8BDEC7 |

| 3B05 BF64CC04 | cmp eax,dword ptr ds:[0x7FF711584344] |

| 7D 40 | jge lineager.7FF70C8BDEC7 |

| 99 | cdq |

| 0FB7D2 | movzx edx,dx |

| 03C2 | add eax,edx |

| 8BC8 | mov ecx,eax |

| 0FB7C0 | movzx eax,ax |

| 2BC2 | sub eax,edx |

| C1F9 10 | sar ecx,0x10 |

| 48:98 | cdqe |

| 48:63C9 | movsxd rcx,ecx |

| 48:8D1440 | lea rdx,qword ptr ds:[rax+rax2] |

| 48:8B05 8964CC04 | mov rax,qword ptr ds:[0x7FF711584330] |[[[[0x00007FF711584330]+rcx*8]+rdx*8]+158]+8+60

| 48:8B0CC8 | mov rcx,qword ptr ds:[rax+rcx8] | [[[rax+rcx*8]+rdx*8]+158]+8+60

| 48:8D04D1 | lea rax,qword ptr ds:[rcx+rdx8] | [[rcx+rdx*8]+158]+8+60

| 48:85C0 | test rax,rax |

| 74 13 | je lineager.7FF70C8BDEC7 |

| 44:3940 10 | cmp dword ptr ds:[rax+0x10],r8d |

| 75 0D | jne lineager.7FF70C8BDEC7 |

| F740 08 00000030 | test dword ptr ds:[rax+0x8],0x30000000 |

| 75 04 | jne lineager.7FF70C8BDEC7 |

| 48:8B00 | mov rax,qword ptr ds:[rax] | [[rax]+158]+8+60

| C3 | ret |

| 33C0 | xor eax,eax |

| C3 | ret |

表达式

[[[[0x00007FF711584330]+rcx*8]+rdx*8]+158]+8+60

追的过程看上去非常简单,其实这也复合UE4引擎的设计,数据一般都不难

对象加密下标

我们刚才追出来的人物血量表达式

[[[[0x00007FF711584330]+rcx*8]+rdx*8]+158]+8+60

里面的rcx 和 rdx 实际不是固定的值, 可以认为他是加密下标

但是无论是什么,我们追其来源就可以了

而且这个里面[[0x00007FF711584330]+rcx*8]+rdx*8] 是2个大的数组,很有可能类似于世界数组,但是暂时我们不关心

我们追这个表达式里的rcx 和rdx 两个下标即可

很容易就追到了

| 44:8B41 04 | mov r8d,dword ptr ds:[rcx+0x4] |

| 45:85C0 | test r8d,r8d |

| 74 4E | je lineager.7FF70C8BDEC7 |

| 8B01 | mov eax,dword ptr ds:[rcx] | rcx下标==dword ptr:[rcx]/0x10000

| 85C0 | test eax,eax | rdx下标 == word ptr:[rcx]3| 78 48 | js lineager.7FF70C8BDEC7 |

| 3B05 BF64CC04 | cmp eax,dword ptr ds:[0x7FF711584344] |

| 7D 40 | jge lineager.7FF70C8BDEC7 |

| 99 | cdq |

| 0FB7D2 | movzx edx,dx |

| 03C2 | add eax,edx | edx==0

| 8BC8 | mov ecx,eax | rcx下标==eax/0x10000

| 0FB7C0 | movzx eax,ax | rdx下标 == ax3

| 2BC2 | sub eax,edx | edx==0

| C1F9 10 | sar ecx,0x10 | rcx下标== ecx右移动0x10位 相当于/0x10000

| 48:98 | cdqe |

| 48:63C9 | movsxd rcx,ecx | rcx下标 ==ecx 说明不超过DWORD

| 48:8D1440 | lea rdx,qword ptr ds:[rax+rax2] | rdx下标 == rax3

| 48:8B05 8964CC04 | mov rax,qword ptr ds:[0x7FF711584330] | [[[[0x00007FF711584330]+rcx8]+rdx8]+158]+8+60

| 48:8B0CC8 | mov rcx,qword ptr ds:[rax+rcx8] | [[[rax+rcx8]+rdx8]+158]+8+60

| 48:8D04D1 | lea rax,qword ptr ds:[rcx+rdx8] | [[rcx+rdx*8]+158]+8+60

| 48:85C0 | test rax,rax |

| 74 13 | je lineager.7FF70C8BDEC7 |

| 44:3940 10 | cmp dword ptr ds:[rax+0x10],r8d |

| 75 0D | jne lineager.7FF70C8BDEC7 |

| F740 08 00000030 | test dword ptr ds:[rax+0x8],0x30000000 |

| 75 04 | jne lineager.7FF70C8BDEC7 |

| 48:8B00 | mov rax,qword ptr ds:[rax] | [[rax]+158]+8+60

| C3 | ret |

| 33C0 | xor eax,eax |

| C3 | ret |

rcx下标==dword ptr:[rcx]/0x10000

rdx下标 == word ptr:[rcx]*3

我们继续返回追rcx,就是来源于基地址

| 48:83EC 28 | sub rsp,0x28 |

| 6548:8B0425 58000000 | mov rax,qword ptr gs:[0x58] |

| 8B0D AD0EB306 | mov ecx,dword ptr ds:[0x7FF71172C7A0] |

| BA 54040000 | mov edx,0x454 |

| 48:8B0CC8 | mov rcx,qword ptr ds:[rax+rcx*8] |

| 8B040A | mov eax,dword ptr ds:[rdx+rcx] |

| 3905 0B658506 | cmp dword ptr ds:[0x7FF711451E10],eax |

| 7F 0C | jg lineager.7FF70ABFB913 |

| 48:8D05 FA648506 | lea rax,qword ptr ds:[0x7FF711451E08] | 基地址

| 48:83C4 28 | add rsp,0x28 |

| C3 | ret |

| 48:8D0D F6648506 | lea rcx,qword ptr ds:[0x7FF711451E10] |

| E8 05F7EF03 | call lineager.7FF70EAFB024 |

| 833D EA648506 FF | cmp dword ptr ds:[0x7FF711451E10],0xFFFFFFFF |

| 75 DF | jne lineager.7FF70ABFB907 |

| 48:8D0D E1648506 | lea rcx,qword ptr ds:[0x7FF711451E10] |

| C705 CF648506 FFFFFF | mov dword ptr ds:[0x7FF711451E08],0xFFFFFFFF |

| C705 C9648506 000000 | mov dword ptr ds:[0x7FF711451E0C],0x0 |

| E8 7CF6EF03 | call lineager.7FF70EAFAFC4 |

| 48:8D05 B9648506 | lea rax,qword ptr ds:[0x7FF711451E08] |

| 48:83C4 28 | add rsp,0x28 |

| C3 | ret |

rcx下标==dword ptr:[0x7FF711451E08]/0x10000

rdx下标 == word ptr:[0x7FF711451E08]*3

最终表达式:

[[[0x00007FF711584330]+dword ptr:[0x7FF711451E08]/0x10000*8]+word ptr:[0x7FF711451E08]*3*8]

也可以写成

[[[[0x00007FF711584330]+([0x7FF711451E08]&0ffffffff)/0x10000*8]+([0x7FF711451E08]&0ffff)*3*8]+158]+8+60

基地址:

lineager.exe+7A64330

lineager.exe+7931E08

同时我们在附近分析到其他属性,相当于赠送

+38 经验值

+50 最大血量

+80 最大蓝量

+98 蓝量

+C8 向性值

朝向值

分析完毕人物的血量,我们再来看看这个游戏里的朝向

朝向有什么用? 释放非锁定性技能的时候,我们要面朝怪物才能准确性打击,相当于FPS游戏里的自瞄

我们发现这个游戏的朝向不是自由的, 只能8个朝向

和什么类似呢?对的,就是传奇,在我们讲过的5种类型朝向里面,这种是最简单的,几乎是不用计算的

这个游戏用这种简单朝向是出乎我们的意料

那么8朝向,最有可能的就是0-7喽,扫描下试试

通过CE扫描,很容易就扫到了一个地址

西北方向为0

顺时针,8个朝向分别是 0 - 7

但是这里发现一个问题,我们修改了,人物朝向不会变,那么说明这个朝向,只是一个表现形式

真实的内存值不是他,

我们有两种方法找到真实值(这里不代表0-7这个数据没有用哦,后面我们分析寻路封包的时候,发现是有用的)

①.从未知初始值重新扫描

②.对0-7的朝向地址下写入断,找其来源

都是可以的

用CE扫描,最终扫描到7个地址

变化规律都是

西北方向为0 顺时针 分别是

0 ,45,90,135,180,-135,-90,-45 8个值

看来是角度

这里面有一个很大的误区!!!!!!!!

包括我也是,看了很久才看出来原因

我们同时修改这7个朝向值,发现人物不转向

但是1个1个修改确转向了,为什么?

我的理解是这样的 ,其实7个地址有5个是打酱油的

只有2个地址是有用的,一个是本地效果地址,一个是真实可以转向的地址

当你去同时修改2个地址的时候,有代码判断值相同, 不执行转向操作,因为他认为目前朝向和要转向的朝向是一致的,所以不执行代码, 只有我们单独修改真实转向地址 他才会转

这里大家需要注意一下哦,当然我们追表达式一定要追真实地址

同时本地效果地址3C + 40 +44 可以明显的看到 自己的X Y Z坐标

| 0F57CA | xorps xmm1,xmm2 |

| 0F540D 77CDB603 | andps xmm1,xmmword ptr ds:[0x7FF7116A68D0] |

| 0FC2C1 01 | cmpps xmm0,xmm1,0x1 |

| 0F50C0 | movmskps eax,xmm0 |

| 85C0 | test eax,eax |

| 74 68 | je lineager.7FF70DB39BCC |

| F2:0F1007 | movsd xmm0,qword ptr ds:[rdi] |

| F2:0F1183 88020000 | movsd qword ptr ds:[rbx+0x288],xmm0 | rbx+288 写入真实朝向

| 8B47 08 | mov eax,dword ptr ds:[rdi+0x8] |

| 8983 90020000 | mov dword ptr ds:[rbx+0x290],eax |

| 48:8B83 30010000 | mov rax,qword ptr ds:[rbx+0x130] |

| 48:85C0 | test rax,rax |

| 74 47 | je lineager.7FF70DB39BCC |

| F680 4C010000 08 | test byte ptr ds:[rax+0x14C],0x8 |

可以不继续往上追,后面再说

分析发包

了解了一些基本数据以后,我们开始分析封包了,这是必然的

一个游戏最重要的就是收发包,有了明文收包相当于有了所有的数据,有了明文发包相当于有所有的功能

之后就是各种数据结构和补充了

首先我们到send下断,看下发包情况和参数

几乎是下断立刻断(我们有理由怀疑是线程发包,但是无法做定论)

1: rcx 0000000000006030====发包动作断下是这个socket,心跳可能不是这个socket

2: rdx 000001D812BD0088====做发包动作发现,发包地址固定的

3: r8 000000000000000D====如果断的太频繁,手速慢的同学,无法断到功能发包,也可以通过包长做条件断

4: r9 0000000000000000===flags为0

那么来验证一下是否是线程发包

方法非常简单

第一步,走路发包断下 CTRL+F9 看看返回的call 都是什么我们记录一下

返回第一层

00007FF712 | 48:8BF1 | mov rsi,rcx |

00007FF712 | 48:8B49 28 | mov rcx,qword ptr ds:[rcx+0x28] |

00007FF712 | 45:33C9 | xor r9d,r9d |

00007FF712 | FF15 4E9C1F02 | call qword ptr ds:[] | 1

00007FF712 | 8BF8 | mov edi,eax |

00007FF712 | 8903 | mov dword ptr ds:[rbx],eax |

返回第二层

00007FF711 | 49:8BD6 | mov rdx,r14 |

00007FF711 | FF50 58 | call qword ptr ds:[rax+0x58] | 2

00007FF711 | 84C0 | test al,al |

00007FF711 | 0F84 8D000000 | je lineager.7FF711E874EC |

返回第三层

00007FF711 | 0F1F4400 00 | nop dword ptr ds:[rax+rax],eax |

00007FF711 | 48:8B0F | mov rcx,qword ptr ds:[rdi] |

00007FF711 | E8 F8A21400 | call lineager.7FF711E871F0 | 3

00007FF711 | 48:83C7 10 | add rdi,0x10 |

00007FF711 | 48:3BFE | cmp rdi,rsi |

再返回直接卡死,必须要重启电脑,所以想尝试的同学可以尝试下,尝试一次就够了,记住这个地方,不要一直尝试....

第二步,我们用其他发包功能,例如穿装备来验证一下

发现过程更刚才一模一样

那么我们就确定这个游戏是 线程发包

确定了线程发包,我们就要找明文包了,找到HOOK明文包的位置

前面留了个伏笔,我们的发包地址是固定的

那么就简单了,本身我们跳出线程循环的思路就是追其来源,现在发包地址不断,我们直接对其下写入断即可

,但是我们下写入断要注意,不要在包头几个字节下,因为包头的处理很可能和包内容不在一个地方

所以我们在+8的位置下写入断

00007FFE77 | 0FB60A | movzx ecx,byte ptr ds:[rdx] |

00007FFE77 | 8808 | mov byte ptr ds:[rax],cl |

00007FFE77 | C3 | ret |

00007FFE77 | 8B0A | mov ecx,dword ptr ds:[rdx] |

00007FFE77 | 8908 | mov dword ptr ds:[rax],ecx | 断下的位置

00007FFE77 | C3 | ret |

00007FFE77 | 90 | nop |

00007FFE77 | 49:83F8 20 | cmp r8,0x20 | 20:' '

00007FFE77 | 77 17 | ja vcruntime140.7FFE77B5142D |

00007FFE77 | F3:0F6F0A | movdqu xmm1,xmmword ptr ds:[rdx] |

返回,发现是memmove 函数

对其进行分析,如下:

| 40:53 | push rbx |

| 48:83EC 20 | sub rsp,0x20 |

| 4C:8BC9 | mov r9,rcx |

| 8B49 10 | mov ecx,dword ptr ds:[rcx+0x10] |

| 83C1 08 | add ecx,0x8 |

| 48:63D9 | movsxd rbx,ecx |

| 48:8D0413 | lea rax,qword ptr ds:[rbx+rdx] |

| 4C:3BC0 | cmp r8,rax |

| 77 08 | ja lineager.7FF711D142D3 |

| 33C0 | xor eax,eax |

| 48:83C4 20 | add rsp,0x20 |

| 5B | pop rbx |

| C3 | ret |

| 890A | mov dword ptr ds:[rdx],ecx |

| 41:0FB709 | movzx ecx,word ptr ds:[r9] |

| 66:894A 04 | mov word ptr ds:[rdx+0x4],cx |

| 41:0FB749 02 | movzx ecx,word ptr ds:[r9+0x2] |

| 66:894A 06 | mov word ptr ds:[rdx+0x6],cx | 此处可以hOOK 明文包

| 48:8D4A 08 | lea rcx,qword ptr ds:[rdx+0x8] | rcx== send 的发包地址+8

| 49:8B51 08 | mov rdx,qword ptr ds:[r9+0x8] | rdx 拷贝来源

| 4D:6341 10 | movsxd r8,dword ptr ds:[r9+0x10] | r8 拷贝的长度

| E8 E6FA0503 | call | 返回的位置

| 48:8BC3 | mov rax,rbx |

| 48:83C4 20 | add rsp,0x20 |

| 5B | pop rbx |

| C3 | ret |

| 48:895C24 18 | mov qword ptr ss:[rsp+0x18],rbx |

| 55 | push rbp |

我们发现了几个问题

第一, 此处已经是明文包了,大家可能觉得我们对加密包下断怎么跑到明文包了? 先不管,下面再分析,因为我们还要找加密呢,直接到明文不代表什么好事.

第二,大家说这个位置下断,ctrl+f9 返回还是在线程内, 有关系吗? hook 明文包是线程内好还是线程外好呢?

当然是越接近加密越好了,能内层hook 肯定不到外层,一个是外层可能结构和最内层加密前不一样,一个是外层可能有多个分支,抓不全,至于跳出线程,那是下面的事,还是要做的

第三,此处就可以做为我们hook 抓明文包的位置了,需要定位保存

分析明文包,组明文走路包

有同学可能要问,为啥就判断是明文了,当然看到数据不是特别乱的比如走路抓到下面的内容

000001D80606020A,加密是不可能这面整齐的数值的

还是不能百分百确定是吧?

那么我们就分析一个封包 就要可以完全确认了

我们就拿走路封包吧

我们发现走一步的话,封包除了包头,长度只有3字节

下面走一步

05 01 0A

右面走一步

03 01 0A

上面走一步

01 01 0A

这样就能确定 A是固定的字节 01代表走路步数? 第三个字节代表走路的方向了

就是我们的0-7朝向

再继续走多步来判断,如下:

寻路封包

加密值 包头 包长

A990 0096 00000012

步数 固定值

02 02 02 02 01 02 08 0A

03 03

是不是就分析的更加清晰了,确认是明文包了呢?OK ,明文包确定没问题了

自己发包,不走游戏代码的全过程

1.hook抓到明文包

2.分析明文包组包,自己构造封包

3.对封包进行加密

4.send 发送封包

前2步已经搞定了

那么我们就开始找加密函数了,并且把加密流程代码偷出来

加密call

由于我们从send直接跳到了明文包

那么还要从明文包回头去找加密函数

明文包---->加密----send 是这样一个过程

所以我们对明文包下访问, 想加密肯定要访问啊,跟踪明文包去向就可以了

直接就断到了加密call里

| 48:8B41 10 | mov rax,qword ptr ds:[rcx+0x10] |

| 48:83C1 10 | add rcx,0x10 |

| 4D:63C8 | movsxd r9,r8d |

| 4C:8BC2 | mov r8,rdx |

| 48:FF60 38 | jmp qword ptr ds:[rax+0x38] |

| 4D:85C9 | test r9,r9 |

| 0F84 CE000000 | je lineager.7FF71494DAB7 |

| 48:896C24 10 | mov qword ptr ss:[rsp+0x10],rbp |

| 48:897424 18 | mov qword ptr ss:[rsp+0x18],rsi |

| 57 | push rdi |

| 4C:8B99 20010000 | mov r11,qword ptr ds:[rcx+0x120] |

| 49:8BF1 | mov rsi,r9 |

| 0FB681 28020000 | movzx eax,byte ptr ds:[rcx+0x228] |

| 49:8BF8 | mov rdi,r8 |

| 48:895C24 10 | mov qword ptr ss:[rsp+0x10],rbx |

| 4C:8BD2 | mov r10,rdx |

| 0FB699 29020000 | movzx ebx,byte ptr ds:[rcx+0x229] |

| 48:8BE9 | mov rbp,rcx |

| 4C:3BC2 | cmp r8,rdx |

| 75 3A | jne lineager.7FF71494DA59 |

| 90 | nop |

| 46:0FB60C18 | movzx r9d,byte ptr ds:[rax+r11] |

| 4D:8D52 01 | lea r10,qword ptr ds:[r10+0x1] |

| 42:8D0C0B | lea ecx,qword ptr ds:[rbx+r9] |

| 0FB6D9 | movzx ebx,cl |

| 42:0FB6141B | movzx edx,byte ptr ds:[rbx+r11] |

| 42:881418 | mov byte ptr ds:[rax+r11],dl |

| 49:03D1 | add rdx,r9 |

| 46:880C1B | mov byte ptr ds:[rbx+r11],r9b |

| FEC0 | inc al |

| 0FB6CA | movzx ecx,dl |

| 0FB6C0 | movzx eax,al |

| 42:0FB61419 | movzx edx,byte ptr ds:[rcx+r11] |

| 41:3052 FF | xor byte ptr ds:[r10-0x1],dl |

| 48:83EE 01 | sub rsi,0x1 | 断到这里

| 75 C9 | jne lineager.7FF71494DA20 |

| EB 42 | jmp lineager.7FF71494DA9B |

| 4D:2BD0 | sub r10,r8 |

| 0F1F40 00 | nop dword ptr ds:[rax],eax |

| 46:0FB60C18 | movzx r9d,byte ptr ds:[rax+r11] |

| 48:8D7F 01 | lea rdi,qword ptr ds:[rdi+0x1] |

| 42:8D0C0B | lea ecx,qword ptr ds:[rbx+r9] |

| 0FB6D9 | movzx ebx,cl |

| 42:0FB6141B | movzx edx,byte ptr ds:[rbx+r11] |

| 42:881418 | mov byte ptr ds:[rax+r11],dl |

| 49:03D1 | add rdx,r9 |

| 46:880C1B | mov byte ptr ds:[rbx+r11],r9b |

| FEC0 | inc al |

| 0FB6CA | movzx ecx,dl |

| 0FB6C0 | movzx eax,al |

| 42:0FB61419 | movzx edx,byte ptr ds:[rcx+r11] |

| 3257 FF | xor dl,byte ptr ds:[rdi-0x1] |

| 41:88543A FF | mov byte ptr ds:[r10+rdi-0x1],dl |

| 48:83EE 01 | sub rsi,0x1 |

| 75 C5 | jne lineager.7FF71494DA60 |

| 48:8B7424 20 | mov rsi,qword ptr ss:[rsp+0x20] |

| 889D 29020000 | mov byte ptr ss:[rbp+0x229],bl |

| 48:8B5C24 10 | mov rbx,qword ptr ss:[rsp+0x10] |

| 8885 28020000 | mov byte ptr ss:[rbp+0x228],al |

| 48:8B6C24 18 | mov rbp,qword ptr ss:[rsp+0x18] |

| 5F | pop rdi |

| C3 | ret |

这个加密call 非常简单,我们直接就能复制偷到自己的C++代码里

何乐而不为呢

返回看一下调用过程,分析如下:

| 80BE 88030500 00 | cmp byte ptr ds:[rsi+0x50388],0x0 |

| 74 6F | je lineager.7FF711E8ABFC |

| 33C0 | xor eax,eax |

| F044:0FB1BE AC000500 | lock cmpxchg dword ptr ds:[rsi+0x500AC],r15d |

| 74 5D | je lineager.7FF711E8ABF7 |

| 48:8D5E 58 | lea rbx,qword ptr ds:[rsi+0x58] |

| 48:895C24 58 | mov qword ptr ss:[rsp+0x58],rbx |

| 48:8BCB | mov rcx,rbx |

| FF15 84EF0E03 | call qword ptr ds:[ |

| 90 | nop |

| 48:8D96 88000000 | lea rdx,qword ptr ds:[rsi+0x88] |

| 48:0396 80000000 | add rdx,qword ptr ds:[rsi+0x80] |

| 4D:8BC6 | mov r8,r14 |

| 48:8B4C24 50 | mov rcx,qword ptr ss:[rsp+0x50] |

| E8 E896E8FF | call lineager.7FF711D142B0 |

| 48:8D8E 48010500 | lea rcx,qword ptr ds:[rsi+0x50148] |

| 44:8D45 FC | lea r8d,qword ptr ss:[rbp-0x4] | r8 == 加密长度

| 48:8D96 8C000000 | lea rdx,qword ptr ds:[rsi+0x8C] | rcx ==秘钥 需要追

| 48:0396 80000000 | add rdx,qword ptr ds:[rsi+0x80] | rdx == 封包地址+4 因为前4字节封包长度不加密

| E8 1AB6EAFF | call lineager.7FF711D36200 | 加密call

| 48:01AE 80000000 | add qword ptr ds:[rsi+0x80],rbp |

| 48:85DB | test rbx,rbx |

| 74 4B | je lineager.7FF711E8AC3D |

| 48:8BCB | mov rcx,rbx |

| EB 40 | jmp lineager.7FF711E8AC37 |

| 48:8B5C24 50 | mov rbx,qword ptr ss:[rsp+0x50] |

| 48:8D7E 58 | lea rdi,qword ptr ds:[rsi+0x58] |

| 48:897C24 60 | mov qword ptr ss:[rsp+0x60],rdi |

| 48:8BCF | mov rcx,rdi |

| FF15 22EF0E03 | call qword ptr ds:[ |

| 90 | nop |

| 48:8D96 88000000 | lea rdx,qword ptr ds:[rsi+0x88] |

| 48:0396 80000000 | add rdx,qword ptr ds:[rsi+0x80] |

| 4D:8BC6 | mov r8,r14 |

| 48:8BCB | mov rcx,rbx |

追秘钥 和 线程发包死循环

追来源

返回

| 90 | nop |

| 48:8BCE | mov rcx,rsi |

| E8 E8360000 | call lineager.7FF711E8AAD0 | rsi+50148

| 837E 40 01 | cmp dword ptr ds:[rsi+0x40],0x1 |

再返回,发现来到了一个熟悉的地方,就是我们send 外的第三层,再返回既死机

| 74 1A | je lineager.7FF711D3CF05 |

| 0F1F4400 00 | nop dword ptr ds:[rax+rax],eax |

| 48:8B0F | mov rcx,qword ptr ds:[rdi] | [rdi]+50148

| E8 F8A21400 | call lineager.7FF711E871F0 | 3

| 48:83C7 10 | add rdi,0x10 |

| 48:3BFE | cmp rdi,rsi |

这地方为什么会返回卡死?我们分析下发现 这里其实是线程发包的死循环, 一旦CTRL+F9 就卡死在死循环里出不来了,分析如下:

00007F | 48:895C24 20 | mov qword ptr ss:[rsp+0x20],rbx |

00007F | 55 | push rbp |

00007F | 56 | push rsi |

00007F | 57 | push rdi |

00007F | 41:54 | push r12 |

00007F | 41:55 | push r13 |

00007F | 41:56 | push r14 |

00007F | 41:57 | push r15 |

00007F | 48:83EC 30 | sub rsp,0x30 |

00007F | 0F297424 20 | movaps xmmword ptr ss:[rsp+0x20],xmm6 |

00007F | 4C:8BE9 | mov r13,rcx | 下断不断 真实来源真就是这 只不过是游戏刚开始的时候执行 死循环以后就不执行了

00007F | 33ED | xor ebp,ebp |

00007F | 33C0 | xor eax,eax |

00007F | F0:0FB169 10 | lock cmpxchg dword ptr ds:[rcx+0x10],ebp |

00007F | 0F85 95010000 | jne lineager.7FF673FFA140 |

00007F | F3:0F1035 7D6B6203 | movss xmm6,dword ptr ds:[0x7FF677620B30] |

00007F | 41:837D 48 00 | cmp dword ptr ds:[r13+0x48],0x0 | 循环头

00007F | 0F84 6C010000 | je lineager.7FF673FFA12A | 死循环

00007F | 49:8D5D 18 | lea rbx,qword ptr ds:[r13+0x18] |

00007F | 48:899C24 80000000 | mov qword ptr ss:[rsp+0x80],rbx |

00007F | 48:8BCB | mov rcx,rbx |

00007F | FF15 5D6B2203 | call qword ptr ds:[ |

00007F | 90 | nop |

00007F | 49:8B7D 40 | mov rdi,qword ptr ds:[r13+0x40] | [[[LineageR.exe+794D3A0]+40]]+50148

00007F | 49:6355 48 | movsxd rdx,dword ptr ds:[r13+0x48] |

00007F | 48:8BF2 | mov rsi,rdx |

00007F | 48:C1E6 04 | shl rsi,0x4 |

00007F | 48:03F7 | add rsi,rdi |

00007F | 48:3BFE | cmp rdi,rsi |

00007F | 74 1A | je lineager.7FF673FFA005 |

00007F | 0F1F4400 00 | nop dword ptr ds:[rax+rax],eax |

00007F | 48:8B0F | mov rcx,qword ptr ds:[rdi] | [rdi]+50148

00007F | E8 98821400 | call lineager.7FF674142290 | 3

00007F | 48:83C7 10 | add rdi,0x10 | 所以这个位置 CTRL+F9 是出不去的 直接会卡死

00007F | 48:3BFE | cmp rdi,rsi | 线程发包的死循环

00007F | 75 EF | jne lineager.7FF673FF9FF0 |

00007F | 41:8B55 48 | mov edx,dword ptr ds:[r13+0x48] |

00007F | 85D2 | test edx,edx |

00007F | 0F84 0F010000 | je lineager.7FF673FFA11C |

00007F | 8BFD | mov edi,ebp |

00007F | 896C24 78 | mov dword ptr ss:[rsp+0x78],ebp |

00007F | 44:8BFD | mov r15d,ebp |

00007F | 48:8BF5 | mov rsi,rbp |

00007F | 49:8B45 40 | mov rax,qword ptr ds:[r13+0x40] |

00007F | 48:8B08 | mov rcx,qword ptr ds:[rax] |

00007F | 8379 40 03 | cmp dword ptr ds:[rcx+0x40],0x3 |

00007F | 41:0F9EC2 | setle r10b |

00007F | 44:885424 70 | mov byte ptr ss:[rsp+0x70],r10b |

00007F | 4C:63E2 | movsxd r12,edx |

00007F | 4D:63CF | movsxd r9,r15d |

00007F | 41:FFC7 | inc r15d |

00007F | 48:FFC6 | inc rsi |

00007F | 49:3BF4 | cmp rsi,r12 |

00007F | 7D 32 | jge lineager.7FF673FFA070 |

00007F | 45:0FB6C2 | movzx r8d,r10b |

00007F | 48:8BD6 | mov rdx,rsi |

00007F | 48:C1E2 04 | shl rdx,0x4 |

00007F | 49:0355 40 | add rdx,qword ptr ds:[r13+0x40] |

00007F | 0F1F00 | nop dword ptr ds:[rax],eax |

00007F | 48:8B02 | mov rax,qword ptr ds:[rdx] |

00007F | 8BCD | mov ecx,ebp |

00007F | 8378 40 03 | cmp dword ptr ds:[rax+0x40],0x3 |

00007F | 0F9EC1 | setle cl |

00007F | 44:3BC1 | cmp r8d,ecx |

00007F | 75 0F | jne lineager.7FF673FFA070 |

00007F | 41:FFC7 | inc r15d |

00007F | 48:FFC6 | inc rsi |

00007F | 48:83C2 10 | add rdx,0x10 |

00007F | 49:3BF4 | cmp rsi,r12 |

00007F | 7C E0 | jl lineager.7FF673FFA050 |

00007F | 41:8BEF | mov ebp,r15d |

00007F | 41:2BE9 | sub ebp,r9d |

00007F | 45:84D2 | test r10b,r10b |

00007F | 74 35 | je lineager.7FF673FFA0B0 |

00007F | 41:3BF9 | cmp edi,r9d |

00007F | 74 28 | je lineager.7FF673FFA0A8 |

00007F | 49:8BD1 | mov rdx,r9 |

00007F | 48:C1E2 04 | shl rdx,0x4 |

00007F | 49:0355 40 | add rdx,qword ptr ds:[r13+0x40] |

00007F | 48:63CF | movsxd rcx,edi |

00007F | 48:C1E1 04 | shl rcx,0x4 |

00007F | 49:034D 40 | add rcx,qword ptr ds:[r13+0x40] |

00007F | 4C:63C5 | movsxd r8,ebp |

00007F | 49:C1E0 04 | shl r8,0x4 |

00007F | E8 D1500203 | call |

00007F | 44:0FB65424 70 | movzx r10d,byte ptr ss:[rsp+0x70] |

00007F | 03FD | add edi,ebp |

00007F | 897C24 78 | mov dword ptr ss:[rsp+0x78],edi |

00007F | EB 51 | jmp lineager.7FF673FFA101 |

00007F | 4D:8BF1 | mov r14,r9 |

00007F | 49:C1E6 04 | shl r14,0x4 |

00007F | 49:83C6 08 | add r14,0x8 |

00007F | 85ED | test ebp,ebp |

00007F | 74 42 | je lineager.7FF673FFA101 |

00007F | 4D:0375 40 | add r14,qword ptr ds:[r13+0x40] |

00007F | 49:8B3E | mov rdi,qword ptr ds:[r14] |

00007F | 48:85FF | test rdi,rdi |

00007F | 74 23 | je lineager.7FF673FFA0EE |

00007F | 836F 08 01 | sub dword ptr ds:[rdi+0x8],0x1 |

00007F | 75 1D | jne lineager.7FF673FFA0EE |

00007F | 48:8B07 | mov rax,qword ptr ds:[rdi] |

00007F | 48:8BCF | mov rcx,rdi |

00007F | FF10 | call qword ptr ds:[rax] |

00007F | 836F 0C 01 | sub dword ptr ds:[rdi+0xC],0x1 |

00007F | 75 0F | jne lineager.7FF673FFA0EE |

00007F | 48:8B07 | mov rax,qword ptr ds:[rdi] |

00007F | BA 01000000 | mov edx,0x1 |

00007F | 48:8BCF | mov rcx,rdi |

00007F | FF50 08 | call qword ptr ds:[rax+0x8] |

00007F | 90 | nop |

00007F | 49:83C6 10 | add r14,0x10 |

00007F | 83ED 01 | sub ebp,0x1 |

00007F | 75 CC | jne lineager.7FF673FFA0C3 |

00007F | 44:0FB65424 70 | movzx r10d,byte ptr ss:[rsp+0x70] |

00007F | 8B7C24 78 | mov edi,dword ptr ss:[rsp+0x78] |

00007F | 41:80F2 01 | xor r10b,0x1 |

00007F | 44:885424 70 | mov byte ptr ss:[rsp+0x70],r10b |

00007F | 49:3BF4 | cmp rsi,r12 |

00007F | BD 00000000 | mov ebp,0x0 |

00007F | 0F8C 18FFFFFF | jl lineager.7FF673FFA030 |

00007F | 41:897D 48 | mov dword ptr ds:[r13+0x48],edi |

00007F | 48:85DB | test rbx,rbx |

00007F | 74 09 | je lineager.7FF673FFA12A |

00007F | 48:8BCB | mov rcx,rbx |

00007F | FF15 FE692203 | call qword ptr ds:[ |

00007F | 0F28C6 | movaps xmm0,xmm6 |

00007F | E8 FE46C100 | call lineager.7FF674C0E830 |

00007F | 33C0 | xor eax,eax |

00007F | F041:0FB16D 10 | lock cmpxchg dword ptr ds:[r13+0x10],ebp |

00007F | 0F84 73FEFFFF | je lineager.7FF673FF9FB3 | 循环尾

00007F | 33C0 | xor eax,eax | 这句代码不断 做实了是死循环

00007F | 48:8B9C24 88000000 | mov rbx,qword ptr ss:[rsp+0x88] |

00007F | 0F287424 20 | movaps xmm6,xmmword ptr ss:[rsp+0x20] |

00007F | 48:83C4 30 | add rsp,0x30 |

00007F | 41:5F | pop r15 |

00007F | 41:5E | pop r14 |

00007F | 41:5D | pop r13 |

00007F | 41:5C | pop r12 |

00007F | 5F | pop rdi |

00007F | 5E | pop rsi |

00007F | 5D | pop rbp |

00007F | C3 | ret |

由于在死循环里,R13最终通过CE来扫描

最终表达式

[[[LineageR.exe+794D3A0]+40]]+50148

这样我们4步都有了,可以自己抓包,组包,发包了

明文收包

曾经更大家讲过,绝大多数的游戏,你有了明文包,就相当于有了加密call,因为有直接关系

而你有了加密call,就相当于有了解密call, 因为加密一般是可逆的

有了解密call,就有了明文收包,因为收包之后一定要解密,从而变成明文收包

好的,既然这样

我们XDBG 搜素加密call 的汇编代码

右键搜索---->命令---->call 0x00007FF711D36200(此时的加密call)

获得两个结果 一个是我们刚才找的加密call

另外一个就是解密call 喽

| 4C:8D86 88000500 | lea r8,qword ptr ds:[rsi+0x50088] |

| 48:8D96 88000000 | lea rdx,qword ptr ds:[rsi+0x88] |

| 48:0396 80000000 | add rdx,qword ptr ds:[rsi+0x80] |

| 41:FFD1 | call r9 |

| 4C:63F8 | movsxd r15,eax |

| 80BE 88030500 00 | cmp byte ptr ds:[rsi+0x50388],0x0 |

| 74 38 | je lineager.7FF711E875FC |

| 33C0 | xor eax,eax |

| F0:0FB19E AC000500 | lock cmpxchg dword ptr ds:[rsi+0x500AC],ebx |

| 74 24 | je lineager.7FF711E875F4 |

| 807E 1A 02 | cmp byte ptr ds:[rsi+0x1A],0x2 |

| 75 1E | jne lineager.7FF711E875F4 |

| 48:8D8E 48010500 | lea rcx,qword ptr ds:[rsi+0x50148] |

| 45:8D47 FC | lea r8d,qword ptr ds:[r15-0x4] |

| 48:8D96 8C000000 | lea rdx,qword ptr ds:[rsi+0x8C] |

| 48:0396 80000000 | add rdx,qword ptr ds:[rsi+0x80] |

| E8 0CECEAFF | call lineager.7FF711D36200 | 解密call

| 48:8B7D 58 | mov rdi,qword ptr ss:[rbp+0x58] |

| 4C:8B75 D8 | mov r14,qword ptr ss:[rbp-0x28] | rsi+8C

| 4C:01BE 80000000 | add qword ptr ds:[rsi+0x80],r15 | 下面的位置可以hook明文收包

| C646 18 01 | mov byte ptr ds:[rsi+0x18],0x1 |

| 48:85FF | test rdi,rdi |

| 74 0A | je lineager.7FF711E87616 |

| 48:8BCF | mov rcx,rdi |

| FF15 13250F03 | call qword ptr ds:[ |

| 90 | nop |

| 49:8BCE | mov rcx,r14 |

| FF15 51240F03 | call qword ptr ds:[0x7FF714F79A70] |

| 837E 40 03 | cmp dword ptr ds:[rsi+0x40],0x3 |

| 75 0F | jne lineager.7FF711E87634 |

| 48:8BCE | mov rcx,rsi |

| E8 332CFEFF | call lineager.7FF711E6A260 |

| C746 40 04000000 | mov dword ptr ds:[rsi+0x40],0x4 |

| 48:83C4 70 | add rsp,0x70 |

| 41:5F | pop r15 |

| 41:5E | pop r14 |

| 41:5C | pop r12 |

| 5F | pop rdi |

| 5E | pop rsi |

| 5B | pop rbx |

| 5D | pop rbp |

| C3 | ret |

跳出线程外,找线程外明文包

之前可能有同学疑问,我们没有跳出线程,原因是我们不需要

如果现在我们想要功能call,而不是发包,那么我们需要跳出线程了

也是非常简单的

先来到之前明文copy 的位置,继续追明文来源就可以了啊

| 41:0FB709 | movzx ecx,word ptr ds:[r9] |

| 66:894A 04 | mov word ptr ds:[rdx+0x4],cx |

| 41:0FB749 02 | movzx ecx,word ptr ds:[r9+0x2] |

| 66:894A 06 | mov word ptr ds:[rdx+0x6],cx | 此处可以hOOK 明文包

| 48:8D4A 08 | lea rcx,qword ptr ds:[rdx+0x8] | rcx== send 的发包地址+8

| 49:8B51 08 | mov rdx,qword ptr ds:[r9+0x8] | rdx 拷贝来源

| 4D:6341 10 | movsxd r8,dword ptr ds:[r9+0x10] | r8 拷贝的长度

| E8 E6FA0503 | call | 返回的位置

| 48:8BC3 | mov rax,rbx |

发现copy 来源地址是变化的,那么还是我们基础里面讲线程发包原理的思路,一直追到公式地址为固定的再下写入断对吗?

那么往上追

| 33C0 | xor eax,eax |

| F044:0FB1BE A4000500 | lock cmpxchg dword ptr ds:[rsi+0x500A4],r15d |

| 44:3BC8 | cmp r9d,eax |

| 0F84 31010000 | je lineager.7FF711E8AC65 |

| 4D:6306 | movsxd r8,dword ptr ds:[r14] |

| 49:8BD0 | mov rdx,r8 |

| 49:23D1 | and rdx,r9 |

| 49:8B4E 08 | mov rcx,qword ptr ds:[r14+0x8] |

| 48:8D04D1 | lea rax,qword ptr ds:[rcx+rdx*8] |

| 49:8BCF | mov rcx,r15 |

| 48:8D5424 50 | lea rdx,qword ptr ss:[rsp+0x50] |

| 48:3BD0 | cmp rdx,rax |

| 74 0E | je lineager.7FF711E8AB60 | rax 每次+8 , 我们只要对其下一个地址下断即可

| 48:8B08 | mov rcx,qword ptr ds:[rax] | [[rax]+8]

| 48:894C24 50 | mov qword ptr ss:[rsp+0x50],rcx |

| 4C:8938 | mov qword ptr ds:[rax],r15 |

| 45:8B06 | mov r8d,dword ptr ds:[r14] |

| 41:8D41 01 | lea eax,qword ptr ds:[r9+0x1] |

| 41:23C0 | and eax,r8d |

| 41:8746 18 | xchg dword ptr ds:[r14+0x18],eax |

| 48:85C9 | test rcx,rcx |

直接跳出了线程,来带线程外

| 48:23CA | and rcx,rdx |

| 48:8D14C8 | lea rdx,qword ptr ds:[rax+rcx*8] |

| 49:3BD0 | cmp rdx,r8 |

| 74 30 | je lineager.7FF711E7A472 |

| 49:8B00 | mov rax,qword ptr ds:[r8] |

| 48:897C24 30 | mov qword ptr ss:[rsp+0x30],rdi |

| 48:8B3A | mov rdi,qword ptr ds:[rdx] |

| 48:8902 | mov qword ptr ds:[rdx],rax | rax 写入明文包!!!!!!!!!!!!!!!!!

| 4D:8908 | mov qword ptr ds:[r8],r9 |

| 48:85FF | test rdi,rdi |

线程外明文包1

| 48:8BD8 | mov rbx,rax |

| 48:895C24 20 | mov qword ptr ss:[rsp+0x20],rbx |

| 48:8D5424 20 | lea rdx,qword ptr ss:[rsp+0x20] |

| 48:8D8F 88000500 | lea rcx,qword ptr ds:[rdi+0x50088] |

| E8 01BAFEFF | call lineager.7FF711E7A400 | 线程外明文包1

| 0FB6F0 | movzx esi,al |

| 48:8B5C24 20 | mov rbx,qword ptr ss:[rsp+0x20] |

| 48:85DB | test rbx,rbx |

| 74 15 | je lineager.7FF711E8EA21 |

线程外明文包2

| E8 2095FCFF | call lineager.7FF711D067B0 |

| 66:03F0 | add si,ax |

| 48:8B87 A0010000 | mov rax,qword ptr ds:[rdi+0x1A0] |

| 48:8B48 08 | mov rcx,qword ptr ds:[rax+0x8] |

| 48:85C9 | test rcx,rcx |

| 74 0F | je lineager.7FF711D3D2B2 |

| 4C:8BCD | mov r9,rbp |

| 44:0FB7C6 | movzx r8d,si |

| 0FB7D3 | movzx edx,bx |

| E8 AE161500 | call lineager.7FF711E8E960 | 线程外明文包2

| 48:8B5C24 50 | mov rbx,qword ptr ss:[rsp+0x50] |

| 48:8B6C24 58 | mov rbp,qword ptr ss:[rsp+0x58] |

| 48:8B7424 60 | mov rsi,qword ptr ss:[rsp+0x60] |

| 48:83C4 40 | add rsp,0x40 |

| 5F | pop rdi |

| C3 | ret |

最外层明文包

| 75 39 | jne lineager.7FF711C8069E |

| E8 96A343FF | call lineager.7FF7110BAA00 |

| 48:8BC8 | mov rcx,rax |

| E8 6E06EB00 | call lineager.7FF712B30CE0 |

| 48:85C0 | test rax,rax |

| 74 65 | je lineager.7FF711C806DC |

| E8 84A343FF | call lineager.7FF7110BAA00 |

| 48:8BC8 | mov rcx,rax |

| E8 5C06EB00 | call lineager.7FF712B30CE0 |

| 4C:8BC3 | mov r8,rbx | 包内容结构

| 8BD7 | mov edx,edi | 包头

| 48:8BC8 | mov rcx,rax | 对象

| E8 7FCA0B00 | call lineager.7FF711D3D110 | 最外层明文包

| B0 01 | mov al,0x1 |

| 48:8B5C24 30 | mov rbx,qword ptr ss:[rsp+0x30] |

| 48:83C4 20 | add rsp,0x20 |

| 5F | pop rdi |

| C3 | ret |

| 80FA 01 | cmp dl,0x1 |

| 75 39 | jne lineager.7FF711C806DC |

在这里下断 直接返回就是功能call了

找加密值

现在整个发包过程

我们只少一个包内容中的加密值了,不知道大家是不是已经忘了

从跳出线程的代码

到线程外最内层明文包

我们分析出来 rdx+2 就是要追的加密值

00007FF6E93 | 74 15 | je lineager.7FF6E93BE9E9 |

00007FF6E93 | 4D:8BCF | mov r9,r15 |

00007FF6E93 | 45:0FB7C4 | movzx r8d,r12w | r12 == 加密值

00007FF6E93 | 0FB7D5 | movzx edx,bp |

00007FF6E93 | 48:8BC8 | mov rcx,rax | rcx 结构体

00007FF6E93 | E8 8ADCE6FF | call lineager.7FF6E922C670 | 组包内容来源于这个call

00007FF6E93 | 48:8BD8 | mov rbx,rax | rax+2

00007FF6E93 | 48:895C24 20 | mov qword ptr ss:[rsp+0x20],rbx |

00007FF6E93 | 48:8D5424 20 | lea rdx,qword ptr ss:[rsp+0x20] | [rsp+20]+2 加密值

00007FF6E93 | 48:8D8F 88000500 | lea rcx,qword ptr ds:[rdi+0x50088] |

00007FF6E93 | E8 01BAFEFF | call lineager.7FF6E93AA400 | 线程外明文包1

00007FF6E93 | 0FB6F0 | movzx esi,al |

00007FF6E92 | 74 43 | je lineager.7FF6E926D293 |

00007FF6E92 | 0FB750 02 | movzx edx,word ptr ds:[rax+0x2] |

00007FF6E92 | 0FB730 | movzx esi,word ptr ds:[rax] |

00007FF6E92 | 48:894C24 20 | mov qword ptr ss:[rsp+0x20],rcx | rcx == [[人物对象+478]+30]

00007FF6E92 | 8BCA | mov ecx,edx |

00007FF6E92 | 2BCE | sub ecx,esi |

00007FF6E92 | 48:C74424 28 2000000 | mov qword ptr ss:[rsp+0x28],0x20 | +8 写入 20

00007FF6E92 | 81F9 FFFF0000 | cmp ecx,0xFFFF |

00007FF6E92 | C74424 30 FFFFFFFF | mov dword ptr ss:[rsp+0x30],0xFFFFFFFF | +10写入 FFFFFFFF

00007FF6E92 | 48:8D4C24 20 | lea rcx,qword ptr ss:[rsp+0x20] |

00007FF6E92 | 75 07 | jne lineager.7FF6E926D285 | 只有rcx一个参数 结构体

00007FF6E92 | E8 CD6D0000 | call lineager.7FF6E9274050 | 取加密值call

00007FF6E92 | EB 0B | jmp lineager.7FF6E926D290 | 加密值取WORD型

00007FF6E92 | 66:2BD6 | sub dx,si |

00007FF6E92 | 66:FFC2 | inc dx |

00007FF6E92 | E8 2095FCFF | call lineager.7FF6E92367B0 | 这也是加密 但是没执行!

00007FF6E92 | 66:03F0 | add si,ax | 加密值 ==ax

00007FF6E92 | 48:8B87 A0010000 | mov rax,qword ptr ds:[rdi+0x1A0] |

00007FF6E92 | 48:8B48 08 | mov rcx,qword ptr ds:[rax+0x8] |

00007FF6E92 | 48:85C9 | test rcx,rcx |

00007FF6E92 | 74 0F | je lineager.7FF6E926D2B2 |

00007FF6E92 | 4C:8BCD | mov r9,rbp |

00007FF6E92 | 44:0FB7C6 | movzx r8d,si | 加密值 ==si

00007FF6E92 | 0FB7D3 | movzx edx,bx |

00007FF6E92 | E8 AE161500 | call lineager.7FF6E93BE960 | 线程外明文包2

00007FF6E92 | 48:8B5C24 50 | mov rbx,qword ptr ss:[rsp+0x50] |

00007FF6E92 | 48:8B6C24 58 | mov rbp,qword ptr ss:[rsp+0x58] |

00007FF6E92 | 48:8B7424 60 | mov rsi,qword ptr ss:[rsp+0x60] |

00007FF6E92 | 48:83C4 40 | add rsp,0x40 |

00007FF6E92 | 5F | pop rdi |

00007FF6E92 | C3 | ret |

最终结果

mov rcx,结构

rcx+0 写入[[人物对象+478]+30]

rcx+8 写入20

rcx+10 写入ffffffff

call 0x00007FF6E9274050

返回的 ax 就是加密值

好了到这里我们收发包完美解决

在后面的课程里,我们会接着锻炼通过线程外的明文发包函数去分析各类功能函数,并通过C++代码实现函数指针写 CALL,调CALL,send发包

猜你喜欢

转载自blog.csdn.net/qq_43355637/article/details/126424572