网鼎杯一个CrackMe give_a_try.exe

转载请注明出处。https://rhirufxmbcyj.gitlab.io

从看雪一篇帖子上看到的这个程序,没什么难度,就是比较绕。拿下来试一试。

程序:https://rhirufxmbcyj.gitlab.io/files/give_a_try.rar

准备工作
  • 首先,打开程序看一看这是个什么类型的程序,是个name+password类型还是serial类型,心理也好有个底,这个程序只有一个编辑框,也就是个serial类型的。
  • 然后使用PEiD等工具 进行查壳、查语言、查算法。
    • 有壳先脱壳,尽量不干扰正常分析程序,能用工具脱就用工具脱,此程序无壳。
    • C类语言大多都一样,不需要特殊的工具,像Delphi或VB或Net就需要使用特定的工具去分析了,这个程序是汇编写的,更容易分析,怪不得函数那么少。
    • 使用PEid的插件Krypto ANALyzer查一下算法,也算是分析的时候考虑算法有个偏向。
分析

程序拖入IDA看一看,发现未知的函数只有几个,其他都是API,直接用IDA的f5一个函数一个函数看就找到了程序的校验算法那块,也就是sub_401103(char *a1),猜测参数1这个char *就是输入的字符串了。

这个是IDA的关键算法的伪代码。

int __stdcall sub_401103(char *a1)
{
  int result; // eax@2
  int v2; // edi@3
  char v3; // al@3
  char *v4; // esi@3
  unsigned int i; // ebx@6
  unsigned int v6; // eax@7
  unsigned int v7; // edx@7
  unsigned int v8; // edx@7
  unsigned int v9; // edx@7
  unsigned int v10; // edx@7
  unsigned int v11; // edx@7
  unsigned int v12; // edx@7
  unsigned int v13; // edx@7
  unsigned int v14; // edx@7
  unsigned int v15; // edx@7
  unsigned int v16; // edx@7
  unsigned int v17; // edx@7
  unsigned int v18; // edx@7
  unsigned int v19; // edx@7
  unsigned int v20; // edx@7
  unsigned int v21; // edx@7

  if ( strlen(a1) == 42 )                       // 字符串长度必须42
  {
    v2 = 0;
    v3 = *a1;
    v4 = a1 + 1;
    while ( v3 )                                // 将每个字符串的ascii累加到V2里
    {
      v2 += (unsigned __int8)v3;
      v3 = *v4++;
    }
    srand(dword_40406C ^ v2);                   // 没有用time(NULL)取随机数,也就是这个随机数是可以追朔的,所以这里是个突破口,前提是dword_40406c要确定
    for ( i = 0; i != 42; ++i )
    {
      v6 = (unsigned __int8)a1[i] * rand();
      v7 = v6 * (unsigned __int64)v6 % 0xFAC96621;
      v8 = v7 * (unsigned __int64)v7 % 0xFAC96621;
      v9 = v8 * (unsigned __int64)v8 % 0xFAC96621;
      v10 = v9 * (unsigned __int64)v9 % 0xFAC96621;
      v11 = v10 * (unsigned __int64)v10 % 0xFAC96621;
      v12 = v11 * (unsigned __int64)v11 % 0xFAC96621;
      v13 = v12 * (unsigned __int64)v12 % 0xFAC96621;
      v14 = v13 * (unsigned __int64)v13 % 0xFAC96621;
      v15 = v14 * (unsigned __int64)v14 % 0xFAC96621;
      v16 = v15 * (unsigned __int64)v15 % 0xFAC96621;
      v17 = v16 * (unsigned __int64)v16 % 0xFAC96621;
      v18 = v17 * (unsigned __int64)v17 % 0xFAC96621;
      v19 = v18 * (unsigned __int64)v18 % 0xFAC96621;
      v20 = v19 * (unsigned __int64)v19 % 0xFAC96621;
      v21 = v20 * (unsigned __int64)v20 % 0xFAC96621;
      // 一堆操作结果要等于aZCeie数组里的值,aZCeie里的值可以从IDA中拿出来,是固定的
      if ( v6 % 0xFAC96621 * (unsigned __int64)v21 % 0xFAC96621 != *(_DWORD *)&aZCeie[4 * i] )
        break;
    }
    if ( i >= 42 )                              // 42位字符都通过校验就是成功
      result = MessageBoxA(0, aCorrect, aCongrats, 0);
    else
      result = MessageBoxA(0, aIncorrect, 0, 0);
  }
  else
  {
    result = MessageBoxA(0, aThinkAgain, 0, 0);
  }
  return result;
}

