xiaoyu2032 发表于 2022-7-4 09:23

练习笔记之160Crackme-024

# 160CM-024

## 1. 爆破

  由于能搜索到关键字符串,爆破还是比较简单的,将4012E9的代码“je short 004012D9”改成“jmp short 00401301”就可以了。



## 2. 算法分析

  由于这是汇编语言编写的程序,IDA分析只能看到三个用户函数,看不出算法函数,只能汇编代码硬杠了。

  4012DD到4012E3部分的代码是对4011EC到4012E3地址范围内的数据每四个字节进行异或计算,最后和0xAFFCCFFB进行比较是否相等。由于4011EC到4012E3为代码段,正常计算结果应该是固定的,但是程序里面有一处对0x4012D9进行的写入操作,该部分写入的数据会影响最后的异或计算结果。

```asm
00401278   .50            push eax                                 ; |pSuccess = 00002274
00401279   .6A 64         push 0x64                              ; |ControlID = 64 (100.)
0040127B   .FF35 50314000 push dword ptr ds:             ; |hWnd = 000B07C0 ('TEXme v2.0',class='CTEX')
00401281   .E8 BC010000   call <jmp.&USER32.GetDlgItemInt>         ; 获取数字serial,存入eax
00401286   .837D FC 00    cmp dword ptr ss:,0x0
0040128A   .74 5F         je short Chafe_2.004012EB
0040128C   .50            push eax
0040128D   .6A 14         push 0x14                              ; /Count = 14 (20.)
0040128F   .68 6C314000   push Chafe_2.0040316C                  ; |Buffer = Chafe_2.0040316C
00401294   .FF35 54314000 push dword ptr ds:             ; |hWnd = 003A07CA (class='Edit',parent=000B07C0)
0040129A   .E8 AF010000   call <jmp.&USER32.GetWindowTextA>      ; \GetWindowTextA
0040129F   .85C0          test eax,eax
004012A1   .74 48         je short Chafe_2.004012EB
004012A3   .A1 0B304000   mov eax,dword ptr ds:          ;算法开始   0x58455443
004012A8   .BB 6C314000   mov ebx,Chafe_2.0040316C               ;ASCII "abcde" :name
004012AD   >0303          add eax,dword ptr ds:                               ;依次移位4字节相加
004012AF   .43            inc ebx
004012B0   .81FB 7C314000 cmp ebx,Chafe_2.0040317C               ; 一直加到字符串存储空间尾部(20-4次)
004012B6   .^ 75 F5         jnz short Chafe_2.004012AD
004012B8   .5B            pop ebx
004012B9   .03C3          add eax,ebx                                                               ;ebx为入栈的serial数值
004012BB   .3105 D9124000 xor dword ptr ds:,eax          ;与4012D9异或,结果存入4012D9,4012D9的初始值为00584554
004012C1   .C1E8 10       shr eax,0x10                                                       ;右移位16bit,取高16位
004012C4   .66:2905 D9124>sub word ptr ds:,ax                       ;减去ax(高16位的数值)
004012CB   .BE EC114000   mov esi,Chafe_2.004011EC                               ;esi起始地址,到40122A,每4字节异或
004012D0   .B9 3E000000   mov ecx,0x3E
004012D5   .33DB          xor ebx,ebx
004012D7   .EB 04         jmp short Chafe_2.004012DD
004012D9   >CC            int3
004012DA      2E            db 2E                                    ;CHAR '.'
004012DB      2B            db 2B                                    ;CHAR '+'
004012DC      22            db 22                                    ;CHAR '"'
004012DD   >AD            lods dword ptr ds:                                        ;获取后esi+4
004012DE   .33D8          xor ebx,eax
004012E0   .49            dec ecx
004012E1   .^ 75 FA         jnz short Chafe_2.004012DD
004012E3   .81FB FBCFFCAF cmp ebx,0xAFFCCFFB
004012E9    ^ 74 EE         je short Chafe_2.004012D9
```

  name输入abcde,serial输入123456,在4012E9处下断(这里需要特别注意,计算异或数据结果时不能在4011EC到4012E3地址范围内设置断点,因为设置断点后调试器会更换中断地址首字节为CC,这将影响计算结果。我就是在这里被坑了差不多一天,算完的serial在调试环境下有时可以,有时不对,在4012D9内容相同的情况下有时最终的异或结果是AFFCCFFB,有时又不是),可以得到4011EC到4012E3所有数据异或后的结果为==D7BA9681==。由于4012D9的数值为222C60B2,按四字节对齐,其影响的数据包括两个WORD,分别为2C60B204和D833AD22。由于A与B的异或结果和B与A的异或结果是一样的,因此2C60B204和D833AD22的异或结果也等于2C60B222(可变部分)和D833AD04(不变部分)的异或结果。

  假设:

  A=D7BA9681;

  B=2C60B222;

  C=D833AD04;

  D=AFFCCFFB;

  4011EC到4012E3程序段的其余部分的异或结果为F;

  则有F ^ B ^ C =A,所以F=A ^ B ^ C。

  假设可变部分的正确结果为G,则D=F ^G ^ C,

  因此G=D ^ C ^ F

