文章内容包含:一、爆破程序,二、找出算法
一:爆破程序
. 这个程序是一个VB的程序,以前并没有摸过除C++之外的程序。程序载入时,下BP MessageBoxA和MessageBoxW都无法断下弹窗。
. 临时看下《加密解密三》VB程序得知VB的显示对话框函数为:rtcMsgBox。吾爱的OD插件中有VB函数断点,点击下即可下rtcMsgBox断点。然后输入账户tqdn密码1234点击控件OK让程序跑起来。

. 程序跑起来后断在了MSVBVM50的动态链接库中。这里应该就是rtcMsgBox函数范围内。直接F8走出这个函数。F8往下的过程中会弹出消息框显示“You Get Wrong Try Again”估摸着就是错误的消息吧。
. 走出函数后就返回到下面
004025E1 . 51 push ecx 004025E2 . 52 push edx 004025E3 . EB 56 jmp XAfkayas_.0040263B ; 跳过了错误的显示 004025E5 > 68 C81B4000 push Afkayas_.00401BC8 ; UNICODE "You Get Wrong" 004025EA . 68 9C1B4000 push Afkayas_.00401B9C ; ASCII "\r" 004025EF . FFD7 call edi 004025F1 . 8BD0 mov edx,eax 004025F3 . 8D4D E8 lea ecx,dword ptr ss:[ebp-0x18] 004025F6 . FFD3 call ebx 004025F8 . 50 push eax 004025F9 . 68 E81B4000 push Afkayas_.00401BE8 ; UNICODE "Try Again" 004025FE . FFD7 call edi 00402600 . 8945 CC mov dword ptr ss:[ebp-0x34],eax 00402603 . 8D45 94 lea eax,dword ptr ss:[ebp-0x6C] 00402606 . 8D4D A4 lea ecx,dword ptr ss:[ebp-0x5C] 00402609 . 50 push eax 0040260A . 8D55 B4 lea edx,dword ptr ss:[ebp-0x4C] 0040260D . 51 push ecx 0040260E . 52 push edx 0040260F . 8D45 C4 lea eax,dword ptr ss:[ebp-0x3C] 00402612 . 6A 00 push 0x0 00402614 . 50 push eax 00402615 . C745 C4 08000>mov dword ptr ss:[ebp-0x3C],0x8 0040261C . FF15 10414000 call dword ptr ds:[<&MSVBVM50.#595>] ; 显示错误信息弹框的函数 00402622 . 8D4D E8 lea ecx,dword ptr ss:[ebp-0x18] ; 当前EIP的位置
. 在当前EIP处网上看,离最近的是一个JMP指令,这条指令在OD上点上去是直接跳过了这个显示错误信息的函数。那么在往上面找找。有没有什么跳转指令。
00402569 . 83C4 0C add esp,0xC 0040256C . B9 04000280 mov ecx,0x80020004 00402571 . B8 0A000000 mov eax,0xA 00402576 . 894D 9C mov dword ptr ss:[ebp-0x64],ecx 00402579 . 66:85F6 test si,si ; 比较SI是否为0 0040257C . 8945 94 mov dword ptr ss:[ebp-0x6C],eax 0040257F . 894D AC mov dword ptr ss:[ebp-0x54],ecx 00402582 . 8945 A4 mov dword ptr ss:[ebp-0x5C],eax 00402585 . 894D BC mov dword ptr ss:[ebp-0x44],ecx 00402588 . 8945 B4 mov dword ptr ss:[ebp-0x4C],eax 0040258B . 74 58 je XAfkayas_.004025E5 ; 如果SI等于0那么跳转到错误的显示 0040258D . 68 801B4000 push Afkayas_.00401B80 ; UNICODE "You Get It" 00402592 . 68 9C1B4000 push Afkayas_.00401B9C ; ASCII "\r" 00402597 . FFD7 call edi 00402599 . 8BD0 mov edx,eax 0040259B . 8D4D E8 lea ecx,dword ptr ss:[ebp-0x18] 0040259E . FFD3 call ebx 004025A0 . 50 push eax 004025A1 . 68 A81B4000 push Afkayas_.00401BA8 ; UNICODE "KeyGen It Now" 004025A6 . FFD7 call edi 004025A8 . 8D4D 94 lea ecx,dword ptr ss:[ebp-0x6C] 004025AB . 8945 CC mov dword ptr ss:[ebp-0x34],eax 004025AE . 8D55 A4 lea edx,dword ptr ss:[ebp-0x5C] 004025B1 . 51 push ecx 004025B2 . 8D45 B4 lea eax,dword ptr ss:[ebp-0x4C] 004025B5 . 52 push edx 004025B6 . 50 push eax 004025B7 . 8D4D C4 lea ecx,dword ptr ss:[ebp-0x3C] 004025BA . 6A 00 push 0x0 004025BC . 51 push ecx 004025BD . C745 C4 08000>mov dword ptr ss:[ebp-0x3C],0x8 004025C4 . FF15 10414000 call dword ptr ds:[<&MSVBVM50.#595>>; msvbvm50.rtcMsgBox 004025CA . 8D4D E8 lea ecx,dword ptr ss:[ebp-0x18] 004025CD . FF15 80414000 call dword ptr ds:[<&MSVBVM50.__vba>; msvbvm50.__vbaFreeStr 004025D3 . 8D55 94 lea edx,dword ptr ss:[ebp-0x6C] 004025D6 . 8D45 A4 lea eax,dword ptr ss:[ebp-0x5C] 004025D9 . 52 push edx 004025DA . 8D4D B4 lea ecx,dword ptr ss:[ebp-0x4C] 004025DD . 50 push eax 004025DE . 8D55 C4 lea edx,dword ptr ss:[ebp-0x3C] 004025E1 . 51 push ecx 004025E2 . 52 push edx 004025E3 . EB 56 jmp XAfkayas_.0040263B ; 跳过了错误的显示 004025E5 > 68 C81B4000 push Afkayas_.00401BC8 ; UNICODE "You Get Wrong" 004025EA . 68 9C1B4000 push Afkayas_.00401B9C ; ASCII "\r" 004025EF . FFD7 call edi 004025F1 . 8BD0 mov edx,eax 004025F3 . 8D4D E8 lea ecx,dword ptr ss:[ebp-0x18]
. 往上找找到了一个靠谱的跳转点:
. 0040258B je XAfkayas_.004025E5 ; 如果SI等于0那么跳转到错误的显示
. 尝试把此处的JE修改成nop。然后跑程序试试~~ 爆破成功!

二:找出算法
. 首先想找算法那么先应该在爆破点下断点,并且跑到这里来。
00402561 . 6A 02 push 0x2 00402563 . FF15 F4404000 call dword ptr ds:[<&MSVBVM50.__vba>; msvbvm50.__vbaFreeObjList 00402569 . 83C4 0C add esp,0xC 0040256C . B9 04000280 mov ecx,0x80020004 00402571 . B8 0A000000 mov eax,0xA 00402576 . 894D 9C mov dword ptr ss:[ebp-0x64],ecx 00402579 . 66:85F6 test si,si ; 比较SI是否为0 0040257C . 8945 94 mov dword ptr ss:[ebp-0x6C],eax 0040257F . 894D AC mov dword ptr ss:[ebp-0x54],ecx 00402582 . 8945 A4 mov dword ptr ss:[ebp-0x5C],eax 00402585 . 894D BC mov dword ptr ss:[ebp-0x44],ecx 00402588 . 8945 B4 mov dword ptr ss:[ebp-0x4C],eax 0040258B . 74 58 je XAfkayas_.004025E5 ; 如果SI等于0那么跳转到错误的显示 0040258D . 68 801B4000 push Afkayas_.00401B80 ; UNICODE "You Get It" 00402592 . 68 9C1B4000 push Afkayas_.00401B9C ; ASCII "\r" 00402597 . FFD7 call edi
. JE的跳转是由TEST SI, SI来控制,所以我们应当找到那里给SI赋值的指令。
0040252D . 8D4D E0 lea ecx,dword ptr ss:[ebp-0x20] 00402530 . FFD3 call ebx ; <&MSVBVM50.__vbaStrMove> 00402532 . 50 push eax 00402533 . FF15 28414000 call dword ptr ds:[<&MSVBVM50.__vba>; msvbvm50.__vbaStrCmp 00402539 . 8BF0 mov esi,eax ; vbaStrCmp 字符串比较函数的返回值给了ESI 0040253B . 8D55 E0 lea edx,dword ptr ss:[ebp-0x20] 0040253E . F7DE neg esi 00402540 . 8D45 E8 lea eax,dword ptr ss:[ebp-0x18] 00402543 . 52 push edx 00402544 . 1BF6 sbb esi,esi 00402546 . 8D4D E4 lea ecx,dword ptr ss:[ebp-0x1C] 00402549 . 50 push eax 0040254A . 46 inc esi 0040254B . 51 push ecx 0040254C . 6A 03 push 0x3 0040254E . F7DE neg esi 00402550 . FF15 5C414000 call dword ptr ds:[<&MSVBVM50.__vba>; msvbvm50.__vbaFreeStrList 00402556 . 83C4 10 add esp,0x10 00402559 . 8D55 D8 lea edx,dword ptr ss:[ebp-0x28] 0040255C . 8D45 DC lea eax,dword ptr ss:[ebp-0x24] 0040255F . 52 push edx 00402560 . 50 push eax 00402561 . 6A 02 push 0x2 00402563 . FF15 F4404000 call dword ptr ds:[<&MSVBVM50.__vba>; msvbvm50.__vbaFreeObjList 00402569 . 83C4 0C add esp,0xC 0040256C . B9 04000280 mov ecx,0x80020004 00402571 . B8 0A000000 mov eax,0xA 00402576 . 894D 9C mov dword ptr ss:[ebp-0x64],ecx 00402579 . 66:85F6 test si,si ; 比较SI是否为0 0040257C . 8945 94 mov dword ptr ss:[ebp-0x6C],eax 0040257F . 894D AC mov dword ptr ss:[ebp-0x54],ecx
. 从 TEST SI, SI 处往上找,在很近的地方vbaStrCmp 字符串比较函数的返回值给了ESI。我们在vbaStrCmp 处下断点,OD跑过来看看比较的是什么内容。观察栈中可以看到函数直接是以明文方式作比较,我输入的密码1234和程序计算出来的KEY值。

. 我们往上看看反汇编代码
004024E5 . 8D45 D8 lea eax,dword ptr ss:[ebp-0x28] 004024E8 . 50 push eax 004024E9 . FFD3 call ebx 004024EB . 8BF0 mov esi,eax 004024ED . 8D55 E4 lea edx,dword ptr ss:[ebp-0x1C] 004024F0 . 52 push edx 004024F1 . 56 push esi 004024F2 . 8B0E mov ecx,dword ptr ds:[esi] 004024F4 . FF91 A0000000 call dword ptr ds:[ecx+0xA0] 004024FA . 85C0 test eax,eax 004024FC . 7D 12 jge XAfkayas_.00402510 004024FE . 68 A0000000 push 0xA0 00402503 . 68 5C1B4000 push Afkayas_.00401B5C 00402508 . 56 push esi 00402509 . 50 push eax 0040250A . FF15 04414000 call dword ptr ds:[<&MSVBVM50.__vbaHresu>; msvbvm50.__vbaHresultCheckObj 00402510 > 8B45 E8 mov eax,dword ptr ss:[ebp-0x18] 00402513 . 8B4D E4 mov ecx,dword ptr ss:[ebp-0x1C] 00402516 . 8B3D 00414000 mov edi,dword ptr ds:[<&MSVBVM50.__vbaSt>; msvbvm50.__vbaStrCat 0040251C . 50 push eax 0040251D . 68 701B4000 push Afkayas_.00401B70 ; UNICODE "AKA-" 00402522 . 51 push ecx ; /String 00402523 . FFD7 call edi ; \__vbaStrCat 00402525 . 8B1D 70414000 mov ebx,dword ptr ds:[<&MSVBVM50.__vbaSt>; msvbvm50.__vbaStrMove 0040252B . 8BD0 mov edx,eax 0040252D . 8D4D E0 lea ecx,dword ptr ss:[ebp-0x20] 00402530 . FFD3 call ebx ; <&MSVBVM50.__vbaStrMove> 00402532 . 50 push eax 00402533 . FF15 28414000 call dword ptr ds:[<&MSVBVM50.__vbaStrCm>; msvbvm50.__vbaStrCmp 00402539 . 8BF0 mov esi,eax ; vbaStrCmp 字符串比较函数的返回值给了ESI
. vbaStrCmp的第一个参数是由vbaStrMove的返回值给的。观察反汇编
00402525 . 8B1D 70414000 mov ebx,dword ptr ds:[<&MSVBVM50.__vbaSt>; msvbvm50.__vbaStrMove 0040252B . 8BD0 mov edx,eax 0040252D . 8D4D E0 lea ecx,dword ptr ss:[ebp-0x20] 00402530 . FFD3 call ebx ; <&MSVBVM50.__vbaStrMove> 00402532 . 50 push eax 00402533 . FF15 28414000 call dword ptr ds:[<&MSVBVM50.__vbaStrCm>; msvbvm50.__vbaStrCmp 00402539 . 8BF0 mov esi,eax ; vbaStrCmp 字符串比较函数的返回值给了ESI
. 并没有看到vbaStrMove存在PUSH参数的指令。我们在这里下断点进去看看vbaStrMove的实现是什么样子的。
vbaStrMove的实现 7403F8DA > 56 push esi 7403F8DB 57 push edi 7403F8DC 8B01 mov eax,dword ptr ds:[ecx] ; ECX去一个DWORD给EAX 7403F8DE 8BFA mov edi,edx ; EDX的内容给EDI 7403F8E0 8BF1 mov esi,ecx ; ECX的内容给ESI 7403F8E2 85C0 test eax,eax ; 判断ECX 去内容是否为NULL 7403F8E4 75 07 jnz Xmsvbvm50.7403F8ED 7403F8E6 8BC7 mov eax,edi 7403F8E8 893E mov dword ptr ds:[esi],edi ; ECX取内容不等于NULL就将EDI存放至ECX中 7403F8EA 5F pop edi 7403F8EB 5E pop esi 7403F8EC C3 retn 7403F8ED 50 push eax 7403F8EE FF15 88190274 call dword ptr ds:[<&OLEAUT32.#6>] ; oleaut32.SysFreeString 7403F8F4 ^ EB F0 jmp Xmsvbvm50.7403F8E6 ; 否则就直接返回了
. 通过解释反汇编,可以知道vbaStrMove不是使用栈来传参数而是使用了两个寄存器EDX和ECX。结合反汇编,EDX是由vbaStrCat的返回值给与的。ECX由ebp-0x20来的。

. vbaStrCat的两个参数是直接push一个字符串”AKA-”和push了一个ECX,ECX指向了一个字符串“390240”。最开始在爆破点下断点得出来的KEY位“AKA-390240”理算法很近了。我们接下来需要就是找到ECX是从哪来的。但是失败了。我对VB不熟悉,函数这里给出的结果让我很莫名其妙。
. 所以我决定在用最挫的方式重新在爆破点的函数头下断点,然后单步跟一次流程。并且关注函数的返回值和传出参数(lea EXX, ebp-XX,遇见这种考虑是传出参数,直接在十六进制窗口查看当前地址是什么内容,函数走完后修改成了什么数据!)
004023ED . FF90 A0000000 call dword ptr ds:[eax+0xA0] ; 这里获得了账户 004023F3 . 3BC7 cmp eax,edi 004023F5 . 7D 12 jge XAfkayas_.00402409 004023F7 . 68 A0000000 push 0xA0 ; 如果返回值小于0就走这里 004023FC . 68 5C1B4000 push Afkayas_.00401B5C 00402401 . 53 push ebx 00402402 . 50 push eax 00402403 . FF15 04414000 call dword ptr ds:[<&MSVBVM50.__vbaHresultChec>; msvbvm50.__vbaHresultCheckObj 00402409 > 8B95 50FFFFFF mov edx,dword ptr ss:[ebp-0xB0] 0040240F . 8B45 E4 mov eax,dword ptr ss:[ebp-0x1C] ; vbaLenBstr 获取 账户的长度,eax返回长度 00402412 . 50 push eax ; /String 00402413 . 8B1A mov ebx,dword ptr ds:[edx] ; | 00402415 . FF15 E4404000 call dword ptr ds:[<&MSVBVM50.__vbaLenBstr>] ; \__vbaLenBstr 0040241B . 8BF8 mov edi,eax ; 账户长度存储在EDI中 0040241D . 8B4D E8 mov ecx,dword ptr ss:[ebp-0x18] 00402420 . 69FF FB7C0100 imul edi,edi,0x17CFB ; 将账户长度 * 0x17CFB 00402426 . 51 push ecx ; /String 00402427 . 0F80 91020000 jo Afkayas_.004026BE ; |计算出来的结果>=0x80000000 就异常 0040242D . FF15 F8404000 call dword ptr ds:[<&MSVBVM50.#516>] ; \rtcAnsiValueBstr 00402433 . 0FBFD0 movsx edx,ax ; 返回账户的第一个字符代码 00402436 . 03FA add edi,edx ; 返回的字符代码与账户长度 * 0x17CFB相加 00402438 . 0F80 80020000 jo Afkayas_.004026BE ; 结果大于0x80000000就异常 0040243E . 57 push edi ; 这个函数是将I4转换成STR 0040243F . FF15 E0404000 call dword ptr ds:[<&MSVBVM50.__vbaStrI4>] ; msvbvm50.__vbaStrI4 00402445 . 8BD0 mov edx,eax ; 返回值为390240字符串
. 这里得到了字符串为390240,通过第一次失败的跟踪。那么可以知道这个地方是算出后面整数字符串的地方,对于KEY只需要在字符串前拼接一个”AKA-”就好了
. 这个算法很简单,也是和001一样只取了账户的第一个字节。不过这个账户有取账户长度做计算得出的结果。所以一个账户算出来的KEY只能在首字母和账户长度都相等的情况才能正常得出正确的结果!
void Fun(char *ZhangHu) { char szBuff[256]; unsigned long data = strlen(ZhangHu) * 0x17CFB + (unsigned long)ZhangHu[0]; sprintf(szBuff, "AKA-%d", data); printf("%s \r\n", szBuff); }
发表评论