逆向3

来源:互联网 发布:178炉石传说数据库 编辑:程序博客网 时间:2024/05/06 13:25
载入运行,并输入name:duan  password:123456,如图,显示Wrong。

peid检查没有壳,并得到里面含有base64,DES,MD5算法;正好可以锻炼下才学的密码学。


OD载入直接运行后会直接结束,说明有反调试,检测到了调试器,重新载入看看能不能找到刚显示的字符串,

并没有找到,关键信息隐藏了,不过显示了很多逆向工具的名称,说明程序检测了这些调试器是否运行,还有“A——+/”的字符串这确实是base64的特点。分别到ollydbg.exe和OllyDbg的调用处。后来用IDA才发现,程序的每次载入的内存都不一样,所以这里的地址仅作参考。
00361AD0  /$  55            push ebp                                 ;  反调试1
00361AD1  |.  8BEC          mov ebp,esp
…………
00361AF2  |.  E8 4F240000   call <jmp.&KERNEL32.CreateToolhelp32Snap>; \CreateToolhelp32Snapshot
00361AF7  |.  8BF8          mov edi,eax
00361AF9  |.  8D85 D4FEFFFF lea eax,[local.75]
00361AFF  |.  50            push eax                                 ; /lppe
00361B00  |.  57            push edi                                 ; |hSnapshot
00361B01  |.  E8 3A240000   call <jmp.&KERNEL32.Process32First>      ; \Process32First
…………
00361B21  |.  B9 DC523600   |mov ecx,crackme6.003652DC               ;  ollyice.exe
…………
00361B57  |.  0F84 CC000000 |je crackme6.00361C29
00361B5D  |.  B9 D0523600   |mov ecx,crackme6.003652D0               ;  olydbg.exe
00361B62  |.  8D85 F8FEFFFF |lea eax,[local.66]
…………
00361B8F  |.  0F84 94000000 |je crackme6.00361C29
00361B95  |.  B9 C4523600   |mov ecx,crackme6.003652C4               ;  peid.exe
00361B9A  |.  8D85 F8FEFFFF |lea eax,[local.66]
…………
00361BC7  |.  74 60         |je Xcrackme6.00361C29
00361BC9  |.  B9 B8523600   |mov ecx,crackme6.003652B8               ;  lda.exe
…………
00361BFB  |.  74 2C         |je Xcrackme6.00361C29
00361BFD  |.  8D95 D4FEFFFF |lea edx,[local.75]
00361C03  |.  52            |push edx                                ; /lppe
00361C04  |.  57            |push edi                                ; |hSnapshot
00361C05  |.  E8 30230000   |call <jmp.&KERNEL32.Process32Next>      ; \Process32Next
00361C0A  |.  85C0          |test eax,eax
00361C0C  |.^ 0F85 03FFFFFF \jnz crackme6.00361B15
00361C12  |.  5E            pop esi
00361C13  |>  57            push edi                                 ; /hObject
00361C14  |.  FF15 08503600 call dword ptr ds:[<&KERNEL32.CloseHandl>; \CloseHandle
…………
00361C28  |.  C3            retn
遍历进程看是否有如上调试器运行有则结束,看来不光检测OD。

00361920  /$  56            push esi                                 ;  反调试2
…………
0036192A  |.  68 98523600   push crackme6.00365298                   ; |OllyDbg
0036192F  |.  FFD6          call esi                                 ; \FindWindowA
…………
00361943  |.  68 90523600   push crackme6.00365290                   ; |1212121
…………
00361956  |.  68 88523600   push crackme6.00365288                   ; |icu_dbg
0036195B  |.  FFD6          call esi                                 ; \FindWindowA
…………
00361967  |.  6A 00         push 0x0                                 ; /Title = NULL
00361969  |.  68 80523600   push crackme6.00365280                   ; |pe--diy
…………
0036197A  |.  6A 00         push 0x0                                 ; /Title = NULL
0036197C  |.  68 78523600   push crackme6.00365278                   ; |odbydyk
…………
003619A0  |.  6A 00         push 0x0                                 ; /Title = NULL
003619A2  |.  68 5C523600   push crackme6.0036525C                   ; |TIdaWindow
…………
003619B5  |.  68 54523600   push crackme6.00365254                   ; |TESTDBG
…………
003619D9  |.  6A 00         push 0x0                                 ; /Title = NULL
003619DB  |.  68 48523600   push crackme6.00365248                   ; |Shadow
…………
00361A01  |.  68 34523600   push crackme6.00365234                   ; |PEiD v0.94
…………
00361A14  \.  C3            retn
第二处检测有没有上述的窗口打开 有则结束,这两处,直接用十六进制编辑器把字符串改一下,就可以通过。
在这两个函数入口设断即可断下,在入口函数处即可发现三次函数调用。
…………
00181E40   .  00E8          add al,ch
00181E42   .  E8 89FCFFFF   call crackme6.00181AD0    ;反调试1 上面
00181E47   .  E8 D4FAFFFF   call crackme6.00181920        ;反调试2 上面
00181E4C   .  E8 EFFDFFFF   call crackme6.00181C40

第三个函数也是反调试:
00181C40  |$  55            push ebp
00181C41  |.  8BEC          mov ebp,esp
00181C43  |.  51            push ecx
00181C44  |.  C745 FC 00000>mov [local.1],0x0
00181C4B  |.  64:A1 3000000>mov eax,dword ptr fs:[0x30]              ;  反调试3
00181C51  |.  3E:0FB640 02  movzx eax,byte ptr ds:[eax+0x2]    ;判断peb+0x2处的BeingDebugged
00181C56  |.  8945 FC       mov [local.1],eax
IDA载入,能直接来到main处,虽然程序运行时地址是动态的,但可以计算偏移,在OD中找到main的入口。设断。程序先运行完三个反调试函数,再到main。
用IDA发现里面调用CreateThread函数创建子线程,
.text:00402966                 push    ecx
.text:00402967                 push    edx
.text:00402968                 call    Print_401EE0    ;打印输出
.text:0040296D                 add     esp, 8
.text:00402970                 mov     ecx, eax
.text:00402972                 call    ds:??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z ; std::basic_ostream<char,std::char_traits<char>>::operator<<(std::basic_ostream<char,std::char_traits<char>> & (*)(std::basic_ostream<char,std::char_traits<char>> &))
.text:00402978                 push    ebx             ; lpThreadId
.text:00402979                 push    ebx             ; dwCreationFlags
.text:0040297A                 push    ebx             ; lpParameter
.text:0040297B                 push    offset StartAddress ; lpStartAddress
.text:00402980                 push    ebx             ; dwStackSize
.text:00402981                 push    ebx             ; lpThreadAttributes
.text:00402982                 call    ds:CreateThread
.text:00402988
.text:00402988 loc_402988:                             ; CODE XREF: _main+E1j
.text:00402988                 call    Fan1_401AD0     ; 遍历进程看是否有od,ice,IDA,peid运行
.text:0040298D                 call    Fan2_401920     ; 查看是否有逆向工具
.text:00402992                 mov     [ebp+var_1C], ebx
.text:00402995                 mov     eax, large fs:30h
可以看出主线程在创建线程后,一直循环调用反调试函数1,2来检测。在子线程处设断,当停在子线程入口处时,将主线程挂起。
……
00182538  |.  64:A3 0000000>mov dword ptr fs:[0],eax
0018253E  |.  8B0D 8C501800 mov ecx,dword ptr ds:[<&MSVCP100.std::ci>;  MSVCP100.std::cin
00182544  |.  8D45 C4       lea eax,[local.15]
00182547  |.  50            push eax
00182548  |.  51            push ecx
00182549  |.  E8 E2FBFFFF   call crackme6.00182130                   ;  输入
0018254E  |.  83C4 08       add esp,0x8
00182551  |.  E8 1AF7FFFF   call crackme6.00181C70                   ;  有检测,进去改一下跳转不要让它结束。
00182556  |.  6A 08         push 0x8
继续往下:
00182583  |.  8D70 01       lea esi,dword ptr ds:[eax+0x1]
00182586  |>  8A10          /mov dl,byte ptr ds:[eax]
00182588  |.  40            |inc eax
00182589  |.  3AD3          |cmp dl,bl
0018258B  |.^ 75 F9         \jnz Xcrackme6.00182586        ;循环计算name长度
0018258D  |.  2BC6          sub eax,esi
0018258F  |.  50            push eax                                 ;  size
00182590  |.  8D45 C4       lea eax,[local.15]
00182593  |.  50            push eax                                 ;  buff
00182594  |.  E8 67EFFFFF   call crackme6.00181500                   ;  转成ASICC
00182599  |.  8D8D 3CFFFFFF lea ecx,[local.49]
0018259F  |.  51            push ecx
001825A0  |.  E8 6B0D0000   call crackme6.00183310                   ;  有转换 MD5
跟踪参数返回的结果,这样可以省去分析代码的时间,查看结果为  5d676628e56b33c125c3e2f04656afdd,就是上述三种算法的一种,在网上在线MD5解密后为duan,说明就是MD5加密了。接下来看:
001825A5  |.  83C4 20       add esp,0x20
001825A8  |.  8BC8          mov ecx,eax
001825AA  |.  895D FC       mov [local.1],ebx
001825AD  |.  E8 1EFFFFFF   call crackme6.001824D0                   ;  有转换返回的结果为25c3e2f0 刚好是上面MD5加密后的值的一部分
001825B2  |.  3978 14       cmp dword ptr ds:[eax+0x14],edi
001825B5  |. /72 02         jb Xcrackme6.001825B9
001825B7  |. |8B00          mov eax,dword ptr ds:[eax]
001825B9  |> \50            push eax                                 ; |src
001825BA  |.  8D55 E4       lea edx,[local.7]                        ; |
001825BD  |.  52            push edx                                 ; |dest
001825BE  |.  FF15 D8501800 call dword ptr ds:[<&MSVCR100.strncpy>]  ; \strncpy 将有name变换得来的key1=25c3e2f0换个位置
001825C4  |.  83C4 0C       add esp,0xC
001825C7  |.  39BD 6CFFFFFF cmp [local.37],edi

接着往下面回来到第二个输入调用;
00182667  |.  8D45 9C       lea eax,[local.25]
0018266A  |.  50            push eax
0018266B  |.  51            push ecx
0018266C  |.  E8 BFFAFFFF   call crackme6.00182130                   ;  scanf  输入123456
00182671  |.  83C4 08       add esp,0x8
00182674      E8 A7F3FFFF   call crackme6.00181A20                   ;  禁止鼠标键盘
这个函数跟进去会调用BlockInput函数禁止鼠标,来阻止下断点之类,所以nop掉,不让其调用。
接下来,调用了四次xxxxxx600函数 应该是base64,没发现des的特征,观察参数的变化。
001826B0  |.  50            push eax
001826B1  |.  8D8D 3CFFFFFF lea ecx,[local.49]
001826B7  |.  51            push ecx
001826B8  |.  C745 FC 01000>mov [local.1],0x1
001826BF  |.  E8 3CEFFFFF   call crackme6.00181600    ;参数1为返回数据的地址


由123456得到的返回值由上,不是个字符串,继续试了试下面的三个函数结果返回的数据为0,然后得到最后的0数据用MD5加密,得到的数据就是定值,无论输入什么都是0的MD5值,这样password就失去了作用,显然password输入有问题。用iDA分析该函数也比较麻烦,最后还是将123456在网上用base64工具编码解码试试,结果发现编码的值显然不是上面这个,不过解码后都不是正常的字符串,若是解码,那就要解码4次,于是用1在网上编码四次得到VkZaRk9WQlJQVDA9,作为password输入。这次调用一次后得到的字符串正常。
001826C4  |.  50            push eax
001826C5  |.  8D95 CCFEFFFF lea edx,[local.77]
001826CB  |.  52            push edx
001826CC  |.  C645 FC 02    mov byte ptr ss:[ebp-0x4],0x2
001826D0  |.  E8 2BEFFFFF   call crackme6.00181600
001826D5  |.  50            push eax
001826D6  |.  8D85 20FFFFFF lea eax,[local.56]
001826DC  |.  50            push eax
001826DD  |.  C645 FC 03    mov byte ptr ss:[ebp-0x4],0x3
001826E1  |.  E8 1AEFFFFF   call crackme6.00181600
001826E6  |.  50            push eax
001826E7  |.  8D8D E8FEFFFF lea ecx,[local.70]
001826ED  |.  51            push ecx
001826EE  |.  C645 FC 04    mov byte ptr ss:[ebp-0x4],0x4
001826F2  |.  E8 09EFFFFF   call crackme6.00181600
001826F7  |.  83C4 20       add esp,0x20
四次调用完后,字符串依次为VFZFOVBRPT0=,TVE9PQ=,MQ==,1,说明此函数就是base64解码函数。
 
一直往下会来到类似name处理的代码:
0009280C  |.  2BC6          sub eax,esi
0009280E  |.  50            push eax
0009280F  |.  8D95 74FFFFFF lea edx,[local.35]
00092815  |.  52            push edx
00092816  |.  E8 E5ECFFFF   call crackme6.00091500                   ;  密码长度1
0009281B  |.  8D85 04FFFFFF lea eax,[local.63]
00092821  |.  50            push eax
00092822  |.  E8 E90A0000   call crackme6.00093310            ;MD5加密1的MD5值为:c4ca4238a0b923820dcc509a6f75849b
00092827  |.  83C4 20       add esp,0x20
0009282A  |.  8BC8          mov ecx,eax
0009282C  |.  C745 FC 05000>mov [local.1],0x5
00092833  |.  E8 98FCFFFF   call crackme6.000924D0    ;去MD5值中的某几位key2=a0b92382
00092838  |.  3978 14       cmp dword ptr ds:[eax+0x14],edi
0009283B  |.  72 02         jb Xcrackme6.0009283F
0009283D  |.  8B00          mov eax,dword ptr ds:[eax]
0009283F  |>  50            push eax                                 ; |src
00092840  |.  8D4D D8       lea ecx,[local.10]                       ; |
00092843  |.  51            push ecx                                 ; |dest
00092844  |.  FF15 D8500900 call dword ptr ds:[<&MSVCR100.strncpy>]  ; \strncpy

接着到最后:
000928AD  |.  889D 04FFFFFF mov byte ptr ss:[ebp-0xFC],bl
000928B3  |.  885D E0       mov byte ptr ss:[ebp-0x20],bl
000928B6  |.  E8 35FAFFFF   call crackme6.000922F0                   ;  跟进最后一个函数,肯定会去判断参数就是key1 key2
000928BB  |.  83C4 08       add esp,0x8
000928BE  |.  33C0          xor eax,eax

分别是由name和password得到的,最后这个函数会进行验算,跟进,用IDA看了下
 
很明显后半段有个分支,两种不同的结果,最终都会打印,那就有个正确一个错误,这里测试明显是错的,所以OD这里设断后,改标志位,执行相反的流程,结果如下,没有Wrong而是Congratulation

还是好好分析下验证算法,函数开始时申请了内存。然后有两个关键的函数调用,如下:
00212343  |> \6A 00         push 0x0
00212345  |.  56            push esi
00212346  |.  8BCF          mov ecx,edi
00212348  |.  C745 FC FFFFF>mov [local.1],-0x1
0021234F  |.  E8 8C150000   call crackme6.002138E0                   ;  变换key1
先根据参数的变化判断一下,参数有esi为key1,还有edi放的是上面申请的内存地址,在数据窗口监视,调用过后的结果为
 
全是0和1,有点分组密码的特征,那就可能是上面说的des,那key1有可能为密钥,key2可能就是文本啦,在网上查下,

结果如上,
00212354  |.  6A 00         push 0x0
00212356  |.  53            push ebx
00212357  |.  8BCF          mov ecx,edi
00212359  |.  E8 B2190000   call crackme6.00213D10                   ;  变换key2  des加密
第二个函数参数为ebx即key2,和edi即申请的内存,还是观察返回结果,用IDA分析和结合后面的调用结果返回在[EDI+0x600]处

得到的结果仍是二进制,用计算器将第一行转换为十六进制发现为464e,刚好和网上的计算一样,说明就是des加密,key1密钥,key2文本。
接着往下看:
0021235E  |.  A1 0C532100   mov eax,dword ptr ds:[0x21530C]
00212363  |.  8B0D 10532100 mov ecx,dword ptr ds:[0x215310]
00212369  |.  8B15 14532100 mov edx,dword ptr ds:[0x215314]
0021236F  |.  8945 D4       mov [local.11],eax
00212372  |.  A1 18532100   mov eax,dword ptr ds:[0x215318]
00212377  |.  894D D8       mov [local.10],ecx
0021237A  |.  8A0D 1C532100 mov cl,byte ptr ds:[0x21531C]
00212380  |.  8945 E0       mov [local.8],eax
00212383  |.  8D45 D4       lea eax,[local.11]
00212386  |.  8955 DC       mov [local.9],edx
00212389  |.  884D E4       mov byte ptr ss:[ebp-0x1C],cl
0021238C  |.  8D50 01       lea edx,dword ptr ds:[eax+0x1]
0021238F  |.  90            nop
00212390  |>  8A08          /mov cl,byte ptr ds:[eax]
00212392  |.  40            |inc eax
00212393  |.  84C9          |test cl,cl
00212395  |.^ 75 F9         \jnz Xcrackme6.00212390                  ;  长度
00212397  |.  2BC2          sub eax,edx
00212399  |.  8BC8          mov ecx,eax
0021239B  |.  33C0          xor eax,eax
0021239D  |.  85C9          test ecx,ecx
0021239F  |.  7E 15         jle Xcrackme6.002123B6
002123A1  |>  8A5405 D4     /mov dl,byte ptr ss:[ebp+eax-0x2C]
002123A5  |.  80F2 03       |xor dl,0x3
002123A8  |.  885405 C0     |mov byte ptr ss:[ebp+eax-0x40],dl
002123AC  |.  C64405 C1 00  |mov byte ptr ss:[ebp+eax-0x3F],0x0
002123B1  |.  40            |inc eax
002123B2  |.  3BC1          |cmp eax,ecx
002123B4  |.^ 7C EB         \jl Xcrackme6.002123A1
002123B6  |>  8BCF          mov ecx,edi
这段代码读取内存固定信息B13:5GG0EF47A7F,经过与3异或后为A2096DD3FE74B4EC,
002123B6  |> \8BCF          mov ecx,edi
002123B8  |.  8D75 C0       lea esi,[local.16]
002123BB  |.  E8 F0140000   call crackme6.002138B0                   ;  关键生成,就是将des生成的上面的二进制转换为十六进制464E765999867BBF
接下来就是直接比对,就是上面爆破的地方。相等就成功。
所以,要将key1作为密钥加密key2得到A2096DD3FE74B4EC才行。
题目要求输入XDCSC2012作为name,同时得到的key1为d4021e38
那么这个key1就可以作为密钥解密A2096DD3FE74B4EC
在网上解得为8018faaf,这是password经过MD5加密后的一部分,写程序取password从一个字符开始,字符为字母数字,一次MD5加密,取5到8个字节与8018faaf相等成功。
BOOL MD5Hash() 
{
MD5_CTX context;
long i;
int j,k,m;
char szName[MAXINPUTLEN]={"QWERTYUIOPASDFGHJKLZXCVBNM1234567890qwertyuiopasdfghjklzxcvbnm"};
char szHash[MAXINPUTLEN]={0};
char szBuffer[MAXINPUTLEN]={0};
char  duan[3]={0};
char  buff[] = {"8018FAAF"};
for (i=1;i<=2;i++)
{
for (j=0;j<62;j++)
{
duan[0] = szName[j];
if (i==1)
{
MD5Init(&context);
MD5Update(&context, duan, i);
MD5Final(szHash, &context);
for(k=0; k < 16; k++)   // 将szHash[]中的16进制转换成字符形式显示
wsprintf(&szBuffer[k*2], "%02X", *(byte*)(szHash+k));
if (strncmp(buff,(szBuffer+8),sizeof(buff)-1) == 0)
{
printf("%s\n",duan);
return;
}
}
if (i==2)
{
for (m=0;m<62;m++)
{
duan[1] = szName[m];
MD5Init(&context);
MD5Update(&context, duan, i);
MD5Final(szHash, &context);
for(k=0; k < 16; k++)   // 将szHash[]中的16进制转换成字符形式显示
wsprintf(&szBuffer[k*2], "%02X", *(byte*)(szHash+k));
if (strncmp(buff,(szBuffer+8),8) == 0)
{
printf("%s\n",duan);
return;
}
}
}
}
return TRUE;
}

则password为VkdwT1VsQlJQVDA9
总结:
该程序用遍历进程,查看窗口反调试,查看BingDebugged反调试,并禁止鼠标反调试,创建子线程验证,主线程检测反调试,name用MD5加密取部分作为des的密钥,password进行4四次base64解码后得到的字符串用MD5加密取部分作为des的文本,des加密后的得到的字符串与固定字符串比较,相等则成功,没有调用一般的比较函数来防止跟踪,还是比较有意思的,爆破简单,分析算法对我来说还是有点麻烦。

0 0