[BUU]HellScream

题目:[BUU]HellScream

0x00 RSA

这题比较烧脑,考察的是rsa算法,先简单了解一下rsa:
在这里插入图片描述

0x01

本篇文章的OD测试flag是“1234567890ABCDEF12345”,不再赘述。

这个程序先对一个给定的长字符串取出每个字符,依次转化成数据(‘A’——>A),然后经过两个函数sub_4019F0和sub_401940的处理,
在这里插入图片描述
先看看sub_4019F0函数,

int __usercall sub_4019F0@<eax>(int *a1@<ecx>, int *a2@<esi>)
{
  int v2; // eax
  int v3; // edx
  char *v4; // edi
  char *v5; // ecx
  int v6; // ebx
  int v7; // edi
  __int64 v8; // kr00_8
  bool v9; // zf
  int v10; // eax
  int v12; // [esp+10h] [ebp-8h]
  int v13; // [esp+14h] [ebp-4h]

  v2 = *a1;
  v3 = 0;
  v4 = (a1 + 1);
  v5 = (a2 + 1);
  if ( v2 )
  {
    v6 = 0;
    if ( v2 > 0 )
    {
      v7 = v4 - v5;
      v12 = v7;
      v13 = v2;
      while ( 1 )
      {
        v8 = v3 + 16i64 * *&v5[v7];
        *v5 = v8;
        v5 += 4;
        v9 = v13-- == 1;
        v3 = HIDWORD(v8);
        if ( v9 )
          break;
        v7 = v12;
      }
      v6 = v2;
    }
    a2[v6 + 1] = v3;
    *a2 = v6;
    do
    {
      if ( a2[*a2 + 1] )
        break;
      v10 = *a2 - 1;
      *a2 = v10;
    }
    while ( v10 >= 0 );
    if ( ++*a2 > 128 )
    {
      *a2 = 128;
      return 4099;
    }
  }
  else
  {
    *a2 = 0;
  }
  return 0;
}

动调一下看出eax寄存器放的是下标,ebx放的是值,esi和ecx放的是内存单元00978680的地址。
在这里插入图片描述
数据窗口中跟随00978680,然后F7步入发现无实际作用。内存单元中值不曾改变
在这里插入图片描述
继续看下一个函数sub_401940,

int __usercall sub_401940@<eax>(int *a1@<eax>, unsigned int a2@<ecx>)
{
  int v2; // edx
  int v3; // ecx
  int result; // eax
  int v5; // esi
  int v6; // ecx
  _DWORD *v7; // edx
  int v9; // ecx
  int v10; // ecx

  v2 = *a1;
  if ( *a1 )
  {
    a1[1] += a2;
    v5 = 1;
    if ( a1[1] < a2 )
    {
      if ( v2 == 1 )
      {
        a1[2] = 1;
        v5 = 2;
      }
      else
      {
        v6 = a1[2] + 1;
        a1[2] = v6;
        if ( !v6 )
        {
          v7 = a1 + 2;
          while ( 1 )
          {
            ++v5;
            ++v7;
            if ( v5 >= *a1 )
              break;
            if ( (*v7)++ != -1 )
              goto LABEL_15;
          }
          a1[v5 + 1] = 1;
        }
LABEL_15:
        ++v5;
      }
    }
    if ( v5 >= *a1 )
    {
      v9 = v5 - 1;
      if ( v5 - 1 >= 127 )
        v9 = 127;
      *a1 = v9;
      if ( v9 >= 0 )
      {
        do
        {
          if ( a1[*a1 + 1] )
            break;
          v10 = *a1 - 1;
          *a1 = v10;
        }
        while ( v10 >= 0 );
      }
      ++*a1;
    }
    result = v5 <= 128 ? 0 : 0x1003;
  }
  else
  {
    a1[1] = a2;
    *a1 = 0;
    do
    {
      if ( a1[*a1 + 1] )
        break;
      v3 = *a1 - 1;
      *a1 = v3;
    }
    while ( v3 >= 0 );
    ++*a1;
    result = 0;
  }
  return result;
}

ecx和ebx中放的是数值,eax放的是内存单元00978680的地址。

F7步入然后走出发现数据读入以968084为起始的内存单元,
在这里插入图片描述
既然只是对978680内存单元的操作,直接数据窗口跟随,然后F8步过,看数据窗口的变化即可,几次循环进行下来,发现sub_4019F0进行的其实是移位操作,方便sub_401940把数据填入978684。

直接循环外下断点按F9走出循环看数据窗口,不出所料,数据被读入了978684,与字符串的一致。

