目录

封包的加密与解密、线程发包、收包

三大发包函数

  • send
  • WSASend
  • sendto

模块前缀为ws2_32

WSPSend (在同一个电脑上位置是固定的)

DXF单机版 私服

找到WSPSend

  1. 附加上口袋西游(或者任何的send发包的软件都可以),跳到send位置
  2. send 第三个call下断(win7是第三个,win10是第四个),断下后F7进入,这就是WSPSend的位置。
  3. 附加到目标进程,跳转到这个地址,下断。
  4. 有时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下断<br>(win7 第三个,win10 第四个)"]-->
断下后F7进入-->
此处就是WSPSend的位置-->
z2["记录下来,附加到目标进程,<br>跳转到这个地址,下断"]-->
正常断-->
找到发包函数
z2-->胡乱断-->
记录它的特征码-->
查找所有命令-->
输入特征码-->
右键在每个命令上下设置断点-->
在所有被断下的位置做注释-->
取消断点-->
执行喊话等发包操作-->
看在哪断下-->
如果不断-->
说明在刚才注释的里面-->
逐一下断测试
找出WSPSend -->
在三大发包函数下-->
找到ws2_32.WEP-->
再下一句就是WSPSend的外部-->
下断后反复操作游戏-->
断下后F7进入

线程发包

线程发包的特点

  1. 断得非常频繁
  2. 任何功能堆栈返回都是一样的
1
2
3
4
5
6
7
graph
	y0(["判断是不是线程发包"])-->
  观察断得是否频繁-->
  y1["在发包函数头部下断,<br>喊话断一次,<br>按K打开堆栈,<br>复制整个表"]-->
  y2["走路断一次,再复制整个表"]-->
  y3{"对比两次的表内容是否相同"}-->|相同|线程发包
  y3-->|不同|普通发包

正常流程

  1. 找到发包函数
  2. 判断是不是线程发包
  3. 判断包内容(非包长)的地址是否变化

跳出线程发包的步骤:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  graph
  z([跳出线程发包])-->z2{"判断包内容<br>(非包长)的地址<br>是否变化"}
  
  z2 --是--> z4[唯一突破口,找到包的内容来源]--> z5["向上追,直到追到不变的地址"]---> z7["在不变的地址上,<br>下硬件写入断点,<br>返回,看是否跳出线程外"]
  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

https://cdn.jsdelivr.net/gh/xinqinew/pic@main/img/image-20210925152332017.png

发送喊话函数封包

 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:在此添加控件通知处理程序代码
}

https://cdn.jsdelivr.net/gh/xinqinew/pic@main/img/image-20210925152855694.png

https://cdn.jsdelivr.net/gh/xinqinew/pic@main/img/image-20210925152756644.png

C++写 加密CALL

1
2
3
4
5
6
7
8
__declspec(naked) void 加密Ca11(DWORD秘钥,DWORD 加密长度,DWOED 加密地址,DWORD 加密地址2)
{
  __asm
  {
    ...
  }
}
//__declspec(naked) 裸体函数

结合加密call,写加密封包,完全不走游戏代码

https://cdn.jsdelivr.net/gh/xinqinew/pic@main/img/image-20210925181247651.png

https://cdn.jsdelivr.net/gh/xinqinew/pic@main/img/image-20210925181330747.png

不走游戏代码,写吃药封包

https://cdn.jsdelivr.net/gh/xinqinew/pic@main/img/image-20210925183139002.png

收包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-->
逐层验证并确定