solly 发表于 2019-11-24 12:16

160 个 CrackMe 之 082 - ultraschall 自带微型VM的跟踪和分析

本帖最后由 solly 于 2019-11-27 09:36 编辑

160 个 CrackMe 之 082 - ultraschall 是一个自带了包含10个指令的微型VM的 Crackme 程序,并且其VM部分的代码带有“花指令”干扰。
我们先来看看文件信息:

显示是用汇编语言写的,没有加壳。重新用 “Scan /t” 扫描一下:

这次显示有一个 PE Diminisher v0.1 处理过了,应该就是加了一些“花指令”,我们在后面会看到。


但是这个程序在 Win10 下不能直接运行,会报兼容性问题,如下图所示,Windows10不允许其运行:


这个错误是由于程序的“节”的参数不严谨造成的,是“.rsrc”节的大小超出了文件结尾,在 Windows 9x 下没有问题,在 Win10下则不认可,不过可以修正(本论坛有贴有详细説明,修改原理这里不重复了),修改方法如下面的两图所示:

上图中的 0x00004A00 需要修改,改成 0x00004200 即可,如下图所示:

修改后即可在 Windows 10 下正常运行了。


启动后界面如下:

需要找出其 SN 或写出注册机。


接下来我们用 OD 加载 Crackme,先进行静态分析。

加载后,如上图所示,用汇编写的代码比较精简整洁,结构清晰。
上图中红框中的代码就是 WinMain() 入口函数的代码,直接生成一个基于“对话框”的程序,没有一句多余代码。
由上图可以看到对话框的事件处理回调函数 DlgProc() 的入口为 0x0040103E,也是直接就是在 WinMain() 函数的后面,如上图蓝框所标示的部分。
下图所示就是 DlgProc() 的部分代码:

最前面的代码是处理 WM_SHOWWINDOW 消息,对文本框的输入长度进行了一些限制,防止后面接收字符串时溢出。同时将光标(输入焦点)定位到输入用户名的文本框内。
图中红框中所示代码是发送一个自定义的消息,不过这个消息是用来退出 CrackMe 的,也就是将 WM_CLOSE 消息,转换成自定义的 WM_COMMAND消息,再由 WM_COMMAND消息处理代码来关闭 Crackme。


下面来看看 DlgProc() 中对 WM_COMMAND 处理的代码,如下图所示:


上图中,蓝框中的代码就是处理自定义的 WM_COMMAND 消息的,用来关闭对话框程序。
主要的处理代码在红框中,这就是处理“Register”按钮事件的代码。关键就是一个 call 调用: call 004010FF,这里调用了一个微型 VM 系统。往下看,就可以看到这个调用的代码,如下图所示:

可以看到,这个函数内部的代码很乱,这是由于有“花指令”的干扰,往下拖,看看函数结尾在哪里,如下图所示,在 IAT 的跳转表前面就是其结尾:


下面先去除“花指令”,在 OD 的“插件”菜单中,选择“5. 花指令去除器”==>“去除花指令”,在弹出的对话框中输入搜索地址和搜索大小,如下图所示:

搜索地址为函数地址:0x004010FF,搜索大小为 0x31F = 0x0040141E - 0x004010FF。再点“确定”就可以了,如下图所示:

去除了 112 条花指令。后面OD代码区的花指令变成了大量的 nop 指令了,如下图所示,就是 VM 的入口,先将Host机器的上下文(寄存器信息)保存。


同时在地址 0x0040110D 处的 NOP 指令,是 VM 机器的执行标志,后面有说明,当这个字节由 0x90 变成 0xCC 时,VM 结束。
静态分析到这里,下面进行动态跟踪,分析注册机制。

我们先在下图所示位置(0x004010CE),下一个“断点”:


然后按 “F9” 运行 CrackMe,显示主界面,并输入注册假码数据,如下图所示:

输入完注册信息,点“Register”按钮,OD 马上断下程序,如下图所示,定位到了我们前面下的断点处:

可以看到,按钮的 ControlID = 0x000003F4,按几次 F8,来到那个 call 004010FF 处,如下图所示,同时在 call 后面下一个断点(防止退出CrackMe):


我们按 F7 进入 call 调用,跟踪 VM 的执行。


一路按 F8 执行,来到如下图所示位置,这个 VM 内的 call dword ptr ,就是解释执行 VM 指令的调用:

