简单逆向18

35 篇文章 5 订阅
"本文详细分析了一个名为诺莫18的程序,该程序要求用户输入符合特定条件的字符串。通过逆向工程,解析了输入字符串的处理流程,包括字符转换、异或运算和MD5加密。最终确定了正确的输入字符串'flag{d2be2981b84f2a905669995873d6a36c}
摘要由CSDN通过智能技术生成

诺莫18

原题:在这里插入图片描述

开始:

放入IDA:

int __usercall wmain@<eax>(int a1@<ebx>)
{
  FILE *v1; // eax
  FILE *v2; // eax
  char v4; // [esp+3h] [ebp-405h]
  char v5; // [esp+4h] [ebp-404h]
  char v6; // [esp+5h] [ebp-403h]
  char v7; // [esp+104h] [ebp-304h]
  char v8; // [esp+105h] [ebp-303h]
  char v9; // [esp+204h] [ebp-204h]
  char v10; // [esp+205h] [ebp-203h]
  char v11; // [esp+304h] [ebp-104h]
  char v12; // [esp+305h] [ebp-103h]

  printf("Come one! Crack Me~~~\n");
  v11 = 0;
  memset(&v12, 0, 0xFFu);
  v9 = 0;
  memset(&v10, 0, 0xFFu);
  while ( 1 )
  {
    do
    {
      do
      {
        printf("user(6-16 letters or numbers):");
        scanf("%s", &v11);
        v1 = (FILE *)sub_4024BE();
        fflush(v1);
      }
      while ( !sub_401000(&v11) );
      printf("password(6-16 letters or numbers):");
      scanf("%s", &v9);
      v2 = (FILE *)sub_4024BE();
      fflush(v2);
    }
    while ( !sub_401000(&v9) );
    sub_401090(&v11);
    v7 = 0;
    memset(&v8, 0, 0xFFu);
    v5 = 0;
    memset(&v6, 0, 0xFFu);
    v4 = ((int (__cdecl *)(char *, char *))loc_4011A0)(&v7, &v5);
    if ( sub_401830(a1, (int)&v11, &v9) )
    {
      if ( v4 )
        break;
    }
    printf(&v5);
  }
  printf(&v7);
  return 0;
}

sub_401000()判断输入字符长度为6-16,并且为字母或者字符

sub_401090(&v11)没有对v11进行操作,不影响密码和流程,跳过

v4 = ((int (__cdecl *)(char *, char *))loc_4011A0)(&v7, &v5);

看后面printf(&v5);
v5的数据没有,使用动态调试:
在这里插入图片描述
在这里插入图片描述
一个跳出这个循环就对了,那就要
sub_401830(a1, (int)&v11, &v9)
v4均满足if,v4是由两个已知量输入的,所以我们不用管

sub_401830(a1, (int)&v11, &v9):

