逆向工程初步160个crackme-------3
阅读原文时间:2023年07月09日阅读:2

这个Crackme3 涉及到浮点指令以及浮点数的存储与运算,我没学习过浮点指令,不得不从网上恶补了1个小时,一边看汇编指令一边百度其指令含义。 回头得好好补补这方面的知识了,太菜了!

我大致了解了一下浮点数运算的一些知识:

计算机中浮点数运算是由FPU进行处理的,现在FPU都集成在CPU中。FPU和CPU一样有自己独立的寄存器。FUP有8个80位的通用寄存器(这8个通用寄存器组成一个首尾相连的栈),1个状态寄存器,1个标志寄存器和1个控制寄存器

8个通用的寄存器为 st(0) - st(7),他们用来参与浮点数的相关运算。运算完所产生的的结果反映在状态和标志寄存器中(其每个位都有每个位独立的含义)。

下面我们就开始分析这个程序!

运行一下这个程序,程序首先会弹出一个小窗口

显示几秒后又弹出一个对话框,让输入用户名和序列号。(其还有个要求是把一开始弹出的窗口去除,这个我还没有实现,哈哈我还太菜!)

我们输入错误后其会弹出一个消息框,提示:输入错误让再次尝试!

我们熟悉完程序后开始正式分析程序

  1. 我们先用PEID打开程序查看其基本信息。

    发现是用VB写的32位程序,而且无壳!

  2. 然后我们打开程序并运行此程序,随便输入用户名和序列号后先不要点击确定。因为我们输入的是错误的序列号,所以我们点击确定后其会弹出消息框提示失败!此程序使用VB写的,VB一般使用弹出消息框的API函数为rtcMsgBox(),我们在此函数处下断点后点击确定按钮程序会停在此函数处。

    然后我们在取消此处断点后,在栈窗口中栈顶位置右击鼠标,点击反汇编窗口跟随,反汇编窗口就会显示其调用处的代码。然后我们在此函数调用处下断点运行程序让程序停在其调用处。

    我们发现在其调用处上面有序列号错误的提示信息,我们继续往上看又发现的成功的提示信息,这应该就是序列号是否正确的判断处!

    往上寻找我们发现关键判断处!

    00408662 . 894D 9C mov dword ptr ss:[ebp-0x64],ecx
    00408665 . 66:85F6 test si,si ; 关键判断处
    00408668 . 8945 94 mov dword ptr ss:[ebp-0x6C],eax
    0040866B . 894D AC mov dword ptr ss:[ebp-0x54],ecx
    0040866E . 8945 A4 mov dword ptr ss:[ebp-0x5C],eax
    00408671 . 894D BC mov dword ptr ss:[ebp-0x44],ecx
    00408674 . 8945 B4 mov dword ptr ss:[ebp-0x4C],eax
    00408677 . 74 62 je XAfKayAs_.004086DB ; 爆破只需把此处用nop填充即可
    00408679 . 8B35 14B14000 mov esi,dword ptr ds:[<&MSVBVM50._vbaSt>; MSVBVM50.__vbaStrCat 0040867F . 68 C06F4000 push AfKayAs.00406FC0 ; UNICODE "You Get It"
    00408684 . 68 DC6F4000 push AfKayAs_.00406FDC ; /String = "
    "
    00408689 . FFD6 call esi ; __vbaStrCat
    0040868B . 8BD0 mov edx,eax
    0040868D . 8D4D E8 lea ecx,dword ptr ss:[ebp-0x18]
    00408690 . FF15 94B14000 call dword ptr ds:[<&MSVBVM50._vbaStrMo>; MSVBVM50.__vbaStrMove 00408696 . 50 push eax 00408697 . 68 E86F4000 push AfKayAs.00406FE8 ; UNICODE "KeyGen It Now"
    0040869C . FFD6 call esi
    0040869E . 8945 CC mov dword ptr ss:[ebp-0x34],eax

接着我们需要往上追码:分析其判断处理算法。

004085D8   .  8B4D E4       mov ecx,dword ptr ss:[ebp-0x1C]
004085DB   .  DD9D 1CFFFFFF fstp qword ptr ss:[ebp-0xE4]
004085E1   .  51            push ecx
004085E2   .  FF15 74B14000 call dword ptr ds:[<&MSVBVM50.__vbaR8Str>
004085E8   .  833D 00904000>cmp dword ptr ds:[0x409000],0x0
004085EF   .  75 08         jnz XAfKayAs_.004085F9
004085F1   .  DCBD 1CFFFFFF fdivr qword ptr ss:[ebp-0xE4]            ;  让我们输入的序列号与真正的序列号相除,结果为1则相等
004085F7   .  EB 11         jmp XAfKayAs_.0040860A
004085F9   >  FFB5 20FFFFFF push dword ptr ss:[ebp-0xE0]
004085FF   .  FFB5 1CFFFFFF push dword ptr ss:[ebp-0xE4]
00408605   .  E8 888AFFFF   call <jmp.&MSVBVM50._adj_fdivr_m64>
0040860A   >  DFE0          fstsw ax
0040860C   .  A8 0D         test al,0xD
0040860E   .  0F85 AB010000 jnz AfKayAs_.004087BF
00408614   .  FF15 34B14000 call dword ptr ds:[<&MSVBVM50.__vbaFpR8>>
0040861A   .  DC1D 28104000 fcomp qword ptr ds:[0x401028]            ;  if( st(0) == [0x401028])   CR3状态位才为1
00408620   .  DFE0          fstsw ax                                 ;  将状态字保存到ax中
00408622   .  F6C4 40       test ah,0x40                             ;  ah的第6位必须为1才能使esi不为0,其第6位刚好是CR3状态位
00408625   .  74 07         je XAfKayAs_.0040862E
00408627   .  BE 01000000   mov esi,0x1
0040862C   .  EB 02         jmp XAfKayAs_.00408630
0040862E   >  33F6          xor esi,esi
00408630   >  8D55 E4       lea edx,dword ptr ss:[ebp-0x1C]
00408633   .  8D45 E8       lea eax,dword ptr ss:[ebp-0x18]
00408636   .  52            push edx
00408637   .  50            push eax
00408638   .  6A 02         push 0x2
0040863A   .  FF15 80B14000 call dword ptr ds:[<&MSVBVM50.__vbaFreeS>
00408640   .  83C4 0C       add esp,0xC
00408643   .  8D4D D8       lea ecx,dword ptr ss:[ebp-0x28]
00408646   .  8D55 DC       lea edx,dword ptr ss:[ebp-0x24]
00408649   .  51            push ecx
0040864A   .  52            push edx
0040864B   .  6A 02         push 0x2
0040864D   .  FF15 08B14000 call dword ptr ds:[<&MSVBVM50.__vbaFreeO>
00408653   .  F7DE          neg esi                                  ;  esi不能为0
00408655   .  83C4 0C       add esp,0xC
00408658   .  B9 04000280   mov ecx,0x80020004
0040865D   .  B8 0A000000   mov eax,0xA
00408662   .  894D 9C       mov dword ptr ss:[ebp-0x64],ecx
00408665   .  66:85F6       test si,si                               ;  关键判断处,要想成功就得esi为0
00408668   .  8945 94       mov dword ptr ss:[ebp-0x6C],eax
0040866B   .  894D AC       mov dword ptr ss:[ebp-0x54],ecx
0040866E   .  8945 A4       mov dword ptr ss:[ebp-0x5C],eax
00408671   .  894D BC       mov dword ptr ss:[ebp-0x44],ecx
00408674   .  8945 B4       mov dword ptr ss:[ebp-0x4C],eax
00408677   .  74 62         je XAfKayAs_.004086DB                    ;  爆破只需把此处用nop填充即可
00408679   .  8B35 14B14000 mov esi,dword ptr ds:[<&MSVBVM50.__vbaSt>
0040867F   .  68 C06F4000   push AfKayAs_.00406FC0                   ;  UNICODE "You Get It"
00408684   .  68 DC6F4000   push AfKayAs_.00406FDC                   ; /String = "
"
00408689   .  FFD6          call esi                                 ; \__vbaStrCat

只有当我们输入的序列号的值等于 st(0)时才能成功,而st(0)中存储的是正确的序列号的浮点数形式。 我们接下来需要分析此序列号是如何产生的。

我们继续往上分析发现从函数头部到判断转移处共有四处代码块参与序列号的生成。

第一处代码块:

004081E9   > \8B95 50FFFFFF mov edx,dword ptr ss:[ebp-0xB0]
004081EF   .  8B45 E4       mov eax,dword ptr ss:[ebp-0x1C]
004081F2   .  50            push eax                                         ; /
004081F3   .  8B1A          mov ebx,dword ptr ds:[edx]                       ; |
004081F5   .  FF15 F8B04000 call dword ptr ds:[<&MSVBVM50.__vbaLenBstr>]     ; \
004081FB   .  8BF8          mov edi,eax                                      ;  edi =  用户名的长度
004081FD   .  8B4D E8       mov ecx,dword ptr ss:[ebp-0x18]
00408200   .  69FF 385B0100 imul edi,edi,0x15B38                             ;  edi * 0x15B38
00408206   .  51            push ecx                                         ; /
00408207   .  0F80 B7050000 jo AfKayAs_.004087C4                             ; |
0040820D   .  FF15 0CB14000 call dword ptr ds:[<&MSVBVM50.#516>]             ; \
00408213   .  0FBFD0        movsx edx,ax                                     ;  edx = 用户名的第一个字符
00408216   .  03FA          add edi,edx                                      ;  edi = edi + edx
00408218   .  0F80 A6050000 jo AfKayAs_.004087C4
0040821E   .  57            push edi                                         ;  把edx转化为字符串
0040821F   .  FF15 F4B04000 call dword ptr ds:[<&MSVBVM50.__vbaStrI4>]

第二处代码块:

004082E6   .  52            push edx
004082E7   .  8B19          mov ebx,dword ptr ds:[ecx]
004082E9   .  FF15 74B14000 call dword ptr ds:[<&MSVBVM50.__vbaR8Str>]       ;  st(0)  = 刚才计算产生的字符串的浮点形式
004082EF   .  D905 08104000 fld dword ptr ds:[0x401008]                      ;  st(0)  =  10.0   栈中各个数据都往下压一层([0x401008] 的值为10.0)
004082F5   .  833D 00904000>cmp dword ptr ds:[0x409000],0x0
004082FC   .  75 08         jnz XAfKayAs_.00408306
004082FE   .  D835 0C104000 fdiv dword ptr ds:[0x40100C]                     ;  st(0) = st(0) / 5.0    ([0x40100c] 的值为5.0)
00408304   .  EB 0B         jmp XAfKayAs_.00408311
00408306   >  FF35 0C104000 push dword ptr ds:[0x40100C]
0040830C   .  E8 578DFFFF   call <jmp.&MSVBVM50._adj_fdiv_m32>
00408311   >  83EC 08       sub esp,0x8
00408314   .  DFE0          fstsw ax
00408316   .  A8 0D         test al,0xD
00408318   .  0F85 A1040000 jnz AfKayAs_.004087BF
0040831E   .  DEC1          faddp st(1),st                                   ;  st(0) = st(1) + s(0) 同时 st(0)出栈
00408320   .  DFE0          fstsw ax
00408322   .  A8 0D         test al,0xD
00408324   .  0F85 95040000 jnz AfKayAs_.004087BF
0040832A   .  DD1C24        fstp qword ptr ss:[esp]                          ;  把st(0)的数据保存到[esp],在把浮点栈出栈
0040832D   .  FF15 48B14000 call dword ptr ds:[<&MSVBVM50.__vbaStrR8>]
00408333   .  8BD0          mov edx,eax

第三处代码块:

004083F3   .  8B19          mov ebx,dword ptr ds:[ecx]
004083F5   .  FF15 74B14000 call dword ptr ds:[<&MSVBVM50.__vbaR8Str>]
004083FB   .  DC0D 10104000 fmul qword ptr ds:[0x401010]                     ;  st(0) = st(0) * 3.0      ([0x401010]处值为3.0)
00408401   .  83EC 08       sub esp,0x8
00408404   .  DC25 18104000 fsub qword ptr ds:[0x401018]
0040840A   .  DFE0          fstsw ax                                         ;  st(0) = st(0) - 2.0      ([0x401018]处的值为2.0)

第四处代码块:

004084DD   .  8B19          mov ebx,dword ptr ds:[ecx]
004084DF   .  FF15 74B14000 call dword ptr ds:[<&MSVBVM50.__vbaR8Str>]
004084E5   .  DC25 20104000 fsub qword ptr ds:[0x401020]                     ;  st(0) = st(0) + 15.0

总结st(0)的值产生的算法:

(用户名的长度 * 0x15B38 + 用户名的第一个字符)* 3 + 13

算法并不复杂,主要是这些算法都是依靠浮点指令和浮点数的运算以及浮点寄存器完成的,所以 没接触过浮点数指令的不容易分析此程序。(有时间要去补点浮点运算的知识,还有那个窗口还没有去除。害!菜的不行看来汇编知识还是不扎实,还得多扣扣汇编。)