其中 al 为操作码,esi 为操作码解释执行程序的入口基址,当前的 cx 为下一条指令的地址,EBX 为第一个操作数,EDX 为第二个操作数,后面再对指令结构详细説明,在 OD 的数据区,就显示了当前正在执行的VM指令。
eax+esi = 0x00403199,这个位置保存的是解释执行当前VM指令的子过程(0x0040128A),如下图所示,在 OD 的数据区就是这些子过程的入口表,共 10 个入口,表示这个小小的 VM 只有 10 条指令。


具体入口代码如下:
;Func ~ Func, 共 10 个指令解释程序入口地址
00403189: 004011DF    00401202    00401224    00401256
00403199: 0040128A    004012E0    00401343    0040136F
004031A9: 00401380    004013C0

VM 执行的第一条指令的 opcode 为 0x10,这是一条从界面取文本数据的指令,其第2个参数就是 ControlID,如下图所示,取到了用户名:

本 VM 机器执行的 VM 程序全部指令序列如下所示:
0040300B10 E0 00 00 00 F0 03 00 00 0B 00   ; Func GetText,取用户名 存于 , "solly", API:User32.GetDlgItemTextA()
0040301610 F5 00 00 00 F1 03 00 00 16 00   ; Func GetText,取序列号(文本) 存于 , "78787878", API:User32.GetDlgItemTextA()
0040302108 DC 00 00 00 DC 00 00 00 21 00   ; Func Sub,计算 = - = 0 - 0 = 0,变量初始化为0
0040302C04 DC 00 00 00 5A 01 00 00 2C 00   ; Func Add,计算 = + = 0 + 0 = 0
0040303720 E0 00 00 00 4E 01 00 00 37 00   ; Func Equ,赋值 = *((dword *)&name) = 0x6C6C6F73("soll"),("olly"),......, 下面Func跳转到这里执行循环。
0040304204 4E 01 00 00 56 01 00 00 42 00   ; Func Add,计算 = + = 0x00 + 0x01 = 0x01, j++(char索引)
0040304D04 4A 01 00 00 DC 00 00 00 4D 00   ; Func Add,计算 = + = 0x00 + 0x6C6C6F73 = 0x6C6C6F73, sum += *((dword *)&name), 循环结束时 sum = 0x5589C233
0040305808 52 01 00 00 56 01 00 00 58 00   ; Func Sub,计算 = - = 0x14 - 0x01 = 0x13, 为循环次数, i--(char索引)
0040306318 52 01 00 00 00 00 2C 00 63 00   ; Func Jnz,不为0跳转 (操作数1 != 0) 结果(IP) == 操作数2:0x002C0000 ==> bswap(0x00002C00) ==> (xchg)0x0000(002C),跳回去循环
0040306E20 66 01 00 00 52 01 00 00 6E 00   ; Func Equ,赋值 = xor_arr = (0xD4E1C094), 下面Func跳转到这里执行循环。
004030790C 4A 01 00 00 DC 00 00 00 79 00   ; Func Xor,计算 = ^ ,sum ^= *((dword *)&xor_arr),循环结束时 sum = 0x061F4FD2
0040308404 52 01 00 00 62 01 00 00 84 00   ; Func Add,计算 = + = 0x00 + 0x04 = 0x04, i++(int索引)
0040308F08 4E 01 00 00 62 01 00 00 8F 00   ; Func Sub,计算 = - = 0x14 - 0x04 = 0x10, j--(int索引)
0040309A18 4E 01 00 00 00 00 63 00 9A 00   ; Func Jnz,不为0跳转 (操作数1 != 0) 结果(IP) == 操作数2:0x00630000 ==> bswap(0x00006300) ==> (xchg)0x0000(0063),跳回去循环
004030A524 7A 01 00 00 F1 03 00 00 A5 00   ; Func GetInt,取序列号(整数) 存于 , API:User32.GetDlgItemInt(), SN = eax == 0x04B23526(78787878)
004030B008 4A 01 00 00 7A 01 00 00 B0 00   ; Func Sub,计算 = - = sum - SN,sum -= SN,结果为0时表示序列号正确
004030BB18 4A 01 00 00 00 00 C6 00 BB 00   ; Func Jnz,不为0跳转 (操作数1(sum) != 0) 结果(IP) == 操作数2:0x00C60000 ==> bswap(0x0000C600) ==> (xchg)0x0000(00C6),跳转去显示注册错误
004030C614 2C 01 00 00 00 00 00 00 D1 00   ; Func Msg,显示注册正确,MsgText == ===> "Registration Code is CORRECT!",API: USER32.MessageBoxExA()
004030D114 0A 01 00 00 00 00 00 00 D1 00   ; Func Msg,显示注册错误,MsgText == ===> "Registration Code is not correct!",API: USER32.MessageBoxExA()
004030DC1C 00 00 00 00 00 00 00 00 00 00   ; Func End,设置程序结束标志, = 0xCC;int3, 设置程序结束标志
可以看出,一共20条指令代码,每个指令占用 11 个字节,具体结构如下所示:
/// 指令结构
struct Instruction {
      char opcode;
      DWORD operand1; /// and result
      DWORD operand2;
      WORD next_ip;
};
VM 伪指令的主要执行流程如下:
1、首先取得用户名和字符串形式的注册码;
2、循环计算用户名中字符的ASCII码的累加和(每次读取4个字符,合并其ASCII码组成一个 DWORD进行累加运算),但索引指针还是一个字节一个字节的移动;
3、计算得到的ASCII码的累加和再循环与5个DWORD常量依次进行异或运算后得到的数值,就是最终的序列号;
4、重新从界面取得无符号整数形式的注册码,与前面计算的序列号进行比较(相减运算),如果相等,则显示序列号正确的提示,否则显示序列号不正确的提示。
5、最后设置结束标志,结束VM主循环地执行,退出VM。

