攻防世界-ReverseMe-120

查壳

无壳

在这里插入图片描述

IDA

v11为输入,在sub_401000函数进行处理。中间暂时看不懂。最后让v13字符串逐位与37异或,最后得到的字符串为 ‘you_know_how_to_remove_junk_code’

在这里插入图片描述



sub_401000:

a1:v15 ; a2:v13(结尾比对) ; a3:v11(输入) ; a4:v11的长度

这部分看吐了……真就和最后对比字符串一样,要自己排除垃圾代码,下文注释掉的代码基本没用……

后半部分是关键,将输入的字符串的ASCⅡ值作为索引,检索table: byte_574E40的值,然后v14通过位移 与 或 操作,每次在末尾加上检索值得二进制形式(每次取后6位添加),四个字符一组。

int __usercall sub_561000@<eax>(unsigned int *a1@<edx>, _BYTE *a2@<ecx>, unsigned __int8 *a3, unsigned int a4)
{
  int v4; // ebx
  unsigned int v5; // eax
  int v6; // ecx
  unsigned __int8 *v7; // edi
  int v8; // edx
  bool v9; // zf
  unsigned __int8 v10; // cl
  char v11; // cl
  _BYTE *v12; // esi
  unsigned int v13; // ecx
  int v14; // ebx
  unsigned __int8 v15; // cl
  char v16; // dl
  int v20; // [esp+14h] [ebp-4h]
  unsigned int v21; // [esp+14h] [ebp-4h]
  int i; // [esp+24h] [ebp+Ch]

//   v4 = 0;
//   v5 = 0;
//   v6 = 0;
//   v20 = 0;
//   if ( !a4 )
//     return 0;
//   v7 = a3;
//   do
//   {
//     v8 = 0;
//     v9 = v5 == a4;
//     if ( v5 < a4 )
//     {
//       do
//       {
//         if ( a3[v5] != 32 )
//           break;
//         ++v5;
//         ++v8;
//       }
//       while ( v5 < a4 );
//       v9 = v5 == a4;
//     }
//     if ( v9 )
//       break;
//     if ( a4 - v5 >= 2 && a3[v5] == 13 && a3[v5 + 1] == 10 || (v10 = a3[v5], v10 == 10) )
//     {
//       v6 = v20;
//     }
//     else
//     {
//       if ( v8 )
//         return -44;
//       if ( v10 == 61 && (unsigned int)++v4 > 2 )
//         return -44;
//       if ( v10 > 0x7Fu )
//         return -44;
//       v11 = byte_574E40[v10];
//       if ( v11 == 127 || (unsigned __int8)v11 < 0x40u && v4 )
//         return -44;
//       v6 = ++v20;
//     }
//     ++v5;
//   }
//   while ( v5 < a4 );
//   if ( !v6 )
//     return 0;
  v12 = a2;
  v13 = ((unsigned int)(6 * v6 + 7) >> 3) - v4;
  if ( a2 && *a1 >= v13 )                       // a1:v15 ; a2:v13(结尾比对) ; a3:v11(输入) ; a4:v11的长度
  {
    v21 = 3;
    v14 = 0;
    for ( i = 0; v5; --v5 )                     // v5 输入字符串的长度
    {
      v15 = *v7;                                // v7是输入字符串,其字符挨个传给v15
      if ( *v7 != 13 && v15 != 10 && v15 != 32 )
      {
        v16 = byte_574E40[v15];                 // v15是输入的ASCⅡ值,这里作为base表的索引
        v21 -= v16 == 64;
        v14 = v16 & 0x3F | (v14 << 6);          // 取v16二进制的后6位,v14整体左移6位。通过或运算缝合,实现v14+=v16二进制的后六位
        if ( ++i == 4 )         //四个字符串切割成3位
        {
          i = 0;
          if ( v21 )
            *v12++ = BYTE2(v14);
          if ( v21 > 1 )
            *v12++ = BYTE1(v14);
          if ( v21 > 2 )        
            *v12++ = v14;
        }
      }
      ++v7;
    }
    *a1 = v12 - a2;
    return 0;
  }
  *a1 = v13;
  return -42;
}

