# 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(" |