封包协议,加密解密,线程发包,recv,明文收包(转载)

转自吾爱大佬

 

1.封包,协议
什么是封包?封包就是按照某种协议组成的一段字节集.这个很容易理解
那为什么要按照某种协议呢?这个我们一会再说
所有联网的软件,包括网络游戏,客户端和服务端进行通信都是通过封包进行的.
大家可以想象成2个人打电话,封包就是说话的内容.
而协议呢?就是封包的语种,  2个人打电话一个用日语,一个用英文
もしもし、韓さんです。 那边接电话 What are you talking?  这怎么交流啊?
好,那么我们定好协议,本通电话我们全部采用中文,这样就可以正常交流了对吧?
这就是协议
一句话概括, 联网软件,客户端和服务端通过约定好的协议用封包来进行通信.


2.收发包
上面了解了什么是封包协议,我们来看下什么是收发包
网络游戏和单机游戏的区别就在于时刻要和服务器进行通信,也就是收发包.我们发给服务器的叫做发包, 我们收到服务器发给我们的叫做收包,
当然这是相当于我们而言.反之一样.
来一个简单的例子:
我们进入游戏说一句:你好! 为什么别的玩家可以看到?
通过这个流程我们就会了解收发包以及服务器的作用.
首先我们发送要和周围人说你好的封包给服务器,
服务器收到我们的封包以后, 一般会给我们回一个封包,说OK没问题,当然也可能高冷的不回啊.
然后服务器会执行代码判断我周围都有哪些玩家,然后对这些玩家逐个发包
这些玩家收到服务器的封包以后,就会看到我们的喊话内容了.
了解了什么是收发包
我们就要知道调用什么函数来实现.
这里的三大快递公司
就是三个发包函数    send   sendto   WSASend 以及对应的  3个收包函数  recv recvfrom  WSARecv
用哪一组都是可以的.
例如 口袋西游是  send recv
幻想神域是   WSASend  WSARecv

3.心跳包
上面了解了封包协议,收发包,我们来看下什么是心跳包?
心跳包也是封包,只是一种比较特殊的封包.
他可以证明我们的客户端还活着,顾名思义,有心跳就是活着.
举个简单的例子:
2个人打电话, A: 喂在吗? B:... A:在吗? B....A:挂了.  多次没得到回应直接就挂断了.
心跳包也一样,当服务器给我们发包,我们没有回复的时候,服务器就有可能把我们强制踢下线. 当然也有可能我们定时主动发送
1-2次服务器可能会认为偶尔丢包,如果2个以上应该就会直接认为我们掉线,直接强制踢下线了.
心跳包有很多好处:
1.一定程度防止脱机外挂
2.掉线立刻下线, 比如以前的老游戏,电脑蓝屏关机了,由于没有自然下线, 心跳也特别少,人物可能还在上面很长一段时间,这是不安全的,比如传奇2
3.可以隐藏一些数据在心跳包中.


4.针对发包函数的保护方法
了解了封包的概念,我们发现发包函数非常重要,
监听了发包函数就相当于掌握了所有功能实现的方法,可以替代所有call.监听了收包函数就相当于掌握了所以数据信息.因为数据本质来源于服务器
同时通过调用关系我们还知道,可以通过发包函数返回到所有的功能函数.
这是连新手的都知道的2点,所以一定要进行防护

针对这2点我们可以做什么防护呢?
那么第一件事,当然是把这个函数给尽量的隐藏起来
有的时候我们会发现,三大发包函数都不断!
那么是不是这个游戏不用这3个函数发包呢?当然不是,我们只能用这三个发包函数发包,没有其他的方法。
那就是重新实现发包函数,自己重新写一个发包函数,不用系统提供的成品函数了。
说的简单点就是,代码还是这三个发包函数的代码,只是换了个位置,搬家了,不直接调用这3个函数了。
知道这个原理以后就很容易处理了

方法一
既然还是之前的代码,那么我们到他的更内层函数下断就可以了
既WSPSend,相当于用内层函数当特征码。
函数体内的虚函数

X64结构不一样,但是一样是虚函数进去 找到WSPSend

方法二
通过三个函数的特征码到对应模块中进行搜索,这里注意的是这个重新实现的发包函数,可能在原来的模块中ws2_32,也可能在游戏自己的某个模块中。
所以搜索的时候要注意.
特征码比如头部这一段

可能会有微小变化,所以我们要尝试
同时也可能搜索到多个位置,全部下断,测试即可.
总之两种方法思路是一致的,都是找原代码的特征而已。