参与异或运算的常量数组如下:
;第二次循环进行异或的操作数,5个整数常量:
0040317194 C0 E1 D4 11 98 1F FF C8 BB FA 91 10 94 1D 78
00403181BC FA 8F 91

以16进制形式表示:0xD4E1C094, 0xFF1F9811, 0x91FABBC8, 0x781D9410, 0x918FFABC;


VM的具体代码如下所示(去除了大量的 nop 指令,所以指令前的地址值不是连贯的了):
004010FF      55               push    ebp
00401100      8BEC               mov   ebp, esp
00401107      60               pushad
00401108      9C               pushfd
0040110B      90               nop
0040110C      90               nop
0040110D      90               nop                                 ;程序开始(nop)/结束(int3)标志存放点
0040110E      90               nop
00401112      BB 0B304000      mov   ebx, 0040300B               ;初始化数据区基址
0040111B      BE 89314000      mov   esi, 00403189               ;初始化指令解释执行函数dispatcher表基址
00401124      33C0               xor   eax, eax                      ;初始化指令指针寄存器IP = 0
00401128      90               nop
00401129      90               nop
0040112A      50               push    eax                           ;开始循环执行VM指令
0040112B      53               push    ebx
00401131      8BF8               mov   edi, eax                      ;IP
00401137      50               push    eax                           ;IP
0040113C      3D FF000000      cmp   eax, 0FF                      ;IP>255
00401145      0F8F 88000000      jg      004011D3                      ;IP大于255则退出VM执行循环
00401153      90               nop
00401154      D7               xlat    byte ptr              ;取指令 AL=opcode, 相当于指令 mov al, byte ptr
00401155      50               push    eax
0040115E      90               nop
0040115F      8B541F 05          mov   edx, dword ptr     ;取操作数2
00401163      90               nop
00401167      59               pop   ecx                           ;指令
0040116C      58               pop   eax                           ;IP
00401172      51               push    ecx                           ;指令
00401176      90               nop
00401177      66:8B4C18 09       mov   cx, word ptr       ;取下一条指令IP
0040117C      90               nop
00401180      8B5C1F 01          mov   ebx, dword ptr     ;取操作数1,结果缓冲区
00401184      58               pop   eax                           ;指令
00401188      90               nop
00401189      FF1430             call    dword ptr          ;执行指令
0040118C      90               nop
00401190      5B               pop   ebx
00401195      58               pop   eax
0040119A      8BC1               mov   eax, ecx                      ;eax ==>下一条指令结构
0040119F      90               nop
004011A0      803D 0D114000 CC   cmp   byte ptr , 0CC      ;int3, 程序是否结束,0xCC表示VM程序结束
004011AB    ^ 0F85 79FFFFFF      jnz   0040112A                      ;没有结束则继续执行下一条指令
004011B1      90               nop
004011B5      C605 0D114000 90   mov   byte ptr , 90         ;改成 nop,可重新开始执行的标志
004011C0      90               nop
004011C1      9D               popfd
004011C6      61               popad
004011CB      C9               leave
004011CC      C2 0400            retn    4                           ;返回,退出VM程序
004011CF      90               nop
004011D0      90               nop
004011D3      8A0418             mov   al, byte ptr       ;IP>255 时跳转到这里,取指令并重新返回循环执行指令
004011DA    ^ E9 76FFFFFF      jmp   00401155
;===============================================================================================================================
004011DF      90               nop                                 ;Func 入口,Equ 指令
004011E3      C605 7A124000 89   mov   byte ptr , 89         ;修改为 Mov 指令
004011EF      68 46124000      push    00401246                      ;ret 返回地址
004011F8      68 56124000      push    00401256                      ;ret 返回地址
00401201      C3               retn                                  ;jmp 00401256
;===============================================================================================================================
00401202      90               nop                                 ;Func 入口,Add 指令
00401206      C605 7A124000 01   mov   byte ptr , 1          ;修改为 Add 指令
00401211      68 46124000      push    00401246                      ;ret 返回地址
0040121A      68 56124000      push    00401256                      ;ret 返回地址
00401223      C3               retn                                  ;jmp 00401256
;===============================================================================================================================
00401224      90               nop                                 ;Func 入口,Sub 指令
00401228      C605 7A124000 29   mov   byte ptr , 29         ;修改为 Sub 指令,修改运算符
00401233      68 46124000      push    00401246                      ;ret 返回地址
0040123C      68 56124000      push    00401256                      ;ret 返回地址
00401245      C3               retn                                  ;jmp 00401256, call Func
;===============================================================================================================================
00401246      90               nop                                 ;Func_Restore()
0040124A      C605 7A124000 31   mov   byte ptr , 31         ;恢复指令
00401255      C3               retn
;===============================================================================================================================
00401256      90               nop                                 ;Func 入口,Xor 指令,同时兼VM的加法器或EU
0040125A      51               push    ecx
0040125F      81C3 0B304000      add   ebx, 0040300B               ;ebx ===> 操作数1,以及结果
00401269      81C2 0B304000      add   edx, 0040300B               ;edx ===> 操作数2
00401274      8B12               mov   edx, dword ptr           ;取得操作数2的值
0040127A      3113               xor   dword ptr , edx          ;执行操作,这里的指令会是可变的,Mov, Add, Sub, Xor 等
00401280      59               pop   ecx
00401285      C3               retn                                  ;返回 00401246 恢复指令, 由 00401233 处指令压入
;===============================================================================================================================
00401289      90               nop
0040128A      51               push    ecx                           ;Func 入口, GetDlgItemText(),ControlID为参数
00401294      81C3 0B304000      add   ebx, 0040300B               ;ebx 为存放用户名和序列号的buffer地址
004012A7      6A 15            push    15                            ;length
004012AD      53               push    ebx                           ;buffer
004012B2      52               push    edx                           ;ControlID
004012B3      FF35 B8314000      push    dword ptr             ;hWnd
004012BE      68 D5124000      push    004012D5                      ;API调用的返回地址
004012C7      68 3C144000      push    <jmp.&USER32.GetDlgItemTextA>
004012D0      C3               retn                                  ;调用 API
004012D4      90               nop
004012D5      59               pop   ecx                           ;API 返回位置
004012DA      C3               retn
;===============================================================================================================================
004012DF      90               nop
004012E0      51               push    ecx                           ;Func 入口,MessageBoxA()
004012E9      81C3 0B304000      add   ebx, 0040300B               ;MsgText
004012F3      6A 00            push    0
004012F9      6A 00            push    0
004012FF      68 00304000      push    00403000                      ;MsgTitle ==> "CrackME #1"
00401308      53               push    ebx                           ;MsgText
00401312      FF35 B8314000      push    dword ptr             ;hWnd
00401321      68 39134000      push    00401339                      ;API 调用返回地址
0040132A      68 48144000      push    <jmp.&USER32.MessageBoxExA>
00401334      C3               retn                                  ;调用 API
00401338      90               nop
00401339      59               pop   ecx                           ;API 返回位置
0040133E      C3               retn
;===============================================================================================================================
00401343      90               nop                                 ;Func 入口,Jnz 指令
00401348      81C3 0B304000      add   ebx, 0040300B
0040134E      8B1B               mov   ebx, dword ptr
00401350      85DB               test    ebx, ebx                      ;检测条件
00401356      74 12            je      short 0040136A
0040135C      8BCA               mov   ecx, edx                      ; 不等于0,则改变IP,执行跳转
00401362      0FC9               bswap   ecx
00401368      86CD               xchg    ch, cl                        ;IP == cl
0040136A      90               nop                                 ;如果等于0,则直接跳转到此,不改变IP。
0040136E      C3               retn
;===============================================================================================================================
0040136F      90               nop                                 ;Func 入口,End 指令
00401373      C605 0D114000 CC   mov   byte ptr , 0CC      ;int3, 设置程序结束标志
0040137F      C3               retn
;===============================================================================================================================
00401380      90               nop                                 ;Func 入口, Equ& 指令
00401384      51               push    ecx
00401389      81C3 0B304000      add   ebx, 0040300B               ;操作数1
00401393      81C2 0B304000      add   edx, 0040300B               ;操作数2
0040139E      8B12               mov   edx, dword ptr           ;取操作数2的值
004013A4      8B0C1A             mov   ecx, dword ptr
004013AB      890D E7304000      mov   dword ptr , ecx       ;返回结果
004013B5      59               pop   ecx
004013BF      C3               retn
;===============================================================================================================================
004013C0      51               push    ecx                           ;Func 入口,GetDlgItemInt()
004013CA      81C3 0B304000      add   ebx, 0040300B
004013D9      6A 00            push    0
004013DF      6A 00            push    0
004013E5      52               push    edx                           ;ControlID, 操作数2
004013E6      FF35 B8314000      push    dword ptr             ;hWnd
004013F1      68 08144000      push    00401408                      ;API 调用返回地址
004013FA      68 42144000      push    <jmp.&USER32.GetDlgItemInt>
00401403      C3               retn                                  ;调用 API
00401407      90               nop
00401408      90               nop                                 ;API 返回位置
0040140C      8903               mov   dword ptr , eax          ;取得的界面整数数据
00401412      59               pop   ecx
00401417      C3               retn
0040141C      90               nop
0040141D      CC               int3
VM 解释执行程序的分析説明都包含在上面的代码内了。