​             =D ^C ^(A ^ B ^ C)

​             =D ^ A ^ B

  因此可变部分的正确结果

​          G=AFFCCFFB ^ D7BA9681 ^2C60B222 =5426EB58

  因此,正确的4012D9的数值应为585426EB。



  在4012D7处下断点,断下后,将4012D9的数据修改成585426EB,可以看到4012D9代号行的跳转调到yes!提示字符位置了,然后取消4012D7处的断点(不取消计算结果会不对),选中4012E9这一行,按F4,可以看到ebx的数值与0xAFFCCFFB相等,满足跳转到4012D9行的条件。



  接下来就要分析4012D9地址数据的计算算法了,算法首先用一个初始值0x58455443与输入的name字符串依次取4字节的数字进行相加,然后地址加1,再继续相加,一直到20(和上一题类似),假设计算结果为temp2,则

  0x585426EB=(temp2 + sn) ^ 0x00584554 - ((temp2 + sn) >> 16)

  假设key=temp2 + sn,则

  0x585426EB=key ^ 0x00584554 - key>>16

  假设key的高16位为key_high,低16位为key_low,则

  0x5854 * 0x10000 + 0x26EB = (key_high ^ 0x58) * 0x10000 + key_low ^ 0x4554 - key_high

  因此上述等式也可以拆分成两个等式,即

  0x5854 = key_high ^ 0x58 和0x26EB = key_low ^ 0x4554 - key_high

  所以 key_high = 0x5854^ 0x58 = 0x580C

​                key_low = ( 0x26EB + 0x580C ) ^ 0x4554 = 3BA3

  最终可得,key=0x580C3BA3

  所以,sn = 0x580C3BA3 - temp2

  编写算法程序如下:

```cpp
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <math.h>

int _tmain(int argc, _TCHAR* argv[])
{
        char name;
        int temp,temp2,key,length;
        int i;
        UINT32 code;
        printf("请输入用户名(长度20以内):");
        scanf_s("%s", name, 20);
        length = strlen(name);
        temp2 = 0x58455443;
        key = 0x580C3BA3;
       
        for (i = 0; i < 19; i++)
        {
        if (i >= length)
                {
                        name = 0;
                }
        }
        for (i = 0; i < 16; i++)
        {
      temp = name + name * 0x100 + name * 0x10000 + name * 0x1000000;
      temp2 = temp2 + temp;
                //printf("16进制temp2 %d为:%x\n", i, temp2);
        }
        code = key-temp2;
        printf("序列号为:%u\n", code);
        system("pause");
        return 0;
}
```

  运行,输入用户名abcde,计算得到序列号如下



  将用户名和序列号输入程序中,验证成功。



## 3. 总结

  这是一道有一些难度的算法题,需要对异或运算的特点有比较清晰的理解。另外,这个程序里面用lods装载指令内容进行校验,校验成功才能正常跳转,这算是程序自校验的一种方法。这里面有一个比较大的坑就是设置断点会影响lods装载的内容,即“设置断点后调试器会更换中断地址首字节为CC”,这个是看了PK大佬的教程才明白的。

不搭落俗笑忘书 发表于 2022-7-4 09:25

值得学习!

ok378 发表于 2022-7-4 09:40

谢谢楼主分享

xyq3q 发表于 2022-7-4 10:17

谢谢楼主分享, 学习了

sky2021 发表于 2022-7-4 10:38

感谢分享,学习

yyb414 发表于 2022-7-4 11:43

向楼主学习

moshuiNW 发表于 2022-7-5 00:21

感谢楼主分享!我应该想你学习

飞翔007 发表于 2022-7-6 11:44

感谢楼主分享!我应该想你学习

lyxy2020 发表于 2022-7-7 16:16

这破解的是什么软件

alokm 发表于 2022-7-7 19:29

厉害,一起学学
页: [1]
查看完整版本: 练习笔记之160Crackme-024