bool __usercall sub_401830@<al>(int ebx0@<ebx>, int user, const char *pow)
{
  int v4; // [esp+18h] [ebp-22Ch]
  signed int v5; // [esp+1Ch] [ebp-228h]
  signed int v6; // [esp+28h] [ebp-21Ch]
  unsigned int v7; // [esp+30h] [ebp-214h]
  char v8; // [esp+36h] [ebp-20Eh]
  char v9; // [esp+37h] [ebp-20Dh]
  char v10; // [esp+38h] [ebp-20Ch]
  unsigned __int8 v11; // [esp+39h] [ebp-20Bh]
  unsigned __int8 v12; // [esp+3Ah] [ebp-20Ah]
  char v13; // [esp+3Bh] [ebp-209h]
  int v14; // [esp+3Ch] [ebp-208h]
  char v15; // [esp+40h] [ebp-204h]
  char v16; // [esp+41h] [ebp-203h]
  char v17; // [esp+140h] [ebp-104h]
  char v18; // [esp+141h] [ebp-103h]

  v5 = 0;
  v6 = 0;
  v12 = 0;
  v11 = 0;
  v17 = 0;
  memset(&v18, 0, 0xFFu);
  v15 = 0;
  memset(&v16, 0, 0xFFu);
  v10 = 0;
  v7 = 0;
  v4 = 0;
  while ( v7 < strlen(pow) )
  {
    if ( isdigit(pow[v7]) )
    {
      v9 = pow[v7] - 48;
    }
    else if ( isxdigit(pow[v7]) )
    {
      if ( *(_DWORD *)(*(_DWORD *)(__readfsdword(0x30u) + 24) + 12) != 2 )
        pow[v7] = 34;
      v9 = (pow[v7] | 0x20) - 87;
    }
    else
    {
      v9 = ((pow[v7] | 0x20) - 97) % 6 + 10;    // 将a,b,c...转换为16进制对应的10进制数值
    }
    v10 = v9 + 16 * v10;
    if ( !((signed int)(v7 + 1) % 2) )
    {
      *(&v15 + v4++) = v10;
      ebx0 = v4;
      v10 = 0;
    }
    ++v7;
  }
  while ( v6 < 8 )
  {
    v11 += byte_416050[++v12];
    v13 = byte_416050[v12];
    v8 = byte_416050[v11];
    byte_416050[v11] = v13;
    byte_416050[v12] = v8;
    if ( *(_DWORD *)(__readfsdword(0x30u) + 104) & 0x70 )
      v13 = v11 + v12;
    *(&v17 + v6) = byte_416050[(unsigned __int8)(v8 + v13)] ^ *(&v15 + v5);
    if ( *(_DWORD *)(__readfsdword(0x30u) + 2) & 0xFF )
    {
      v11 = -83;
      v12 = 43;
    }
    sub_401710((int)&v17, (const char *)user, v6++);
    v5 = v6;
    if ( v6 >= (unsigned int)(&v15 + strlen(&v15) + 1 - &v16) )
      v5 = 0;
  }
  v14 = 0;
  sub_401470(ebx0, &v17, &v14);
  return v14 == 43924;
}

分析:

将输入字符串进行操作后与byte_416050[(unsigned __int8)(v8 + v13)]数据亦或再放入sub_401470(ebx0, &v17, &v14);。这是关键流程,倒着看,要让v14 == 43924,进入sub_401470(ebx0, &v17, &v14):

_DWORD *__usercall sub_401470@<eax>(int a1@<ebx>, _BYTE *a2, _DWORD *a3)
{
  int v3; // ST28_4
  int v4; // ecx
  _DWORD *_EAX; // eax
  int v6; // edx
  int v8; // ST20_4
  int v9; // eax
  int v10; // edi
  int v11; // ST1C_4
  int v12; // edx
  char v13; // di
  int v14; // ST18_4
  int v15; // eax
  int v16; // ST14_4
  int v17; // edx
  char v18; // al
  int v19; // ST10_4
  int v20; // ecx
  char _AL; // al
  int v23; // ST0C_4
  int v24; // eax
  _DWORD *result; // eax
  int v26; // edx

  if ( *a2 == 'd' )
  {
    *a3 |= 4u;
    v4 = *a3;
  }
  else
  {
    *a3 ^= 3u;
  }
  v3 = *a3;
  if ( a2[1] == 'b' )
  {
    _EAX = a3;
    *a3 |= 0x14u;
    v6 = *a3;
  }
  else
  {
    *a3 &= 0x61u;
    _EAX = (_DWORD *)*a3;
  }
  __asm { aam }
  if ( a2[2] == 'a' )
  {
    *a3 |= 0x84u;
    v9 = *a3;
  }
  else
  {
    *a3 &= 0xAu;
  }
  v8 = *a3;
  v10 = ~(a1 >> -91);
  if ( a2[3] == 'p' )
  {
    *a3 |= 0x114u;
    v12 = *a3;
  }
  else
  {
    *a3 >>= 7;
  }
  v11 = *a3;
  v13 = v10 - 1;
  if ( a2[4] == 'p' )
  {
    *a3 |= 0x380u;
    v15 = *a3;
  }
  else
  {
    *a3 *= 2;
  }
  v14 = *a3;
  if ( *(_DWORD *)(*(_DWORD *)(__readfsdword(0x30u) + 24) + 12) != 2 )
  {
    if ( a2[5] == 102 )
    {
      *a3 |= 0x2DCu;
      v17 = *a3;
    }
    else
    {
      *a3 |= 0x21u;
    }
    v16 = *a3;
  }
  if ( a2[5] == 's' )
  {
    *a3 |= 0xA04u;
    v18 = (char)a3;
    v20 = *a3;
  }
  else
  {
    v18 = (char)a3;
    *a3 ^= 0x1ADu;
  }
  v19 = *a3;
  _AL = v18 - v13;
  __asm { daa }
  if ( a2[6] == 'e' )
  {
    *a3 |= 0x2310u;
    v24 = *a3;
  }
  else
  {
    *a3 |= 0x4Au;
  }
  v23 = *a3;
  if ( a2[7] == 'c' )
  {
    result = a3;
    *a3 |= 0x8A10u;
    v26 = *a3;
  }
  else
  {
    *a3 &= 0x3A3u;
    result = (_DWORD *)*a3;
  }
  return result;
}

