欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]破解金山某游戏封包加密算法

[原创]破解金山某游戏封包加密算法

什么游戏我就不说了,反正是金山的游戏。
该游戏加密算法相当简单,但是就是没有人去破解它,大家都以为加密算法很难,所以都不去破
解,致使这个简单的加密算法一直逍遥法外,这就像0day挖掘一样,不要放过任何一个可能性,否
则你就等于放弃了一次0day的机会,哪怕它多么的简单。
开始:
OD attach游戏进程,然后OD崩溃,证明该游戏有反调试,然后,确定游戏没有内核保护之后开
strongod过反调试,然后bp send,返回游戏中走两步,断下后alt+f9回到游戏领空,发现:

从图中可得知EBX中存储的就是密文缓冲区首地址,接下来那就找EBX中的值是从哪获取的,然后
向上找,找到这么一处:

可知是从EDX中获取,然后再向上找EDX中的值是如何获取,依次类推找到这一处:

经过F8反复调试,发现call eax过后的返回值eax中存储的正是密文缓冲区地址,然后mov
edx,eax,将地址写入edx中,接着sub edx,2,将缓冲区扩充2字节,add edi,2是将缓冲区长
度增加2字节,因为edi中存储的就是缓冲区长度,不要问我为什么,在OD中反复跟踪几次就明白
了,最后一句:mov word ptr ds:[edx],di是将长度写入缓冲区结尾,这也就是说发送的封包中
最后2字节是代表封包长度,而且是未加密的,解密时一定要注意把这两字节剔除,否则就错了。
从上面的代码可以看出call eax是获取缓冲区的,那么F7进入后看看,代码如下:

由此可知缓冲区由ecx中来,偏移14,那么继续往上找,找到最后发现这么一处:

反复调试过后发现memcpy将明文复制到一个缓冲区中,而该缓冲区正是前面ecx偏移14那个地方
的值,好了,这下找到了缓冲区的由来了,接下来继续调试……
经过翻来覆去调试了N遍后发现一个惊人的秘密:

当call ecx执行之前,封包的缓冲区是4c 00 00 00……,执行call ecx过后,封包的缓冲区就变
为一堆乱七八糟的数字了,这证明call ecx就是传说中的加密算法。
接着,F7步入传说中的加密算法看看:

然后继续F7步入上图的那个call就到达加密算法入口地址了,接下来就是要分析加密过程了,加密
算法函数如下:
.text:0079E670 arg_0           = dword ptr  4
.text:0079E670 arg_4           = dword ptr  8
.text:0079E670 arg_8           = dword ptr  0Ch
                            push    ebx
.text:0079E671                 push    ebp
.text:0079E672                 mov     ebp, [esp+8+arg_8]
.text:0079E676                 mov     ecx, [ebp+0]
.text:0079E679                 push    esi
.text:0079E67A                 mov     esi, [esp+0Ch+arg_0]
.text:0079E67E                 mov     ebx, esi
.text:0079E680                 and     ebx, 3
.text:0079E683                 shr     esi, 2
.text:0079E686                 push    edi
.text:0079E687                 mov     edi, [esp+10h+arg_4]
.text:0079E68B                 jz      short loc_79E6B5
.text:0079E68D                 lea     ecx, [ecx+0]
.text:0079E690
.text:0079E690 loc_79E690:                             ; CODE XREF: sub_79E670+43j
.text:0079E690                 sub     esi, 1
.text:0079E693                 lea     eax, [ecx+esi]
.text:0079E696                 xor     edx, edx
.text:0079E698                 div     dword_91E9B4
.text:0079E69E                 add     edi, 4
.text:0079E6A1                 mov     ecx, dword_9190F8[edx*4]
.text:0079E6A8                 add     ecx, 2E6D23C1h
.text:0079E6AE                 xor     [edi-4], ecx
.text:0079E6B1                 test    esi, esi
.text:0079E6B3                 ja      short loc_79E690
.text:0079E6B5
.text:0079E6B5 loc_79E6B5:                             ; CODE XREF: sub_79E670+1Bj
.text:0079E6B5                 xor     edx, edx
.text:0079E6B7                 mov     eax, ebx
.text:0079E6B9                 div     dword_91E9B4
.text:0079E6BF                 xor     ecx, dword_9190F8[edx*4]
.text:0079E6C6                 test    ebx, ebx
.text:0079E6C8                 jbe     short loc_79E6DF
.text:0079E6CA                 lea     ebx, [ebx+0]
.text:0079E6D0
.text:0079E6D0 loc_79E6D0:                             ; CODE XREF: sub_79E670+6Dj
.text:0079E6D0                 xor     [edi], cl
.text:0079E6D2                 sub     ebx, 1
.text:0079E6D5                 add     edi, 1
.text:0079E6D8                 shr     ecx, 8
.text:0079E6DB                 test    ebx, ebx
.text:0079E6DD                 ja      short loc_79E6D0
.text:0079E6DF
.text:0079E6DF loc_79E6DF:                             ; CODE XREF: sub_79E670+58j
.text:0079E6DF                 mov     eax, [ebp+0]
.text:0079E6E2                 mov     ecx, eax
.text:0079E6E4                 shl     ecx, 5
.text:0079E6E7                 sub     ecx, eax
.text:0079E6E9                 pop     edi
.text:0079E6EA                 add     ecx, 8088405h
.text:0079E6F0                 pop     esi
.text:0079E6F1                 mov     [ebp+0], ecx
.text:0079E6F4                 pop     ebp
.text:0079E6F5                 mov     eax, 1
.text:0079E6FA                 pop     ebx
.text:0079E6FB                 retn
用OD反复调试过后分析的加密过程:
1.从某一基址处获取初始密钥
2.将缓冲区大小(未添加2字节之前的大小)右移2位
3.循环,循环次数为右移2位后的结果
    循环中:
      a.密钥=密钥+循环次数
      b.取余=密钥%0x162f
      c.密钥=取余*4+0x9190f8
         d.从密钥地址处获取一个新的密钥
      e.新密钥+=0x2e6d23c1
          f.取明文后4字节与新密钥异或