以第1条指令为例説明指令的解释执行过程:
; 第1条指令
0040300B10 E0 00 00 00 F0 03 00 00 0B 00   ; Func GetText,取用户名 存于 , "solly", API:User32.GetDlgItemTextA()
1、指令的 opcode 为 0x10,operand1 为 0x000000E0,operand2 为 0x000003F0,next_ip 为 0x000B,执行该指令时,将 0x10 解码成微代码函数索引,通过该索引调用执行代码完成指令功能,host_func = opcode + entres_base = opcode + ESI = 0x10 + 0x00403189 = 0x00403199,在 0x00403199 处保存的是这条指令的解释程序(微代码)的入口地址,相当于执行 call 。
2、操作数 operand1 = 0x000000E0,是接收字符串的缓冲区地址,与物理内存的联系是 host_address = operand1 + base_addr = operand1 + ebx = 0x000000E0 + 0x0040300B = 0x004030EB,这个0x004030EB就是实际的物理地址。这个地址是调用 Windows API:GetDlgItemTextA() 的 lpString 参数。
3、操作数 operand2 是一个立即数,表示 ControlID = 0x000003F0,这个数据是调用 Windows API:GetDlgItemTextA() 的 nIDDlgItem 参数 。
4、最后的 next_ip 的计算为 host_ip = next_ip + base_addr = next_ip + ebx = 0x000B + 0x0040300B = 0x00403016,所以下一条指令存贮在 0x00403016 的位置。