(分析到后面可以发现处理的那串字符原来是rsa的那个大素数N。)
在这里插入图片描述

0x02

在输入前还有一个sub_406230函数
在这里插入图片描述
用od调试这个函数前看一下寄存器,其中ebx里放的是某内存地址跟"www.51asm.com"所存放的地址的偏移地址,eax里放的是"www.51asm.com"字符串地址978EA0,F8步过发现这个函数没什么实际意义。

然后是对输入的flag必须要是字母和数字的限制。
在这里插入图片描述
然后跟程序开头把一串字符变成大数一样,把输入的flag转化成了数据,
在这里插入图片描述
不出意外存入的是00978A94内存空间,这就是flag对应的数据(16进制大数1234567890ABCDEF12345)地址。
在这里插入图片描述
运行完循环,数据窗口中跟随00978A90,flag对应的数据已经存入00978A94
在这里插入图片描述

0x03 sub_406330

其实看函数sub_406330,这个函数被两次调用,
在这里插入图片描述
第一次传入的参数是dword_448C98("www.51asm.com"字符串的地址)和byte_448EA0(输入的flag字符串的地址,注意区分输入的flag数据的地址)。

int __fastcall sub_406330(_DWORD *a1, _BYTE *a2)
{
  size_t v2; // ebx
  _BYTE *v3; // esi
  int result; // eax
  size_t v5; // edi
  size_t v6; // ebx
  size_t i; // eax
  char v8; // cl
  int v10; // [esp+14h] [ebp-624h]
  char v11[520]; // [esp+18h] [ebp-620h] BYREF
  int v12; // [esp+220h] [ebp-418h]
  char v13[516]; // [esp+224h] [ebp-414h] BYREF
  int v14; // [esp+428h] [ebp-210h]
  int v15[130]; // [esp+42Ch] [ebp-20Ch] BYREF

  v2 = 0;
  v3 = a2;
  if ( !Size )
    return 4097;
  if ( Size == 1 )
  {
    *a2 = 0;
    return 4097;
  }
  if ( *a1 )
  {
    (sub_4018E0)();
    (sub_401910)(16);
    v10 = 0;
    if ( v14 )
    {
      v5 = 1;
      while ( 1 )
      {
        (sub_4020E0)(v11);
        v3[++v5 - 2] = byte_407104[v14 != 0 ? v15[0] : 0];
        ++v2;
        if ( v5 >= Size )
          break;
        v14 = v12;
        if ( v12 )
        {
          if ( v12 > 0 )
          {
            qmemcpy(v15, v13, 4 * v12);
            v3 = a2;
          }
          if ( v14 )
            continue;
        }
        goto LABEL_16;
      }
      v10 = 0x1001;
    }
LABEL_16:
    Size = v2;
    v6 = v2 - 1;
    v3[v6 + 1] = 0;
    for ( i = 0; v6 > i; ++i )
    {
      v8 = v3[v6];
      v3[v6] = v3[i];
      v3[i] = v8;
      --v6;
    }
    result = v10;
  }
  else
  {
    *a2 = 48;
    Size = 2;
    result = 0;
  }
  return result;
}

