# CISCN_2022 planecoode

这个题目算是一个 llvm 的题目,对于我们输入的 code 进行解析操作,但是逆向的工程量很大,设计了很多 if,else 的模块,对应众多的操作。而且并不是说常规 vm 那种可以解析出常见汇编指令格式的操作码。而且使用了很多数据的转换操作,比如 BYTE1~BYTE5,HIBYTE,LOBYTE 等等,这些东西是我们必须回的东西。

然后经过我长时间的解析,大概猜出来了流程,我们指定最开始的 xy, 这是边界限制,申请出来 8*x*y 字节的空间,然后对应的 (x,y) 的位置是一个 code,code 为 8 字节 64 位,储存在上述空间,然后开辟一个 x*y 大小的 stack,用来储存数据。

下面的就是对于 code 解析处理,我们看到每次处理都是以 8 字节位单位,根据 x,y 来取指令,我们最简单的无疑是顺序取指。如何实现顺序取指?我们看看取指令的逻辑

__int64 entry()
{
  __int64 result; // rax
  void *vale; // [rsp+0h] [rbp-10h]
  init_SIZE();                                  // 确定 XY 的范围
  map = (__int64 **)init_map(size_X, size_Y);
  set_code_data();                              // 填入 code 数据
  sub_E91();                                    // 选择初始位置
  sub_1583();                                   // 模拟栈空间
  do
  {
    vale = (void *)get_value_fromXY((__int64)map, size_X, size_Y, x_temp, y_temp);		// 根据 xy 来取指
    result = operation(vale);                   // 根据我们输入的 code 进行操作
  }
  while ( result );
  return result;
}

这里是函数的主入口,我们重点关注取指令跟 x,y 的关系

__int64 __fastcall get_value_fromXY(__int64 map, unsigned int XN, unsigned int YN, unsigned int x, unsigned int y)
{
  __int64 result; // rax
  __int64 pos; // [rsp+28h] [rbp-8h]
  pos = get_pos(map, XN, YN, x, y);
  if ( pos )
    result = get_code(pos);					//return *(_QWORD *)a1;
  else										
    result = 0LL;
  return result;
}
__int64 __fastcall get_pos(__int64 map, unsigned int XN, unsigned int YN, unsigned int x, unsigned int y)
{
  __int64 result; // rax
  __int64 pos; // [rsp+28h] [rbp-8h]
  pos = check(XN, YN, x, y);
  if ( pos == -1 )
    result = 0LL;
  else
    result = 8 * pos + map;
  return result;
}
__int64 __fastcall check(unsigned int XN, unsigned int YN, unsigned int x, unsigned int y)
{
  __int64 result; // rax
  if ( x < XN && y < YN )
    result = XN * y + x;
  else
    result = -1LL;
  return result;
}

最内部是对我们提供的用来定位的 xy 的检查,然后 XN 是我们最开始输入的 x 的范围,因为我们确定了我们要顺序取指,所以,我这里直接将 YN 设置 1,将一个 2 维矩阵简化为以为的顺序,然后我们提供的初始位置都是从 0 开始,(0,0)并且下次取指只要对 x 进行加 1 的操作,y 一直为 0,这样从原来的 code [y][x] 直接简化为 code [x]。但是本质没有差别,知识实现了代码操作的简化。我们填入的 code,每次都会通过 strtoul (s, 0LL, 10) 转化为无符号的整数,储存在 64 位的空间中,这也是为什么我们 code 的空间是 8*x*y。

接下来就是对 code 的解析,这一部分是最恶心的,太多种情况了

__int64 result; // rax
  int n; // [rsp+1Ch] [rbp-24h]
  void *buf; // [rsp+28h] [rbp-18h]
  __int64 s; // [rsp+30h] [rbp-10h] OVERLAPPED BYREF
  __int64 canary; // [rsp+38h] [rbp-8h]
  canary = __readfsqword(0x28u);
  memset(&s, 0, sizeof(s));
  s = code;
  if ( (unsigned __int8)code == 0x54 )          // 输出栈顶数据一字节
  {
    printf("