解题

菜鸡读到junk_code是真要命,读了好久好久才知道大概,有些细节没搞懂怎么回事,但是连蒙带猜流程差不多捋清了:

输入字符串,进行base64解密(没错就是解密,这里本来看不到table的,但是字符串四转三二进制保存有点眼熟),最后每位字符和37异或得到给出的字符串

EXP

import base64
arr='you_know_how_to_remove_junk_code'
arr2=''
flag=''
for s in arr:
    arr2+=chr(ord(s)^37)
flag=base64.b64encode(arr2)
print(flag)

flag

XEpQek5LSlJ6TUpSelFKeldASEpTQHpPUEtOekZKQUA=

坑&填坑

1.下图判断我动调的结果是v21不变,前两次*v12++是junk code,到那时这样的话v12一次性就存入了4个字符的二进制数据,后面也没相应分割操作,迷惑

#提问时的理解:
多次测试发现v21始终是定值3,整个大if判断逻辑就是
每4次输入的值传给v12(v12声明为byte* v12),
然后v12指针指向下一位

解决:

既然是取v12的地址,*v12++的性质很像数组每次添加元素,那我就跳转到其内存地址观察三次if操作的变化:

结果就是将4个输入的字符串的6位二进制字符串分割成三份。虽然看不懂代码咋实现三等份分割效果的,但是通过动调结果能知道函数作用。

在这里插入图片描述



2.关键函数的末尾并没有对a2(传入的v13)进行操作or返回,但是最后确实v13是该函数变化后的值

在这里插入图片描述

解决:

a2是v13传入的形参,函数开头介绍了其值储存在ecx里

在这里插入图片描述

动调观察ecx,每一次if操作都将分割后的数据储存到ecx中:

在这里插入图片描述

动调观察汇编:

            这里随便输入测试,此时ebx是 0xB6399C
.text:00561139 mov     ecx, ebx  //ebx存储输入的4字符对表转换后的值
.text:0056113B shr     ecx, 10h  //10h为 01 0000 shr向右位移,
                                 //这里是ecx右移16进制的4位(取 B6)
.text:0056113E mov     [esi], cl //cl取ecx的低位,仍然是B6存入esi
.text:00561140 inc     esi       //inc +1指令,
                                 //奇怪的是这里执行完esi清0
.text:00561141
.text:00561141 loc_561141:                             ; CODE XREF: sub_561000+137↑j
.text:00561141 cmp     edx, 1    //这里是if判断
.text:00561144 jbe     short loc_56114E
.text:00561146 mov     ecx, ebx  //故技重施,将转换后的值全给ecx
.text:00561148 shr     ecx, 8    //ecx右移十六进制的两位,剩B639
.text:0056114B mov     [esi], cl //取ecx低位,是39
.text:0056114D inc     esi
.text:0056114E
.text:0056114E loc_56114E:                             ; CODE XREF: sub_561000+144↑j
.text:0056114E cmp     edx, 2
.text:00561151 jbe     short loc_561156
.text:00561153 mov     [esi], bl
.text:00561155 inc     esi

3.看汇编解决了四个字符串是如何变成三个的,但是又有新问题出现:每次esi进行inc +1操作后为啥都清零了…… 保存的值又存在哪里,v13是ecx寄存器,但是在函数结束前只有ebx完整保存着未分割前的值,既不知道谁调用了ebx,也不知道分割后的值如何跑进后面的v13

4.最后答案是用base64,但base64 表从头到尾都没看到过,唯一一个根据表操作的表还被隐藏了,看不出来是不是base64……

在这里插入图片描述

其它补充

SHR SHL SAR:

SAR:算数右移(可用作有符号除法)
	用符号位补空位
		eg:1000 0000 算术右移一位: 1100 0000
SHR:逻辑右移
	用0补空位,原最低位移入进位标志CF
           格式:SHR cnt     其中cnt可以是数字(二进制移动位数等于cnt的十进制数值);cnt也可以是cl。
		eg:1000 0000 逻辑右移一位: 0100 0000
SHL:逻辑左移
基本同上
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值