设V6为未知数x,那么V7、V8、V9、Vn都可以根据V6得出,由于aZCeie数组里的值是固定的,那么根据上边的公式就可以得出i=0时V6的值了,开始写穷举代码。

int v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21;
for (int i = 0; i < 0xffffffff; i++)
{
    v7 = i * i % 0xFAC96621;
    v8 = v7 * v7 % 0xFAC96621;
    v9 = v8 * v8 % 0xFAC96621;
    v10 = v9 * v9 % 0xFAC96621;
    v11 = v10 * v10 % 0xFAC96621;
    v12 = v11 * v11 % 0xFAC96621;
    v13 = v12 * v12 % 0xFAC96621;
    v14 = v13 * v13 % 0xFAC96621;
    v15 = v14 * v14 % 0xFAC96621;
    v16 = v15 * v15 % 0xFAC96621;
    v17 = v16 * v16 % 0xFAC96621;
    v18 = v17 * v17 % 0xFAC96621;
    v19 = v18 * v18 % 0xFAC96621;
    v20 = v19 * v19 % 0xFAC96621;
    v21 = v20 * v20 % 0xFAC96621;
    if ((i % 0xFAC96621) * v21 % 0xFAC96621 == 0x63B25AF1)
        printf("%X\n", i);
}
printf("OK!\n");

得出v6 = 81FA5AF1。要求出第一个字符,有了第一个v6,根据公式v6 = (unsigned __int8)a1[i] * rand();所以还需要有rand()的值,rand()的值和srand里的参数有关。dword_40406C这处内存值通过动态调试得出。调试过程中有两个反调试函数,把push和call都nop掉,得出0x40406C = 0x31333359。

如果有了srand,那么rand就是固定了,a1[0]也就求出来了,也通过穷举方法,srand(dword_40406C ^ v2); v2是所有字符的累加值 还需要穷举 最小则是42个“ ”,最大则是42个“~”,以此得到穷举范围。

for (int i = 1344; i <= 5292; i++)
{
    srand(i ^ 0x31333359);
    int random = rand();
    for (int j = ' '; j <= '~'; j++)
    {
        if (j * random == 0x81FA5AF1)
            printf("find first character %c\n", j);
    }
}

跑完这个发现并没有得到想象中的结果 所以某些地方出问题了 回头看汇编代码

call    ds : rand
movzx   ecx, byte ptr[ebx + esi]
mul     ecx
mov     ecx, 0FAC96621h
push    eax        #v6
xor     edx, edx
div     ecx
pop     eax

push    edx        #edx = v6 % 0FAC96621h

mul     eax        #v6*v6
div     ecx
mov     eax, edx   #得到v7

mul     edx
div     ecx
mov     eax, edx    #v8

mul     edx
div     ecx
mov     eax, edx    #v9

mul     edx
div     ecx
mov     eax, edx    #v10

mul     edx
div     ecx
mov     eax, edx    #v11

mul     edx
div     ecx
mov     eax, edx    #v12

mul     edx
div     ecx
mov     eax, edx    #v13

mul     edx
div     ecx
mov     eax, edx    #v14

mul     edx
div     ecx
mov     eax, edx    #v15

mul     edx
div     ecx
mov     eax, edx    #v16

mul     edx
div     ecx
mov     eax, edx    #v17

mul     edx
div     ecx
mov     eax, edx    #v18

mul     edx
div     ecx
mov     eax, edx    #v19

mul     edx
div     ecx
mov     eax, edx    #v20

mul     edx
div     ecx
mov     eax, edx    #v21

mul     edx
div     ecx
mov     eax, edx    #v22

pop     edx         #edx = v6 % 0FAC96621h

