• 标 题: 【原创】内存断点快速定位分析CCProxy6.0字符串加密算法
  • 作 者:prince
  • 时 间:2005-01-23 22:51

内存断点快速定位分析CCProxy6.0字符串加密算法
  
 CCProxy相信大家都很熟悉了吧,他的注册算法肯定也被大家研究过了,今天我们这篇文章来分析一下他的字符串加密算法。
[Cracker]     : prince
[时间]        : 2005.01.23
[声明]        : 只做技术交流,不做商业用途,如果你手头宽裕并喜欢这个软件的话请支持正版软件。
[E-mail]      : Cracker_prince@163.com
[保护方式]    : 序列号
[外壳保护]    : 无
[编译器/语言] : 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