4.and结果=将缓冲区大小(未添加2字节之前的大小)and 3
5.and取余=and结果%0x162f
6.地址1=and取余*4+0x9190f8
7.从密钥地址处获取一个新的密钥
8.从地址1处取值,然后与密钥异或,生成新密钥
9.将密钥低8位与剩下的最后一个字节进行异或
至此加密完成。
加密过程不难就是以4字节为一个密钥对封包进行异或,密钥的生成就是按照上述的过程生成的,
后一个密钥的生成与前一个密钥相关,也就是说只要有一个密钥生成错误,那么后面的密钥都将生
成错误,而初始密钥是存储在某个基址处的,最终用OD按照开始的向上寻找的办法就可以找到,
解密过程其实就是加密过程,因为a^b=c 知道b和c后可以b^c=a,所以该函数给他密文他出来的
就是明文,给他明文他出来的就是密文,最后用C++写出:
void encrypt(HANDLE handle,BYTE buf[],int len)
{
        int a=len && 3;
        int b=len >> 2;
        DWORD encryptKey=NULL;//密钥
        DWORD keyBaseAddr=0xd2a4ec;//密钥基址
        DWORD keyAddress;
        if(!::ReadProcessMemory(handle,(LPVOID)keyBaseAddr,&keyAddress,sizeof(DWORD),NULL)){
                printf("密钥基址读取失败,错误代码:%d\n",::GetLastError());
                return;
        }
        keyAddress+=0x1c;
        if(!::ReadProcessMemory(handle,(LPVOID)keyAddress,&keyAddress,sizeof(DWORD),NULL)){
                printf("密钥内存地址读取失败,错误代码:%d\n",::GetLastError());
                return;
        }
        keyAddress+=0xc;
        if(!::ReadProcessMemory(handle,(LPVOID)keyAddress,&encryptKey,sizeof(DWORD),NULL)){
                printf("密钥读取失败,错误代码:%d\n",::GetLastError());
                return;
        }
        int d;
        int count=0;
        while(b--){
                encryptKey=encryptKey+b;
                d=encryptKey%0x162f;
                encryptKey=(d*4+0x9190f8);
                if(!::ReadProcessMemory(handle,(LPVOID)encryptKey,&encryptKey,sizeof(DWORD),NULL)){
                        printf("生成密钥失败,错误代码:%d\n",::GetLastError());
                }
                encryptKey+=0x2e6d23c1;
                DWORD dwBuf;
                memcpy(&dwBuf,(LPVOID)&buf[count],4);
                dwBuf=dwBuf^encryptKey;
                memcpy(&buf[count],(LPVOID)&dwBuf,4);
                count+=4;
        };
        int f=a%0x162f;
        DWORD g=f*4+0x9190f8;
        if(!::ReadProcessMemory(handle,(LPVOID)g,&g,sizeof(DWORD),NULL)){
                printf("生成密钥失败,错误代码:%d\n",::GetLastError());
        }
        encryptKey=encryptKey^g;
        if(a>0)
        {
                while(a--)
                {
                        BYTE by=buf[count];
                        _asm{
                                push eax
                                push ebx
                                mov al,byte ptr by
                                mov ebx,encryptKey
                                xor al,bl
                                mov byte ptr by,al
                                pop ebx
                                pop eax
                        }
                        buf[count]=by;
                        count++;
                        encryptKey=encryptKey >> 8;
                }
        }
}

上传的附件 未命名.jpg
未命名2.jpg
未命名3.jpg
未命名4.jpg
aa1.png
aa2.png
aa3.png

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功