对发包函数第二个保护方案就是加密
如果不加密,直接监听发包函数就可以获得所有的信息了.
比如直接hook send函数
例如WPE等简单工具就是hook的发包函数
所以必须进行加密
加密也一定要进行动态加密,就是同样的结果加密后内容是不同的.那么以上方法也就没有任何作用了.



5.对发包函数第三个保护方案就是线程发包
正常的游戏调用流程是
功能函数1---》功能函数2---》组包过程---》明文封包---》加密---》发包函数
所谓组包过程就是把功能函数的参数按照格式组成一个字节集
明文封包就是组装完成但是没有进行加密的封包
最后一步加密发送.

通过上面的调用关系,我们能看出来
这样只要找到发包函数,所以功能函数以及加密解密函数随意返回,随意分析
那怎么样避免呢?线程发包!

线程发包原理
就是说用2条线程来控制以上流程,让逆向者不能直接返回
第一条线程
功能函数1---》功能函数2---》组包---》明文封包---》封包写到某一个地址中
第二条线程
得到那个地址中的内容---》加密---》发包函数

当然两条线程分工不一定是严格这样写的,也可能明文封包在第二条线程中

这样的发包会有什么的特征呢?
1.无论什么功能,堆栈返回都是一样的,因为第二条线程的调用过程,什么功能都一样了
2.第二条线程由于不断循环,断的可能会比较频繁



6.线程发包跳出循环线程
先不管线程通信都有哪些方法,我们先来一个锻炼,然后我们再详细分析原理.

上面我们知道了线程发包的原理,那么这2条线程唯一的关联就是封包内容
所以我们直接追封包内容来源即可.就有线索从第二条线程返回到第一条线程了.
1.包内容地址如果是固定的,我们直接下断就能跳出去了,相当于用全局变量做线程之间通信
这里是变化的
那么我们追其来源

edi+2888 开始不发生变化了
根据我们的思路,要找变化的值来源, 那么我们在该地址上下写入断,

找到写入来源以后  我们看是否跳出了线程
发现并未跳出线程,还在原来的线程里.
2.那么我们还要继续追其来源

edx开始不变化了,对EDX下断

跳出了线程,我们可以通过这里下断,断到功能函数了
返回就是明文发包call
而这里就可以当成我们的"send"了

7.整理完全线程发包流程

1.跳出线程在这个位置,我们来整理下整体流程

线程1将ebp写入一个地址.
ebp 是会变的是动态申请的,存放的封包结构
这个地址是全局类对象指向封包结构的指针


线程2将线程1写入的  封包结构取出来
edi+2880就是全局类对象指向封包结构的指针
取出来给edx,我们依然写ebp  为了知道是线程1断出去的ebp
[[ebp]+8]   又写入  全局类型对象 +2888的位置

全局类对象+2888的位置 取出来给ebx
取出来的就是前面的   [[ebp]+8]  
[[ebp+8]+4]  就是是封包内容

8.验证发包内容

我们下断可以验证一下 里面的封包内容是否是最后发送的内容,这个必须验证,防止中间又有多次加密
如果不相同,我们还要在这里对封包下访问断,追踪他经历了什么
方法很简单:
我们同时在这个位置下断 和WSASend 下断
对比两个位置封包是否一样
分别下条件断
byte ptr[[[EBP+8]+4]] != 0x0F

喊话11对比

2B771F00  C2C70011
2B771F04  C0105E58
2B771F08  D805FBB8
2B771F0C  910B1805
2B771F10  6F043A08

$ ==>    >C2C70011
$+4      >C0105E58
$+8      >D805FBB8
$+C      >910B1805
$+10     >6F043A08
$+14     >404B0000
发现是一样的那么我们在这里继续分析即可

9.明文发包call 和锁定加密call

我们现在跳出的位置  其实就是相当于"send"
正常可以返回到各种功能call, 如果使用call的话,我们现在已经足够了
如果想自己加密自己send发送封包,那么我们还需要分析加密解密过程,分析加密算法.
返回一层层看看  是否有明文内容,喊话是最容易看到明文的位置
返回的第一个call里面就看到了明文


那么说明    从这个call的头部   到第二次跳出的位置  中间就有加密call
如果这层发现不了明文,我们继续返回
当然我们也可以从ebp 一句一句逆向来源也是可以的
这个地方同样需要条件断  byte ptr[[[esp]+4]]!=0f

那么这地方就是明文发包call
找到了明文发包call,我们可以直接调用或则调用更外层功能函数实现各种功能,这之前我们都已经讲过了,这节课我们想用更好的方法
就是找到加密函数,我们自己加密封包,然后通过发包函数发送