通过分析 VM 所执行的伪指令序列(见前面的指令表), 可以看出,用了 20 条伪指令就组成了注册算法程序,并由 VM 解释执行。

通过第1条指令的执行过程解释,我们对其指令有所了解,下面我们可以通过patch方式修改伪指令来通过注册验证(Crackme 不允许这种操作,这里只是演示,注册机源码在后面有)。需要修改的伪指令如下所示,就是一条 Jnz 指令,下面显示的第1条指令:
004030BB18 4A 01 00 00 00 00 C6 00 BB 00   ; Func Jnz,不为0跳转 (操作数1(sum) != 0) 结果(IP) == 操作数2:0x00C60000 ==> bswap(0x0000C600) ==> (xchg)0x0000(00C6),跳转去显示注册错误
004030C614 2C 01 00 00 00 00 00 00 D1 00   ; Func Msg,显示注册正确,MsgText == ===> "Registration Code is CORRECT!",API: USER32.MessageBoxExA()
004030D114 0A 01 00 00 00 00 00 00 D1 00   ; Func Msg,显示注册错误,MsgText == ===> "Registration Code is not correct!",API: USER32.MessageBoxExA()

可以看到,其 opcode = 0x18,操作数operand2 = 0x00C60000,通过VM 的 IP 转换算法,最后会变成 0xC6,这就是 Jnz 执行时,如果条件为 True(不相等),则将 next_ip 改为 0x00C6,而原来的 next_ip = 0x00BB,即条件为 False,则继续执行 0x00BB 处的指令,通过上面的 host_ip 计算方法可知,这两个地址分别对应的指令实际地址如下:
0x00C6 ===> 0x0040300B + 0x00C6 = 0x004030D1,执行显示 "Registration Code is not correct!" 的指令。
0x00BB ===> 0x0040300B + 0x00BB = 0x004030C6,执行显示 "Registration Code is CORRECT!" 的指令。
所以,只要把这条 Jnz 的 operand2 修改为 next_ip 一样就可以通过注册。修改结果如下:
; Patch 后的 Jnz 指令
004030BB 18 4A 01 00 00 00 00 BB 00 BB 00 ; Func Jnz,不为0跳转
这样,不管条件如何,都是跳转到(0x00BB),显示注册码正确的提示了。

