封包的加密与解密、线程发包、收包
三大发包函数
send
WSASend
sendto
模块前缀为ws2_32
WSPSend (在同一个电脑上位置是固定的)
DXF单机版 私服
找到WSPSend
附加上口袋西游(或者任何的send发包的软件都可以),跳到send位置
send 第三个call下断(win7是第三个,win10是第四个),断下后F7进入,这就是WSPSend的位置。
附加到目标进程,跳转到这个地址,下断。
有时WSASend胡乱断,不是真正的发包 ,根据它的特征码,去搜索。查找–>所有命令 ,输入特征码,然后右键–>在每个命令上下设置断点。然后在所有被断下的位置做标记(注释),并取消断点。然后去执行喊话等发包操作,看在哪断下。如果不断,说明在刚才被标记的里面。之后逐一下断测试。
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
graph
z([找出三大发包函数])-->
可直接找出
z-->
不可直接找出 -->
找出WSPSend-->
附加其它游戏-->
跳到send处-->
z1["在第三/四个call下断
(win7 第三个,win10 第四个)"]-->
断下后F7进入-->
此处就是WSPSend的位置-->
z2["记录下来,附加到目标进程,
跳转到这个地址,下断"]-->
正常断-->
找到发包函数
z2-->胡乱断-->
记录它的特征码-->
查找所有命令-->
输入特征码-->
右键在每个命令上下设置断点-->
在所有被断下的位置做注释-->
取消断点-->
执行喊话等发包操作-->
看在哪断下-->
如果不断-->
说明在刚才注释的里面-->
逐一下断测试
找出WSPSend -->
在三大发包函数下-->
找到ws2_32.WEP-->
再下一句就是WSPSend的外部-->
下断后反复操作游戏-->
断下后F7进入
线程发包
线程发包的特点:
断得非常频繁
任何功能堆栈返回都是一样的
1
2
3
4
5
6
7
graph
y0(["判断是不是线程发包"])-->
观察断得是否频繁-->
y1["在发包函数头部下断,
喊话断一次,
按K打开堆栈,
复制整个表"]-->
y2["走路断一次,再复制整个表"]-->
y3{"对比两次的表内容是否相同"}-->|相同|线程发包
y3-->|不同|普通发包
正常流程
找到发包函数
判断是不是线程发包
判断包内容(非包长)的地址是否变化
跳出线程发包的步骤:
1
2
3
4
5
6
7
8
9
10
11
graph
z([跳出线程发包])-->z2{"判断包内容
(非包长)的地址
是否变化"}
z2 --是--> z4[唯一突破口,找到包的内容来源]--> z5["向上追,直到追到不变的地址"]---> z7["在不变的地址上,
下硬件写入断点,
返回,看是否跳出线程外"]
z2 --否--> z6[在包内容处下硬件写入dword断点] --> 就能跳出线程发包 --> z8
z7 -.->|否| z5
z7 --是--> z8([yeah])
z7-.->z9["从线程发包处,向外层层标注"]
z8-.->z10(["此时可以来验证功能函数"])-.->
z11["下断并喊话,反复ctrl+f9多次,并标记"]-->
z12(["逐一测试标记call并验证"])
条件断
实例
1
2
3
4
5
6
7
8
9
[[esp+8]]!=11&&[[esp+8]]!=4
# esp 是堆顶
# == 等于
# != 不等于
# && 和
word ptr[[[ebp+8]+4]] != 11
#word ptr 代表一个字节
找出加密封包
找出加密封包的步骤:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
graph
z["跳出线程发包后"]
z1["配好表达式"]
z2["验证是不是加密封包"]
z3["在此处(跳出线程封包的位置)下断"]
z3a["喊话或者其它动作,令其断下"]
z4["dd 表达式"]
z5["在send处下断"]
z5a["喊话或者其它动作,令其断下"]
z6["找到封包内容"]
z7["此处应该就是封包内容"]
z8{"对比"}
z9["确定是加密封包"]
z10["失败,寻找错误或者换其他方法"]
z-->z1-->z2
z2-->z5-->z5a-->z6-->z8
z2-->z3-->z3a-->z4-->z7-->z8
z8--相同-->z9
z8--不相同-->z10
明文包
1
2
graph LR
功能call --> 明文封包 --> 加密CALL --> 加密封包
到加密封包的外层找明文包。
找出明文封包的步骤:
1
2
graph LR
加密封包 --> Ctrl+F9到外层找明文包
找出加密CALL
找出加密CALL的步骤:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
graph
z1["找出已加密的最外层"]-->
z2["找出明文封包"]-->
z3["进入明文包"]-->
z4["先走一圈,并标注跳转"]
z5["回到明文封包外部"]-->
z6["下断"]-->
z7["喊话"]-->
z8["dd 明文封包内容"]-->
z9["F7进入明文封包"]-->
z10["F8逐步走,并观察内存窗口中明文封包何时变化"]-->
z11["如果经过某个call之后,内存窗口内容变化"]-->
z12["找到加密CALL"]-->
z13(["覆盖式加密"])
z10-->没有变化-->
x1(["复制式加密"])-->|没遇到过|需要从明文包开始逐步分析-->
看到底哪里利用过包地址或者包内容
分析加密CALL
需要分析的元素:
CALL的参数
包长
包地址
加密地址
加密长度
密钥(找到就行)
CALL内的寄存器
调用加密CALL加密
实例
1
2
3
4
5
6
7
8
9
10
11
12
push 12345678
push 12345678
push 11
mov ecx,[00f84ba4]
mov ecx,[ecx]
mov ecx.[ecx+4]
mov ecx,[ecx+14]
mov ecx, [ecx]
lea ecx,[ecx+54]
push ecx
call 00B94700
add esp, 10
发送喊话函数封包
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
void HXSYDialog::OnBnClickedEutton15()
{
byte a[100] = {0x11,0x00,0x7E,0x00,0x00,0x00,0x00,0x02,0x00,0x31,0x31, OFF, OxFF,OxFF,OxFF,0x00,0x00,0x00,0x00,0x60,0xA8,0x6C}:
DWORD 包长 = 0x13;
DWORD 包地址=(DWORD)a;
DWORD 加密地址 = 包地址+2;
DWORD 加密长度 = 包长-2;
__asm
{
push 加密地址
push 加密地址
push 加密长度
mov ecx, 0x00f84ba4
mov ecx, [ecx]
mov ecx, [ecx]
mov ecx, [ecx+0x4]
mov ecx, [ecx+0x14]
mov ecx, [ecx]
lea ecx, [ecx+0x54]
push ecx
mov eax, 0x00B94700
call eax
add esp, 0x10
}
HWND 窗口句柄=FindWindowA("Lapis Network C1ass",0);
DWORD A=GetWindowLongW(窗口句柄,-21);
DWORD S=*(DWORD*)(A+0x38);
send(S,(const char*)包地址,包长,0);
//TODO:在此添加控件通知处理程序代码
}
C++写 加密CALL
1
2
3
4
5
6
7
8
__declspec(naked) void 加密Ca11(DWORD秘钥,DWORD 加密长度,DWOED 加密地址,DWORD 加密地址2)
{
__asm
{
...
}
}
//__declspec(naked) 裸体函数
结合加密call,写加密封包,完全不走游戏代码
不走游戏代码,写吃药封包
收包recv
recv也存在重写问题
1
2
3
4
5
6
7
8
9
10
graph LR
subgraph 收包正常流程
z1["recv"]
z2["明文收包"]
z3["加密"]
z4["解密"]
z5["反复MemoryCopy"]
z6["写入内存"]
end
z1-->z3-->z4-->z2-->z5-->z6
1
2
3
4
5
6
7
8
9
graph LR
z1["recv下断"]
z3["正常操作"]
z4["寻找明文收包"]
z5["recv被重写"]
z6["双开喊话"]
z1-->|可断|z3
z1-->|不可断|z5-->z4-->z6-->A喊-->B搜索-->反复验证并最终确定-->向上层返回-->直到找到明文收包
控件包
1
2
3
4
5
6
7
graph
在明文包处-->
|下条件断 过滤心跳包|通过会触发向服务器发包的功能按键-->
z1["触发执行断点"]-->
堆栈 & K键堆栈 & z2["外层(推荐)"]
z2-->
逐层验证并确定