首页
社区
课程
招聘
[原创]破解金山某游戏封包加密算法
发表于: 2013-2-6 13:55 8440

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

2013-2-6 13:55
8440

什么游戏我就不说了,反正是金山的游戏。

该游戏加密算法相当简单,但是就是没有人去破解它,大家都以为加密算法很难,所以都不去破

解,致使这个简单的加密算法一直逍遥法外,这就像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;
                }
        }
}


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 6
支持
分享
最新回复 (12)
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
看了这个突然明白了好多..写的很详细..加油楼主
2013-2-6 16:27
0
雪    币: 20
活跃值: (40)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
我顶我顶我顶顶顶
2013-2-13 23:16
0
雪    币: 40
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
这个真没想到呢,楼主有心!
2013-2-14 00:01
0
雪    币: 20
活跃值: (40)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
我顶我顶我顶顶顶
2013-3-4 17:23
0
雪    币: 3836
活跃值: (4142)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
支持。。。。。。。。。。。。。。。
2013-3-4 19:02
0
雪    币: 10398
活跃值: (5288)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
密钥表 怎么找
2013-7-27 11:56
0
雪    币: 144
活跃值: (63)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
mark,还是不mark,这是一个问题
2014-9-12 18:53
0
雪    币: 97
活跃值: (225)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
我顶
2014-9-13 14:53
0
雪    币: 40
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
暂时是小白,现在只能——膜拜。向楼主学习。
2014-9-14 23:16
0
雪    币: 43
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
受教了,学习学习
2014-9-16 13:20
0
雪    币: 24
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
看了LZ的分析过程学习了很多东西。多谢LZ分享
2014-9-16 20:33
0
雪    币: 243
活跃值: (86)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
学习了,学习楼主分享
2014-11-3 17:40
0
游客
登录 | 注册 方可回帖
返回
//