HITCTF 2020 reverse1 HelloReverse Writeup

folder_open笔记
comment没有评论

本站遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0),原创文章转载请注明出处。

题目

初步分析

使用 Exeinfo PE 软件对其进行分析:

Exeinfo PE 初步分析

得出信息:此为 C++编写的 32 位可执行文件,类型为控制台应用程序,使用 MSVC 编译器编译,并且没有加壳。

执行该程序,界面如下:

目标界面

推测基本逻辑为判断所输入字符串经过一定转换后是否与 flag 相同。

逆向开始

使用 IDA Pro 32 位程序对此程序进行分析,按下 F5 得到其 C 的伪代码:

IDA 得到的 C 伪代码

通过简单分析验证了之前的推测。为了方便,对其中已明确用途的变量和函数重新指定类型和名称:

整理后的伪代码

如此一来,整个程序的流程便已清晰明了:

  1. 首先对输入的字符串(即 Str1)长度进行判断,若长度不等于 29,直接退出程序。由此我们推测,flag 可能是 29 位的字符串。
  2. 然后将 char*类型的指针变量指向 Str1,即 Str1 的第一个字符。计算出 Str2 和 Str1 的地址偏移量 offset。定义计数变量 i 的值为 29。
  3. 暂且跳过循环的部分。从程序结尾部分看出,程序将 Str1 与 Str2 进行了比较,若结果相同便会输出 「Congratulations」。这里的 29 再次印证了之前的推测。

容易想到,flag 极有可能藏在 Str2 中,但 Str2 显然并未在此处定义。为了找出 Str2,我们使用 IDA 的 「跳转至交叉引用」 功能。右击 Str2:

「跳转至交叉引用」

点击 「Jump to xref…」,IDA 将会为我们显示出所有引用了该变量的位置。

Str2 的交叉引用

可以看出,Str2 在 sub_401000 这个函数中出现了两次,点击 「OK」 以跳转至该函数:

sub_401000 子函数

由之前的分析,显然该 result 变量存储的应该就是伪装后的 flag。flag 由 29 个字符组成,而这里的 result 数组有 7 个元素,4×7=28,加上最后赋值操作的元素刚好是 29。为了验证推测,我们将其数据以 16 进制显示:

整理后的 sub_401000

以字节计数,刚好为 29 个字节。

似乎这就是我们需要的结果。然而将其转换后却只能得到一堆毫无意义的字符,经验证也并不是我们寻找的 flag,推测这段数据被程序进行了某种加密。

回到 main 函数的循环部分:

main()

分析第一次循环得出,该循环将 Str1 中字符逐个与其下一个字符进行异或操作,并重新赋值给当前字符,完成了对 Str1 的变换。

下面这条语句看起来这似乎并没有涉及到 Str2 的操作,其实不然。我们知道,在 C 中表达式 p[x] 与*(p+x) 的返回值相同,那么 character[offset-1] 的返回值与*(character+offset-1) 的返回值显然也相同。在循环体的第一条语句中,character 已经自增 1,那么此处一定存在真值表达式:*(character+offset-1)==*(Str1+offset)==*Str2。

也就是说,这里操作的对象实际上是 Str2。这条语句将 Str2 中的当前字符重新赋值为 __ROL1__ 函数的返回值。那么 __ROL1__ 是个什么函数?分析汇编:

汇编代码

得出结论,这里的 __ROL1__ 实际上就是汇编中的 rol 循环左移指令。在 IDA 安装目录下的 plugins 文件夹内,我们可以找到文件名为 defs.h 的头文件,其中就有对 __ROL1__ 函数的声明和定义:

// rotate left
template<class T> T __ROL__(T value, int count)
{
  const uint nbits = sizeof(T) * 8;

  if ( count > 0 )
  {
    count %= nbits;
    T high = value >> (nbits - count);
    if ( T(-1) < 0 ) // signed value
      high &= ~((T(-1) << count));
    value <<= count;
    value |= high;
  }
  else
  {
    count = -count % nbits;
    T low = value << (nbits - count);
    value >>= count;
    value |= low;
  }
  return value;
}

inline uint8  __ROL1__(uint8  value, int count) { return __ROL__((uint8)value, count); }
inline uint16 __ROL2__(uint16 value, int count) { return __ROL__((uint16)value, count); }
inline uint32 __ROL4__(uint32 value, int count) { return __ROL__((uint32)value, count); }
inline uint64 __ROL8__(uint64 value, int count) { return __ROL__((uint64)value, count); }
inline uint8  __ROR1__(uint8  value, int count) { return __ROL__((uint8)value, -count); }
inline uint16 __ROR2__(uint16 value, int count) { return __ROL__((uint16)value, -count); }
inline uint32 __ROR4__(uint32 value, int count) { return __ROL__((uint32)value, -count); }
inline uint64 __ROR8__(uint64 value, int count) { return __ROL__((uint64)value, -count); }

至此,我们已经完成了对该程序的所有分析过程。下面我们只需复现出程序源码,稍作修改便可得到 flag。

代码复现

显然,我们只需注释掉判断长度的代码,并在最后将 Str2 打印出来即可。

复现并稍作修改后的代码如下:

#include <cstdio>
#include <cstring>
#include "../defs.h"

char *Str2;

int main()
{
    int result[8u]; // eax
    result[0] = 0x7171D110;
    result[1] = 0x20204721;
    result[2] = 0x7392B420;
    result[3] = 0x12367431;
    result[4] = 0x36C0A361;
    result[5] = 0x2340A1F6;
    result[6] = 0x22E785A0;
    *((short *)result + 14) = 0xD7;
    Str2 = (char *)result;

    char Str1[256u];    // ebx
    char *character;    // edx
    int offset;         // esi
    int i;              // edi
    char nextCharacter; // al

    printf("Please Input Your Flag:\n");
    scanf("%s", Str1);
    // if (strlen(input) != 29)
    //     return -1;
    character = Str1;
    offset = Str2 - Str1;
    i = 29;
    do
    {
        nextCharacter = *++character;
        *(character - 1) ^= nextCharacter;
        character[offset - 1] = __ROL1__(character[offset - 1], 4);
        --i;
    } while (i);
    if (!strncmp(Str1, Str2, 29u))
        printf("Congratulations");

    //start
    for (int i = 28; i > 0; i--)
        Str2[i - 1] ^= Str2[i];
    printf(Str2);

    return 0;
}

运行结果:

得到 flag

EOF

浏览量: 15
标签:

相关文章

树莓派安装 OpenWrt 突破校园网限制

笔记

the cryptopals crypto challenges (Set 1)

笔记

为什么函数 f(x)=x² 不是满射

笔记

发表评论

邮箱地址不会被公开。 必填项已用*标注

填写此字段
填写此字段
请输入正确的邮箱地址。
您需要同意条款才能继续

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

菜单