另外,需要説明一下,如下图所示:

用户名和注册码的都是21个字节的 char 数组,存贮是连续的,中间没有间隙,所以,在对用户名进行ASCII码求累加和计算时,由于每次都取4个字节,当在进行最后2个字节计算时,就会超出用户名的存贮区域,读取到序列号的最前面2个字节的内容。
如上图所示,在最后两次计算,取得的数据分别是:0x37000000 (00,00,'\0', '7')和 0x38370000(00,'\0','7','8'),所以在进行注册机编写时也要考虑这个问题。

下面是注册机代码,通过两种方式计算注册码,第一种是碰撞测试注册码的前2字节(00~99),测试注册码是否合法,测试00时,效果与第二种方式一致。
第二种方式,是在注册码前加上两个前导 ‘0’,因为前导 0 不影响注册码的大小,可以一次性计算出注册码(第二种算法本论坛中已有说明,参考了“反沉沦”的帖子:https://www.52pojie.cn/forum.php ... &page=5#pid11840041)。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
   
int getSN1(char * name);
int getSN2(char * name);
int testSN1(long sum_name);

/// 伪指令结构
struct Instruction {
      char opcode;
      DWORD operand1; /// and result
      DWORD operand2;
      WORD next_ip;
};

int main(int argc, char** argv) {
      char name;
      printf("Input your name: ");
      gets(name);
         
      getSN1(name);//// 方式1, 碰撞测试
      //getSN2(name);//// 方式2,加双前导 0
         
      return 0;
}

long xor_base[] = {0xD4E1C094, 0xFF1F9811, 0x91FABBC8, 0x781D9410, 0x918FFABC};

int getSN1(char * name) {
    char buffer;
      
    memset(buffer, 0, 48);
      
    /// name: 52pojie.cn
    ///   SN: 1557972636
      
    int n = strlen(name);
    if (n>20) {
      n = 20;
    }
    strncpy(buffer, name, n);
         
    long sum_name = 0;
         
    for(int i=0; i<20; i++) {
      sum_name += * ((long *)&buffer); //// 将char* 当作 long* 取 name,一次取4个字符
    }
         
    int count = testSN1(sum_name);
    if(count == 0) {
      printf("SN: no result.\n");
    }
         
    return 0;
}

int getSN2(char * name) {
    char buffer;
      
    memset(buffer, 0, 48);
      
    /// name: 52pojie.cn
    ///   SN: 001524614812
      
    int n = strlen(name);
    if (n>20) {
            n = 20;
      }
      strncpy(buffer, name, n);

    //// SN处理,SN 前导 0 不影响数值大小
    buffer = '0'; /// CrackMe 中对 name 操作时取到了 SN 最前面两个字符
    buffer = '0'; /// CrackMe 中对 name 操作时取到了 SN 最前面两个字符
         
    long sum = 0;
         
    for(int i=0; i<20; i++) {
      sum += * ((long *)&buffer); //// 将char* 当作 long* 取 name,一次取4个字符
    }

//      long xor_check = 0;
//      for(int i=0; i<5; i++) {
//                xor_check ^= xor_base;
//      }
    long xor_check = 0x53968DE1; //// 5 个整数的 Xor 结果值
    // printf("xor check: %08X\n", xor_check);
    sum ^= xor_check;

    //// SN处理,SN 前导 0 不影响数值大小
    printf("SN: 00%lu\n", (unsigned long)sum);
         
    return 0;
}

/*
* SN 前两位碰撞测试
*/
int testSN1(long sum_name) {
    char sn;
    //long sum0 = 0xE652C233;
    int n = 0;
    long sum0 = sum_name;
    for(int i=9; i>=0; i--) {
      long k = i+0x30;
      long sum1 = (k<<24) + (k<<16);
      for(int j=9; j>=0; j--) {
            long m = j + 0x30;
            long sum2 = (m<<24);
            long sum3 = sum0 + sum1 + sum2;
            long sum = sum3 ^ 0x53968DE1; /// xor_check
            //// 有前导 0 时, 分情况格式化
            if(i==0) {
                if(j==0) {
                  sprintf(sn, "00%lu", (unsigned long)sum); /// 这一种情况与 getSN2() 方式一致
                } else {
                  sprintf(sn, "0%lu", (unsigned long)sum);
                }
            } else {
                //// 没有前导 0 时,直接输出
                sprintf(sn, "%lu", (unsigned long)sum);
            }
            //printf("%c%c: %s\n", k, m, sn);
            if(((sn) == k) && ((sn) ==m)) {
                //printf("%c%c: %s\n", k, m, sn);
                printf("SN: %s\n", sn);
                                 
                n++;
            }
      }
    }
         
    return n;
}
代码由 Dev-C ++ 调试通过。计算结果如下:
Input your name: solly
SN: 0420761554
SN: 00353652690
--------------------------------
Process exited after 4.457 seconds with return value 0
请按任意键继续. . .
再来一个:
Input your name: 52pojie.cn
SN: 1557972636
SN: 01507837596
SN: 001524614812

--------------------------------
Process exited after 1.438 seconds with return value 0
请按任意键继续. . .



返回界面输入上面的注册码,再次点“Register”,CrackMe 提示如下:

表示注册通过,分析完毕!!!


solly 发表于 2020-1-6 16:56

yijian 发表于 2020-1-6 15:20
如何学校Cmake呢?cmake与makefile什么关系呀

它俩的关系类似于 CMD.exe 与 *.BAT 文件的关系。

2019ghua 发表于 2019-11-24 12:51

有朝一日我也能看懂并会操作就好了,,唉太菜

zhang321412 发表于 2019-11-24 13:09

好东西感谢分享

thatup 发表于 2019-11-24 18:57

好东西感谢分享

pk8900 发表于 2019-11-25 11:10

这个一直没研究明白,有时间会好好研读,这回哦了。谢谢楼主的教程。

CHILAS_LEE 发表于 2019-11-25 13:17

学到了,花指令和VM的东西{:1_893:}

一只小木木 发表于 2019-11-25 20:50

你的帖子好牛逼,可惜我学不会

solly 发表于 2019-11-28 10:13

liphily 发表于 2019-11-28 09:46
asm后就不能下断了

不明白你的意思???

richens 发表于 2019-11-28 11:38

虚心学习!

果果没有糖葫芦 发表于 2019-11-28 18:52

谢谢楼主,学习了{:1_927:}
页: [1] 2 3 4
查看完整版本: 160 个 CrackMe 之 082 - ultraschall 自带微型VM的跟踪和分析