内存断点快速定位分析CCProxy6.0字符串加密算法
CCProxy相信大家都很熟悉了吧,他的注册算法肯定也被大家研究过了,今天我们这篇文章来分析一下
他的字符串加密算法。
[Cracker] : prince
[时间] : 2005.01.23
[声明] : 只做技术交流,不做商业用途,如果你手头宽裕并喜欢这个软件的话请支持正版软件。
[E-mail] : Cracker_prince@163.com
[软件信息] :
[软件说明] :
主要特点:支持共享Modem、ISDN、ADSL、DDN、专线等访问internet。支持HTTP、FTP代理协议
,支持浏览器通过808端口代理上网。支持SOCKS 4/5代理协议,支持OICQ通过1080端口代理上网。可以实时观
测代理用户流量(双击绿色网格),有IP帐号管理功能,可以统计上网时间。支持客户端使用Outlook收发邮件。
支持客户端使用RealPlayer,MediaPlayer。
[保护方式] : 序列号
[外壳保护] : 无
[编译器/语言] : Microsoft Visual C++ 6.0 公司在服务器上装了CCProxy6.0,对公司内部每台PC上网都做了限制(网卡MAC地址绑定),结果大家不
能上QQ,MSN,BT,就连带WEB邮箱的网站也不能访问,发邮件也不能超过10K!真是~!#¥%・!极度郁闷骂公
司极度变态。就这么算了吗?呵呵,当然不会,不然就不会出这篇菜文了。不久前,大概一个月前左右吧,牛
人们发现了CCProxy6.0缓冲区溢出漏洞,写出了溢出工具,爽啊!用工具轻松拿到了服务器的shell,哼哼,看
我怎么搞你!找到CCProxy的安装目录,查看CCProxy.ini文件内容,秘密可就在其中啊!二话不说在我的机器
上也装上CCProxy6.0,拿回服务器的CCProxy.ini文件(具体怎样拿回不是我们的讨论范围,所以略去),覆盖本
机的原配置文件,然后在本机修改配置再将配置文件COPY回服务器覆盖,这样我们的配置就生效了。哈哈,真
是不错,等等,这是什么?要密码才能配置?没关系,在配置文件中找到Password关键字,“=”后面就是密码
了,不过被作者加了密。这个更简单,将密码剪切到其他地方保存,然后再启动软件就可以用空密码配置了。
修改完后再把密码粘贴回来,OK,天衣无缝了。搞定!上网是没有什么限制了,可是好不容易拿到的shell不留
后门启不是太亏了?对网络安全稍微了解的人都知道,通常人们都会习惯性的使用同一个密码,那么刚才我们
看到的密码如果是服务器的Administrator的密码的话,后门也省了,哈哈。说干就干,抄起OD...
软件在启动的时候肯定是要读取配置文件的内容来对自身进行配置,在用户修改完密码后也要将用户
输入的保护密码进行加密后写回配置文件,所以我们可以下断点CreateFileA来对软件启动时读取配置文件时拦
截,也可以选择在用户输入完密码保存时,下断点GetWindowTextA拦截密码比较,进一步跟踪到密码加密的地
方进行分析。我们要得到密码,首选断点就是GetWindowTextA。下断点GetWindowTextA,F9运行,被断下很多
次,但在要求输入密码对话框出现之前都不是目标,一直F9直到点击设置弹出输入密码对话框,输入假码
87654321,确定,被断在这里:
----------------------------------------------------------------------------------------
00458B9E |. 50 PUSH EAX
00458B9F |. E8 4>CALL CCProxy.004551F1
00458BA4 |. 50 PUSH EAX ; |Buffer
00458BA5 |. 56 PUSH ESI ; |hWnd
00458BA6 |. FF15>CALL DWORD PTR DS:[<&USER32.GetWindowTex>; \GetWindowTextA 断在这里
00458BAC |. 8B4D>MOV ECX,DWORD PTR SS:[EBP+10]
00458BAF |. 6A F>PUSH -1
00458BB1 |. E8 1>CALL CCProxy.004551C9
00458BB6 |. EB 0>JMP SHORT CCProxy.00458BC3
00458BB8 |> 8B45>MOV EAX,DWORD PTR SS:[EBP+10]
00458BBB |. FF30 PUSH DWORD PTR DS:[EAX] ; /Arg2
00458BBD |. 56 PUSH ESI ; |Arg1
00458BBE |. E8 8>CALL CCProxy.00457C49 ; \CCProxy.00457C49
00458BC3 |> 5F POP EDI ; 0015ABB0
00458BC4 |. 5E POP ESI
00458BC5 |. 5D POP EBP
00458BC6 \. C2 0>RETN 0C
----------------------------------------------------------------------------------------
接下来F8单步跟踪,很容易来到这里:
----------------------------------------------------------------------------------------
00427A6A . 56 PUSH ESI
00427A6B . 8BF1 MOV ESI,ECX
00427A6D . 6A 0>PUSH 1
00427A6F . E8 D>CALL CCProxy.00453951
00427A74 . 8B46>MOV EAX,DWORD PTR DS:[ESI+5C]
00427A77 . 68 2>PUSH CCProxy.004FC924 ; /Arg2 = 004FC924 ASCII "abc" 真码
00427A7C . 50 PUSH EAX ; |Arg1 = 014F7578 ASCII "87654321" 假码
00427A7D . E8 3>CALL CCProxy.0043FBB4 ; \CCProxy.0043FBB4
00427A82 . 83C4>ADD ESP,8
00427A85 . 85C0 TEST EAX,EAX
00427A87 . 74 5>JE SHORT CCProxy.00427ADA
00427A89 . 57 PUSH EDI
00427A8A . 8BCE MOV ECX,ESI
00427A8C . C705>MOV DWORD PTR DS:[4FD128],0
00427A96 . E8 C>CALL CCProxy.0045155B
00427A9B . A0 8>MOV AL,BYTE PTR DS:[477F80]
00427AA0 . B9 0>MOV ECX,400
----------------------------------------------------------------------------------------
用abc试试看,哈,成功了!再用Administrator和密码abc登陆,嘿!真是老天对我不薄啊!又成功啦
!以后就可以用Administrator登陆啦!(说明:真正的密码当然不是这个了,Boss是不会这么傻D)。CCProxy还
是失败在明码比较。不管他,既然已经分析到这步了,我们来看看他是怎么对字符串加密的吧(晕,才进入正题
)。那么怎样快速定位字符解密的代码位置呢?我们这里重点分析密文解密算法。清除所有断点,F2重新加载程
序,下断CreateFileA, F9运行,断在0041B1CA,此时我们使用内存断点快速定位,ALT + M打开内存镜像,搜
索ASCII码密文:"902901900",找到一处,就在这里下内存访问断点,F9运行,断在这里:
-----------------------------------------------------------------------------------------
00409FD2 |. 57 PUSH EDI
00409FD3 |. B9 0>MOV ECX,400 ; ECX=400h
00409FD8 |. 33C0 XOR EAX,EAX ; EAX清零
00409FDA |. 8D7C>LEA EDI,DWORD PTR SS:[ESP+D]
00409FDE |. 8854>MOV BYTE PTR SS:[ESP+C],DL ; 0写入堆栈[ESP+C]
00409FE2 |. F3:A>REP STOS DWORD PTR ES:[EDI] ; 重复前缀,重复次数CX=400h(1024d),执行完
ECX=0
00409FE4 |. B9 0>MOV ECX,400 ; ECX=400h
00409FE9 |. 8DBC>LEA EDI,DWORD PTR SS:[ESP+1411] ; [ESP+1411]=0012BBA1
00409FF0 |. 8894>MOV BYTE PTR SS:[ESP+1410],DL ; 将DL=0写入堆栈[ESP+1410]
00409FF7 |. 8D54>LEA EDX,DWORD PTR SS:[ESP+C] ; [ESP+C]=0,ESP+C=0x12A79C
00409FFB |. F3:A>REP STOS DWORD PTR ES:[EDI] ; 重复前缀,执行完ECX清零
00409FFD |. 8BBC>MOV EDI,DWORD PTR SS:[ESP+341C] ; [ESP+0x341C]为加密字符串
0040A004 |. 83C9>OR ECX,FFFFFFFF ; 0 OR FFFFFFFF
0040A007 |. F2:A>REPNE SCAS BYTE PTR ES:[EDI] ; 断在这里,结果ECX=FFFFFFF5
0040A009 |. F7D1 NOT ECX ; ~FFFFFFF5==0xA
0040A00B |. 2BF9 SUB EDI,ECX ; 将EDI指针指向密文
0040A00D |. 33ED XOR EBP,EBP ; EBP清零
0040A00F |. 8BC1 MOV EAX,ECX ; ECX=0xA
0040A011 |. 8BF7 MOV ESI,EDI ; EDI==密文902901900
0040A013 |. 8BFA MOV EDI,EDX ; EDX==0x12A79C
0040A015 |. C1E9>SHR ECX,2 ; 0xA>>2==2
0040A018 |. F3:A>REP MOVS DWORD PTR ES:[EDI],DWO>; EDX=90290190,同时ECX清零
0040A01A |. 8BC8 MOV ECX,EAX ; EAX==0xA
0040A01C |. 33C0 XOR EAX,EAX ; EAX清零
0040A01E |. 83E1>AND ECX,3 ; 0xA and 3 == 2
0040A021 |. F3:A>REP MOVS BYTE PTR ES:[EDI],BYTE>; 执行完EDX=902901900,ECX清零
0040A023 |. 8D7C>LEA EDI,DWORD PTR SS:[ESP+C] ; 密文地址存入EDI
0040A027 |. 83C9>OR ECX,FFFFFFFF ; 0 OR FFFFFFFF==FFFFFFFF
0040A02A |. F2:A>REPNE SCAS BYTE PTR ES:[EDI] ; 执行完ECX=FFFFFFF5
0040A02C |. F7D1 NOT ECX ; 取反之后ECX=0xA
0040A02E |. 49 DEC ECX ; 0xA-1=9,以上操作实际上是取密文个数
0040A02F |. B8 A>MOV EAX,AAAAAAAB
0040A034 |. F7E1 MUL ECX ; EAX*ECX(9),结果60000003,EAX=3,EDX=6
0040A036 |. D1EA SHR EDX,1 ; EDX=6>>1==3
0040A038 |. 0F84>JE CCProxy.0040A0F0
0040A03E |. 53 PUSH EBX
0040A03F |. 8D5C>LEA EBX,DWORD PTR SS:[ESP+10] ; [ESP+10]为加密字符串
0040A043 |> 8BFB /MOV EDI,EBX ; EBX=密文902901900
0040A045 |. 83C9>|OR ECX,FFFFFFFF ; 9 OR FFFFFFFF = FFFFFFFF
0040A048 |. 33C0 |XOR EAX,EAX ; EAX清零
0040A04A |. 8D94>|LEA EDX,DWORD PTR SS:[ESP+2418>; ESP+2418==0x12CBA4
0040A051 |. F2:A>|REPNE SCAS BYTE PTR ES:[EDI] ; 执行完ECX=FFFFFFF5
0040A053 |. F7D1 |NOT ECX ; 取反ECX=0xA
0040A055 |. 2BF9 |SUB EDI,ECX ; 将EDI指针指向密文902901900
0040A057 |. 8BC1 |MOV EAX,ECX ; ECX=0xA
0040A059 |. 8BF7 |MOV ESI,EDI ; EDI=密文902901900
0040A05B |. 8BFA |MOV EDI,EDX ; EDX=0x12CBA4
0040A05D |. C1E9>|SHR ECX,2 ; 0xA >> 2==2
0040A060 |. F3:A>|REP MOVS DWORD PTR ES:[EDI],DW>; 执行完ECX=0,EDX=90290190
0040A062 |. 8BC8 |MOV ECX,EAX ; EAX=0xA
0040A064 |. 83E1>|AND ECX,3 ; ECX=(0xA and 3)=2
0040A067 |. F3:A>|REP MOVS BYTE PTR ES:[EDI],BYT>; 执行完ECX=0,EDX=密文902901900
0040A069 |. 8D8C>|LEA ECX,DWORD PTR SS:[ESP+2418>; 密文地址存入ECX
0040A070 |. C684>|MOV BYTE PTR SS:[ESP+241B],0 ; 将0写入堆栈,代替原来的901中的9,目的是截取
密文的前3个字符
0040A078 |. 51 |PUSH ECX ; 密文的前3个字符压栈,"902"
0040A079 |. E8 2>|CALL CCProxy.0043FBA9 ; 关键函数,单个字符解密,跟进
0040A07E |. B9 E>|MOV ECX,3E7 ; 0x3E7送入ECX
0040A083 |. 8D94>|LEA EDX,DWORD PTR SS:[ESP+1018>; 堆栈地址[ESP+1018]=0x12B7A0
0040A08A |. 2BC8 |SUB ECX,EAX ; 3E7-386=61
0040A08C |. 51 |PUSH ECX ; 此处即为解密结果
0040A08D |. 68 E>|PUSH CCProxy.004730E0 ; ASCII "%c"
0040A092 |. 52 |PUSH EDX
-----------------------------------------------------------------------------------------
0040A079 |. E8 2>|CALL CCProxy.0043FBA9,这里跟进,来到这里:
-----------------------------------------------------------------------------------------
0043FBA9 /$ FF74>PUSH DWORD PTR SS:[ESP+4] ; [ESP+4]=902
0043FBAD |. E8 6>CALL CCProxy.0043FB1E ; 跟进
0043FBB2 |. 59 POP ECX
0043FBB3 \. C3 RETN
-----------------------------------------------------------------------------------------
来自0043FBAD:
0043FB1E /$ 53 PUSH EBX ; EBX=密文902901900
0043FB1F |. 55 PUSH EBP ; EBP=0
0043FB20 |. 56 PUSH ESI ; ESI=0x12A7A6
0043FB21 |. 57 PUSH EDI ; EDI=0x12CBAE
0043FB22 |. 8B7C>MOV EDI,DWORD PTR SS:[ESP+14] ; [ESP+14]单个字符902
0043FB26 |> 833D>/CMP DWORD PTR DS:[477860],1 ; [477860]=1的值同1比较
0043FB2D |. 7E 0>|JLE SHORT CCProxy.0043FB3E ; 等于,所以跳走
0043FB2F |. 0FB6>|MOVZX EAX,BYTE PTR DS:[EDI]
0043FB32 |. 6A 0>|PUSH 8
0043FB34 |. 50 |PUSH EAX
0043FB35 |. E8 F>|CALL CCProxy.00445339
0043FB3A |. 59 |POP ECX
0043FB3B |. 59 |POP ECX
0043FB3C |. EB 0>|JMP SHORT CCProxy.0043FB4D
0043FB3E |> 0FB6>|MOVZX EAX,BYTE PTR DS:[EDI] ; [EDI]=902,单个字符9的ASCII码39送入EAX
0043FB41 |. 8B0D>|MOV ECX,DWORD PTR DS:[477210] ; CCProxy.0047721A,这里存放一张辅助表
0043FB47 |. 8A04>|MOV AL,BYTE PTR DS:[ECX+EAX*2] ; [0x47721A+39*2]=00100084,读取表中内容
0043FB4A |. 83E0>|AND EAX,8 ; (0x84 and 8)= 0
0043FB4D |> 85C0 |TEST EAX,EAX
0043FB4F |. 74 0>|JE SHORT CCProxy.0043FB54 ; EAX=0,所以跳走
0043FB51 |. 47 |INC EDI
0043FB52 |.^ EB D>\JMP SHORT CCProxy.0043FB26
0043FB54 |> 0FB6>MOVZX ESI,BYTE PTR DS:[EDI] ; [EDI]=902,单个字符9的ASCII码39送入ESI
0043FB57 |. 47 INC EDI ; EDI指针前移,指向02
0043FB58 |. 83FE>CMP ESI,2D ; 0x39同0x2D("-")比较
0043FB5B |. 8BEE MOV EBP,ESI ; ESI=39h
0043FB5D |. 74 0>JE SHORT CCProxy.0043FB64
0043FB5F |. 83FE>CMP ESI,2B ; 39同("+")比较
0043FB62 |. 75 0>JNZ SHORT CCProxy.0043FB68
0043FB64 |> 0FB6>MOVZX ESI,BYTE PTR DS:[EDI]
0043FB67 |. 47 INC EDI
0043FB68 |> 33DB XOR EBX,EBX ; EBX=密文902901900
0043FB6A |> 833D>/CMP DWORD PTR DS:[477860],1 ; [477860]=1,同1比较,搜索[477860],没发现往
其中写其他值的地方,好象其始终是1
0043FB71 |. 7E 0>|JLE SHORT CCProxy.0043FB7F ; 相等,所以跳
0043FB73 |. 6A 0>|PUSH 4
0043FB75 |. 56 |PUSH ESI
0043FB76 |. E8 B>|CALL CCProxy.00445339
0043FB7B |. 59 |POP ECX
0043FB7C |. 59 |POP ECX
0043FB7D |. EB 0>|JMP SHORT CCProxy.0043FB8A
0043FB7F |> A1 1>|MOV EAX,DWORD PTR DS:[477210] ; [477210]=0x47721A,从这开始是一张表
0043FB84 |. 8A04>|MOV AL,BYTE PTR DS:[EAX+ESI*2] ; [0x47721A+39*2],39为"902"中"9"的ASCII码
0043FB87 |. 83E0>|AND EAX,4 ; 477284 and 4,EAX=4
0043FB8A |> 85C0 |TEST EAX,EAX ; 如果EAX=0,认为密文计算结束
0043FB8C |. 74 0>|JE SHORT CCProxy.0043FB9B
0043FB8E |. 8D04>|LEA EAX,DWORD PTR DS:[EBX+EBX*>; EBX=0,第2遍EBX=9,第3遍EBX=5A
0043FB91 |. 8D5C>|LEA EBX,DWORD PTR DS:[ESI+EAX*>; [39+0*2-30]=9,第2遍[30+2D*2-30]=5A,第3次
[32+5A*2-30]=386,每次循环都以计算出的EBX为下一次循环的关键值
0043FB95 |. 0FB6>|MOVZX ESI,BYTE PTR DS:[EDI] ; 将第二个字符0送入ESI,第2遍将第3个字符32送
入ESI
0043FB98 |. 47 |INC EDI ; EDI指针前移
0043FB99 |.^ EB C>\JMP SHORT CCProxy.0043FB6A
0043FB9B |> 83FD>CMP EBP,2D ; EBP=39
0043FB9E |. 8BC3 MOV EAX,EBX ; EBX=386,上面循环的计算结果
0043FBA0 |. 75 0>JNZ SHORT CCProxy.0043FBA4
0043FBA2 |. F7D8 NEG EAX
0043FBA4 |> 5F POP EDI
0043FBA5 |. 5E POP ESI
0043FBA6 |. 5D POP EBP
0043FBA7 |. 5B POP EBX
0043FBA8 \. C3 RETN
-----------------------------------------------------------------------------------------
执行完上面的函数之后返回到这里:
-----------------------------------------------------------------------------------------
0040A079 |. E8 2>|CALL CCProxy.0043FBA9 ; 单个字符解密,返回到下面这条语句
0040A07E |. B9 E>|MOV ECX,3E7 ; 0x3E7送入ECX,
0040A083 |. 8D94>|LEA EDX,DWORD PTR SS:[ESP+1018>; 堆栈地址[ESP+1018]=0x12B7A0
0040A08A |. 2BC8 |SUB ECX,EAX ; 3E7-386=61,61为"a"的ASCII码,单个字符解密
完毕
0040A08C |. 51 |PUSH ECX ; 此处即为解密结果
0040A08D |. 68 E>|PUSH CCProxy.004730E0 ; ASCII "%c"
0040A092 |. 52 |PUSH EDX -----------------------------------------------------------------------------------------
好了,以上就是密文解密的全部过程,加密是个逆过程,不去管他了,如果你有兴趣可以自己去研究
一下。
整理:
密文是这样的格式:单个字符转换为3个数字的密文,所以循环取3位密文送到解密函数
解密。解密过程如下,C语言表述:
------------------------------------------------------------------------------------------
#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
void DecryptString(char *pchEnString, unsigned short *pDeCode)
{
int i;
unsigned short dwTemp01;
unsigned short dwTemp02 = 0;
for (i = 0; i < 3; i++, pchEnString++)
{
dwTemp01 = dwTemp02 + (dwTemp02 * 4);
dwTemp02 = *pchEnString + (dwTemp01 * 2) - 0x30;
}
*pDeCode = dwTemp02;
}
int main(int argc, char* argv[])
{
char chEnString[128];
// 获取密文
printf("Please input Encryped string\n");
scanf("%s", chEnString);
int nKeyCount;
int nEnStringCount = 0;
int i;
for (i = 0; i < 128; i++)
{
if ('\0' == chEnString[i])
{
break;
}
nEnStringCount++;
}
nKeyCount = nEnStringCount / 3;
unsigned int *pdwSingleEnStr = (unsigned int *)malloc(sizeof(unsigned int) * nKeyCount);
// Memory 01
char *p = chEnString;
for (i = 0; i < nKeyCount; i++)
{
pdwSingleEnStr[i] = (unsigned int)p;
p += 3;
}
unsigned short dwDecode[1];
char chSourceKey[128] = {0};
for (i = 0; i < nKeyCount; i++)
{
DecryptString((char *)pdwSingleEnStr[i], dwDecode);
dwDecode[0] = 0x3E7 - dwDecode[0];
chSourceKey[i] = (char)dwDecode[0];
}
if (pdwSingleEnStr != NULL) // release Memory 01
{
free(pdwSingleEnStr);
pdwSingleEnStr = NULL;
}
printf("Source Key: %s\n", chSourceKey);
return 0;
}
为了方便大家,我用VC写了一个WIN32的CCProxy密文解密器,不是注册机哦,请大家测试。
------------------------------------------------------------------------------------------
以上是菜鸟学习算法分析的一个学习过程,所以拿简单的来开刀,希望也能让更多的像我一样小菜们
加入算法分析的行列中来,如有失误,请大侠指教。
菜鸟写菜文。
prince 2005.01.23附件:DeCryptCC.rar
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!