mul     edx
div     ecx
                    #v22 * (v6 % 0FAC96621h) % 0FAC96621h
cmp     edx, [edi + ebx * 4]
jnz     short loc_4011F2

IDA的f5少算了一次运算,还是用汇编吧 避免编译器优化造成的错误 修改下第一次穷举代码

unsigned int result;
for (unsigned int i = 0; i < 0xffffffff; i++)
{
    _asm {
            mov     eax, i
            mov     ecx, 0FAC96621h
            push    eax 
            xor     edx, edx
            div     ecx
            pop     eax
            push    edx
            mul     eax
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            mul     edx
            div     ecx
            mov     eax, edx
            pop     edx
            mul     edx
            div     ecx
            mov     result, edx
    }
    if (result == 0x63B25AF1)
        printf("%X\n", i);
}
printf("OK!\n");

求得v6是0x19AF6A 继续用上边的穷举代码

for (int i = 1344; i <= 5292; i++)
{
    srand(i ^ 0x31333359);
    int random = rand();
    for (int j = ' '; j <= '~'; j++)
    {
        if (j * random == 0x19AF6A)
        {
            printf("find first character %c\n", j);
            printf("string sum is %d", i);
        }
    }
}

马上得到了结果 而且结果只有一个 ‘f’ 字符总和为3681 这时可以按程序的执行流程顺序穷举出全部字符串了。

unsigned int result = 0;
//value从IDA中复制出来的
unsigned int value[42] = { 0x63B25AF1,0x0C5659BA5,0x4C7A3C33,0x0E4E4267,0x0B611769B,0x3DE6438C,0x84DBA61F,0x0A97497E6,0x650F0FB3,0x84EB507C,0x0D38CD24C,0x0E7B912E0,0x7976CD4F,0x84100010,0x7FD66745,0x711D4DBF,0x5402A7E5,0x0A3334351,0x1EE41BF8,0x22822EBE,0x0DF5CEE48,0x0A8180D59,0x1576DEDC,0x0F0D62B3B,0x32AC1F6E,0x9364A640,0x0C282DD35,0x14C5FC2E,0x0A765E438,0x7FCF345A,0x59032BAD,0x9A5600BE,0x5F472DC5,0x5DDE0D84,0x8DF94ED5,0x0BDF826A6,0x515A737A,0x4248589E,0x38A96C20,0x0CC7F61D9,0x2638C417,0x0D9BEB996 };

srand(3681 ^ 0x31333359);
for (int i = 0; i < 42; i++)
{
    unsigned int random = rand();
    for (unsigned int j = ' '; j <= '~'; j++)
    {
        unsigned int v6 = j*random;
        _asm {
                mov     eax, v6
                mov     ecx, 0FAC96621h
                push    eax
                xor     edx, edx
                div     ecx
                pop     eax
                push    edx
                mul     eax
                div     ecx
                mov     eax, edx
                mul     edx
                div     ecx
                mov     eax, edx
                mul     edx
                div     ecx
                mov     eax, edx
                mul     edx
                div     ecx
                mov     eax, edx
                mul     edx
                div     ecx
                mov     eax, edx
                mul     edx
                div     ecx
                mov     eax, edx
                mul     edx
                div     ecx
                mov     eax, edx
                mul     edx
                div     ecx
                mov     eax, edx
                mul     edx
                div     ecx
                mov     eax, edx
                mul     edx
                div     ecx
                mov     eax, edx
                mul     edx
                div     ecx
                mov     eax, edx
                mul     edx
                div     ecx
                mov     eax, edx
                mul     edx
                div     ecx
                mov     eax, edx
                mul     edx
                div     ecx
                mov     eax, edx
                mul     edx
                div     ecx
                mov     eax, edx
                mul     edx
                div     ecx
                mov     eax, edx
                pop     edx
                mul     edx
                div     ecx
                mov     result, edx
        }
        if (result == value[i])
            printf("%c", j);
    }
}
printf("\nOK!\n");

跑完得到真正的结果 flag{wh3r3_th3r3_i5_@w111-th3r3_i5@_w4y}.

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值