od动调看一下它的原理,步入这个函数前依旧是看一下的各个寄存器的值,其中edx是输入的flag所在的地址,ecx是"www.51asm.com"的地址。
在这里插入图片描述
调试几次我们可以发现这个函数其实是将用户名ascii编码成数据,再转化成字符(例:‘A’——>65——>'65’窗口输出),最后整体倒置输出,
在这里插入图片描述
在这里插入图片描述
第二次的调用异曲同工,从内存单元中读取我们之前存入的数据然后编码,原封不动的把我们的输入输出来。

0x04 sub_403E30

这个函数也是调用了两次,&dword_448C98里面存放的是"www.51asm.com",而dword_448A90存放的是我们的flag对应的大数
在这里插入图片描述
这个函数的源码比较长,而且还有ollvm混淆,就不贴上源代码了
在这里插入图片描述
直接od按F8步过并数据窗口跟随看看它们对两个地址做了什么修改,数据没有发生改变
在这里插入图片描述
在这里插入图片描述

0x05 sub_401510

这个函数应该就是关键函数了,参考别的博主的wp后知道这个函数总体流程是先生成公钥e,每次循环(一共循环100次)e会自增,每一个更新后的e会用来做一次rsa加密然后与字符串“Happy Birthday Buddy”作比较,这个循环直到比较出相同(此时成功)或者100次每一次都没有加密出 “Happy Birthday Buddy”(此时便失败),才会退出循环。

unk_448EB5存放的应该就是提示正确的字符串,其实细心点可以发现 byte_448EA0 这个地址为 “加密后的地址 + 0x15” 处的起始地址,0x15刚好是字符串"Happy Birthday Buddy"的长度+1(因为根据strcmp函数的原理,要算上\0,strcmp函数才验证成功),也就是说我们输入的flag加密出来应该就包含有成功提示信息。

所以最后程序应该是加密出来一个“Happy Birthday Buddy\0\输入正确的提示”。这个提示其实是自定义的
在这里插入图片描述

int sub_401510()
{
 int v0; // eax
 int v1; // esi
 int v2; // eax
 char v4[1048]; // [esp+10h] [ebp-830h] BYREF
 int v5; // [esp+428h] [ebp-418h] BYREF
 int v6; // [esp+630h] [ebp-210h]
 int v7[130]; // [esp+634h] [ebp-20Ch]

 v0 = 1;
 v7[1] = 0;
 v7[0] = 17;
 v6 = 1;
 do
 {
   if ( v7[v0] )
     break;
   v6 = --v0;
 }
 while ( v0 >= 0 );
 v6 = v0 + 1;
 sub_4064D0(&v5);                              // 利用用户名www.51asm.com生成初始公钥e。
 v1 = 0;
 while ( 1 )
 {
   v2 = 0;
   dword_44888C[0] = 1;
   dword_448888 = 0;
   do
   {
     if ( dword_44888C[v2] )
       break;
     dword_448888 = --v2;
   }
   while ( v2 >= 0 );
   dword_448888 = v2 + 1;
   if ( !sub_405310(&dword_448680, v4, 0) )    // 数据初始化,构造大数
     sub_401000(v5, v4);                       // rsa加密,密文存入dword_448888
   Size = 10240;
   if ( dword_448888 )
   {
     sub_4062A0();                             // 把dword_448888中的密文复制到byte_448EA0
   }
   else
   {
     memset(byte_448EA0, 0, 0x2800u);
     Size = 0;
   }
   if ( !strcmp("Happy Birthday Buddy", byte_448EA0) )// 密文与"Happy Birthday Buddy"作比较
     break;
   sub_401940(1);
   if ( ++v1 >= 100 )                          // 如果满100次就退出循环
     return 0;
 }
 return 1;
}

0x06 Crack

既然它做的是加密操作,我们反过来对字符串进行解密,后面的那个提示正确的字符串是自定义的,中间有个’\x0’用于strcmp比较函数用,所以我们设置密文为

"Happy Birthday Buddy\x0ok"

转换成大数就是

c=6B6F007964647542207961646874726942207970706148

OD动调得到初始公钥e(e是在从e到e+99间变化的)

初始e=9CA87DE3775787F7695F3F316E503600348AB6F58BEF375D0ED8F8BE84425FA7A6C3

还有程序一开始写入内存的公钥N

16进制:
N=A324F100182D501F6F6F78F397A3AA59641023D6A3DED8A4BF344F1E0FC71C188F4D
10进制形式:
N=4836049822470645700707675549457249771340211537787234319173530875014794666294415181 

必要的数据有了就可以进行解密操作

N——>p,q

先用在线工具求出N的素因子
(用RSA-Tool分解太慢了,建议用在线因式分解工具或者yafu分解)
在这里插入图片描述

16进制:
p=b89eb7e0c9a568202f38b169d9d7e27b93 
q=e2389b3c14e6423ee5eb9db5dac2559f 

10进制:
p=62822928286347608648869628628072621308819
q=76979057716441943100156208562083628995999

e——>d

用RSA-Tool生成d,因为不知道哪个e与N的欧拉函数互素,所以需要e到e+99一个个尝试,发现其实e+2就已经满足了。
在这里插入图片描述
得到私钥d

d=3AD75813052BC545DAC589519734FF0972200E8E31DFA08DE50D15CFA2667132E4E1

Encrypt

网站或者工具解密一下(由于密文有自定义部分,所以答案不唯一):

274964845CCC8AAA8A0CD62B0971A23954F741240EEDD43A02F79DA2B7372D68C80C

得到flag

flag{274964845CCC8AAA8A0CD62B0971A23954F741240EEDD43A02F79DA2B7372D68C80C}

参考wp:

  • https://bbs.pediy.com/thread-214562-1.htm
  • https://bbs.pediy.com/thread-258108.htm
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Em0s_Er1t

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

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

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

打赏作者

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

抵扣说明:

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

余额充值