10.加密call
加密函数的位置我们已经锁定了,就是在返回的call到我们断的位置之间
那么可以进行分析了
从call 断下以后 F7单独执行即可

发现了加密calll
以及分析出来加密call的参数 

00B92266    2BF8            sub     edi, eax
00B92268    83C5 02         add     ebp, 2
00B9226B    55              push    ebp                              ; 加密的地址
00B9226C    83C3 02         add     ebx, 2
00B9226F    53              push    ebx                              ; 加密的地址
00B92270    83C7 FE         add     edi, -2
00B92273    8D46 54         lea     eax, dword ptr [esi+54]
00B92276    57              push    edi                              ; 加密的长度
00B92277    50              push    eax                              ; 秘钥 往上追  [[[[[00f84ba4]]+4]+0xC+8]]+54
00B92278    E8 83240000     call    00B94700                         ; 加密call
00B9227D    8B9E 80280000   mov     ebx, dword ptr [esi+2880]

全过程:
功能call参数进行组包,调用明文发包call, 明文发包call内部进行加密, 写入到一个全局类对象的属性中
线程2访问该属性,进行发包

11.偷功能
偷功能很容易就是把程序的汇编代码复制到我们的程序内进行使用
我们自己组包,自己加密,自己发送封包
好处是,彻底不走游戏的任何代码.

复制出来的汇编代码需要进行初步处理才能写到内联汇编中
1.常数全部要加上0x
2.如果有call 还要自己分析出来继续执行内容  否则还是要以来原有程序
3.mov  eax,[0x12345678] 类似这种容易错误的汇编代码也要转一下
4.跳转都标注出来
例如 jnz  12345678  他是要跳转到目的代码地址执行的,我们偷出来的代码已经不是原来的地址了
所以要通过标签修改跳转的地址。


我们把加密call的代码全部偷出来

push    ebp
push    ebx
push    esi
push    edi
mov     edi, dword ptr [esp+0x14]
mov     edx, dword ptr [esp+0x18]
mov     esi, dword ptr [esp+0x1C]
mov     ebp, dword ptr [esp+0x20]
xor     eax, eax
xor     ebx, ebx
cmp     edx, 0
je      Label1 =========================================
mov     al, byte ptr [edi]
mov     bl, byte ptr [edi+4]
add     edi, 8
lea     ecx, dword ptr [esi+edx]
sub     ebp, esi
mov     dword ptr [esp+0x18], ecx
inc     al
cmp     dword ptr [edi+0x100], -1
je      Label2================================================
mov     ecx, dword ptr [edi+eax*4]
and     edx, 0xFFFFFFFC
 je      Label3===============================================
lea     edx, dword ptr [esi+edx-4]
mov     dword ptr [esp+0x1C], edx
mov     dword ptr [esp+0x20], ebp
 
 
Label4:
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
inc     al
and     edx, 0x0FF
mov     ecx, dword ptr [edi+eax*4]
mov     ebp, dword ptr [edi+edx*4]
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
inc     al
and     edx, 0x0FF
ror     ebp, 8
mov     ecx, dword ptr [edi+eax*4]
or      ebp, dword ptr [edi+edx*4]
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
inc     al
and     edx, 0x0FF
ror     ebp, 8
mov     ecx, dword ptr [edi+eax*4]
or      ebp, dword ptr [edi+edx*4]
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
inc     al
and     edx, 0x0FF
ror     ebp, 8
mov     ecx, dword ptr [esp+0x20]
or      ebp, dword ptr [edi+edx*4]
ror     ebp, 8
xor     ebp, dword ptr [esi]
cmp     esi, dword ptr [esp+0x1C]
mov     dword ptr [ecx+esi], ebp
lea     esi, dword ptr [esi+4]
mov     ecx, dword ptr [edi+eax*4]
jb      Label4============================================
cmp     esi, dword ptr [esp+0x18]
je      Label5===================================
mov     ebp, dword ptr [esp+0x20]
 
 
Label3:
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
 inc     al
and     edx, 0x0FF
mov     edx, dword ptr [edi+edx*4]
xor     dl, byte ptr [esi]
lea     esi, dword ptr [esi+1]
mov     ecx, dword ptr [edi+eax*4]
cmp     esi, dword ptr [esp+0x18]
mov     byte ptr [ebp+esi-1], dl
jb      Label3==========================================
jmp     Label5========================================
 
 
Label2:
 
movzx   ecx, byte ptr [edi+eax]
 