加入每一个if都成立,验证a3的值是不是43924

a3 = 0
a3 |= 0x4
a3 |= 0x14
a3 |= 0x84
a3 |= 0x114
a3 |= 0x380
#a3 |= 0x2DCu  反调试的不要
a3 |= 0xA04
a3 |= 0x2310
a3 |= 0x8A10

print(a3) #43924 #dbappsec

输出结果就是43924
所以v17的值是’dbappsec’
现在研究前面的算法:

while ( v7 < strlen(pow) )
  {
    if ( isdigit(pow[v7]) )
    {
      v9 = pow[v7] - 48;
    }
    else if ( isxdigit(pow[v7]) )
    {
      if ( *(_DWORD *)(*(_DWORD *)(__readfsdword(0x30u) + 24) + 12) != 2 )
        pow[v7] = 34;
      v9 = (pow[v7] | 0x20) - 87;
    }
    else
    {
      v9 = ((pow[v7] | 0x20) - 97) % 6 + 10;    // 将a,b,c...转换为16进制对应的10进制数值
    }
    v10 = v9 + 16 * v10;
    if ( !((signed int)(v7 + 1) % 2) )
    {
      *(&v15 + v4++) = v10;
      ebx0 = v4;
      v10 = 0;
    }
    ++v7;
  }

如果是字符是数字,就转成相应的值,如果是字母,就转成相应的16进制,如:a转成10,b转成11(第二个if不需要管,单个字符字符不肯能识别为16进制)

然后两个两个组合看成16进制,转化为10进制寸到v15。

简而言之就是把一个字符串两个两个看成一个16进制,转换为10进制存到数组中

byte_416050我们也需要动态调试(再IDA找到偏移地址):
在这里插入图片描述
查找ecx值,8次
[0x2a, 0xd7, 0x92, 0xe9, 0x53, 0xe2, 0xc4, 0xcd]

逆向:

import hashlib
a3 = 0
a3 |= 0x4
a3 |= 0x14
a3 |= 0x84
a3 |= 0x114
a3 |= 0x380
#a3 |= 0x2DCu  反调试的不要
a3 |= 0xA04
a3 |= 0x2310
a3 |= 0x8A10

print(a3) #43924 #dbappsec

x = [0x2a, 0xd7, 0x92, 0xe9, 0x53, 0xe2, 0xc4, 0xcd]
y = ['d','b','a','p','p','s','e','c']
m = ""

for i in range(len(y)):
    m += hex(ord(y[i])^x[i])
    m = m.replace("0x","")

n = hashlib.md5()
n.update(m.encode("utf-8"))

print(n.hexdigest())

根据题目要求,要进行md5加密

套上flag
flag{d2be2981b84f2a905669995873d6a36c}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I Am Rex

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值