Label6:
add     bl, cl
movzx   edx, byte ptr [edi+ebx]
mov     byte ptr [edi+ebx], cl
mov     byte ptr [edi+eax], dl
add     dl, cl
movzx   edx, byte ptr [edi+edx]
add     al, 1
xor     dl, byte ptr [esi]
 lea     esi, dword ptr [esi+1]
movzx   ecx, byte ptr [edi+eax]
cmp     esi, dword ptr [esp+0x18]
mov     byte ptr [ebp+esi-1], dl
jb      Label6=============================================
Label5:
dec     al
mov     byte ptr [edi-4], bl
mov     byte ptr [edi-8], al
 
Label1:
 
pop     edi
pop     esi
pop     ebx
pop     ebp
retn


最终代码

__declspec(naked) void 加密call(DWORD 秘钥,DWORD 加密长度,DWORD 加密地址,DWORD 加密地址2)
{
 
 
__asm
{
push    ebp
push    ebx
push    esi
push    edi
mov     edi, dword ptr [esp+0x14]
mov     edx, dword ptr [esp+0x18]
mov     esi, dword ptr [esp+0x1C]
mov     ebp, dword ptr [esp+0x20]
xor     eax, eax
xor     ebx, ebx
cmp     edx, 0
je      Label1
mov     al, byte ptr [edi]
mov     bl, byte ptr [edi+4]
add     edi, 8
lea     ecx, dword ptr [esi+edx]
sub     ebp, esi
mov     dword ptr [esp+0x18], ecx
inc     al
cmp     dword ptr [edi+0x100], -1
je      Label2
mov     ecx, dword ptr [edi+eax*4]
and     edx, 0xFFFFFFFC
 je      Label3
lea     edx, dword ptr [esi+edx-4]
mov     dword ptr [esp+0x1C], edx
mov     dword ptr [esp+0x20], ebp
 
 
Label4:
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
inc     al
and     edx, 0x0FF
mov     ecx, dword ptr [edi+eax*4]
mov     ebp, dword ptr [edi+edx*4]
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
inc     al
and     edx, 0x0FF
ror     ebp, 8
mov     ecx, dword ptr [edi+eax*4]
or      ebp, dword ptr [edi+edx*4]
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
inc     al
and     edx, 0x0FF
ror     ebp, 8
mov     ecx, dword ptr [edi+eax*4]
or      ebp, dword ptr [edi+edx*4]
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
inc     al
and     edx, 0x0FF
ror     ebp, 8
mov     ecx, dword ptr [esp+0x20]
or      ebp, dword ptr [edi+edx*4]
ror     ebp, 8
xor     ebp, dword ptr [esi]
cmp     esi, dword ptr [esp+0x1C]
mov     dword ptr [ecx+esi], ebp
lea     esi, dword ptr [esi+4]
mov     ecx, dword ptr [edi+eax*4]
jb      Label4
cmp     esi, dword ptr [esp+0x18]
je      Label5
mov     ebp, dword ptr [esp+0x20]
 
 
Label3:
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
 inc     al
and     edx, 0x0FF
mov     edx, dword ptr [edi+edx*4]
xor     dl, byte ptr [esi]
lea     esi, dword ptr [esi+1]
mov     ecx, dword ptr [edi+eax*4]
cmp     esi, dword ptr [esp+0x18]
mov     byte ptr [ebp+esi-1], dl
jb      Label3
jmp     Label5
 
 
Label2:
 
movzx   ecx, byte ptr [edi+eax]
 
Label6:
add     bl, cl
movzx   edx, byte ptr [edi+ebx]
mov     byte ptr [edi+ebx], cl
mov     byte ptr [edi+eax], dl
add     dl, cl
movzx   edx, byte ptr [edi+edx]
add     al, 1
xor     dl, byte ptr [esi]
 lea     esi, dword ptr [esi+1]
movzx   ecx, byte ptr [edi+eax]
cmp     esi, dword ptr [esp+0x18]
mov     byte ptr [ebp+esi-1], dl
jb      Label6
Label5:
dec     al
mov     byte ptr [edi-4], bl
mov     byte ptr [edi-8], al
 
Label1:
 
pop     edi
pop     esi
pop     ebx
pop     ebp
retn
 
       }
 
}


12.不走游戏代码自己发送封包
我们简单分析一个喊话封包,开始自己调用不走任何游戏代码
send头文件
#include "winsock.h"
有问题记得加上如下代码
#pragma comment(lib,"ws2_32.lib")

void HXSYDialog::OnBnClickedButton16()
{
 
 
byte a[100]  = {0x11,0x00,0x7E,0x00,0x00,0x00,0x00,0x02,0x00,0x31,0x31,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x60,0xA8,0x6C};
DWORD 包长 = 0x13;
DWORD 包地址 = (DWORD)a;
DWORD 加密地址 = 包地址 + 2;
DWORD 加密长度 = 包长 - 2;
DWORD 秘钥 = 0;
__asm
{
 
mov ecx,0x00f84ba4
        mov ecx,[ecx]
        mov ecx,[ecx]
        mov ecx,[ecx+0x4]
        mov ecx,[ecx+0x14]
        mov ecx,[ecx]
        lea ecx,[ecx+0x54]
mov 秘钥,ecx
 
}
 
加密call(秘钥,加密长度,加密地址,加密地址);
 
 
HWND 窗口句柄 =FindWindowA("Lapis Network Class",0);
DWORD A = GetWindowLongW(窗口句柄,-21);
DWORD S =*(DWORD*)(A+0x38);
 
send(S,(const char*)包地址,包长,0);
 
 
// TODO: 在此添加控件通知处理程序代码
}

我们分析几个明文包  ,然后调用测试

13.收包

收包函数recv,recvfrom, WSARecv
(收包的参数包地址和长度  是准备接受的包地址  和最大包长)
这是必然的,因为调用前是不可能知道收到的内容和具体长度的,只有执行完毕才会获得

recv 的实际包长 在返回值eax

WSARecv 在第四个参数

14.粘包
我们发现断的时间越长 收包越大 原因很简单 因为可能沾包
当我们线程停住的时候,服务器依然在给我们发包,这个时候就会一起接收到
那么停住的时间越长,包就粘的越长.
每个独立的封包都有很明显的划分方法,所以拆包并不难

15.明文收包和解密call
收包我们收到的肯定也是服务器发给我们的加密封包
加密封包经过解密处理,把里面的数据写入到对应内存位置,这样我们就在界面上显示相应的反馈了.

明文收包和明文发包方向是反的
要找去哪里了,所以是下访问断追其去向

我们发现   lpbuffers  +4里的包地址  是固定的, 是否固定其实无所谓
那么我们想追解密函数 就要对  他进行访问断
当然 通过我们自己喊话等方式 让其断下  

2个拷贝的位置都可以
再对拷贝的地址下访问断

返回就是解密call
同时也是明文收包的位置了

然后我们发现    加密和解密是同一个函数
加密和解密是一个可逆的过程
正常我们其实直接搜索call 就可以找到明文收包的位置了 

 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Manifest游戏封包是一种用于存储游戏资源的文件格式,它能够帮助游戏开发者更加高效地管理和使用游戏资源。Manifest封包中包含了游戏资源的信息、定位方式以及其他元数据,这些信息有助于游戏引擎快速准确地加载游戏资源。同时,Manifest封包还可以支持资源压缩和加密,以确保游戏资源的安全性和节省存储空间。 使用Manifest封包可以带来许多好处,包括: 1. 更快的加载速度:Manifest封包中存储了游戏资源的位置和信息,游戏引擎可以快速定位和加载资源,省去了在文件系统中搜索和读取资源的时间。 2. 更高的安全性:Manifest封包可以支持资源的加密和压缩,保护游戏资源不被盗取或篡改。 3. 更简单的管理:Manifest封包能够将多个游戏资源打包到一个文件中,避免了资源散落在不同的文件夹中,使得游戏开发者更加方便地管理游戏资源。 4. 更小的游戏包体:Manifest封包支持资源压缩,可以缩小游戏包体的大小,减少玩家下载和更新游戏的时间和流量。 总之,Manifest封包是一种非常重要的游戏开发工具,在游戏资源的管理和使用上发挥着重要的作用。 ### 回答2: Manifest游戏封包是指包含游戏资源文件的清单文件,常见于游戏开发中。在制作游戏时,游戏文件会被拆分成多个资源文件,如图片、音频、模型等,这些资源文件都需要被封装在一起,以便于游戏程序的读取和管理。 Manifest游戏封包一般包含了游戏中所需加载的所有资源文件的名称、文件路径、版本号等相关信息。游戏程序在启动时,会读取Manifest游戏封包文件,然后根据清单信息去加载游戏所需的资源文件,确保游戏能够正常运行。 Manifest游戏封包除了提供资源文件清单之外,还能够对游戏资源文件进行压缩、加密等处理,为游戏的安全性和性能提供了一定的保障。 总之,Manifest游戏封包是一种有效的游戏资源管理方式,使得游戏的开发、测试和发布变得更加方便、高效、安全。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值