一道中等难度的Reverse题,由蓝鲸安全平台提供。

题目:move! move! 答案格式:ALEXCTF{XXX} 地址:https://pan.baidu.com/s/1Ax3ppUOxVdwpBapugGlXfw

知识点:upx解压缩,pin—字节写入追踪

目录

  1. 题目解析
  2. 总结

题目解析

使用File命令和DIE查看文件属性:发现是32位可执行程序,并且已经被UPX打包了。

于是使用upx解压缩(-d指令),再用DIE查看脱壳成功。

然后使用IDA进行分析,发现由于帧栈太大无法反编译,并且发现汇编指令几乎都是mov指令!

搜索发现,这是通过movfuscator工具编写的地址:https://github.com/xoreaxeaxeax/movfuscator。

然而并没有很好的反编译方法,不过根据http://wiki.yobi.be/wiki/MoVfuscator_Writeup,找到了一些解题思路,并且其中有一个跟踪程序:`tracer.cpp`

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include "pin.H"
#include <fstream>

std::ofstream TraceFile;
PIN_LOCK lock;
ADDRINT main_begin;
ADDRINT main_end;

static ADDRINT WriteAddr;
static INT32 WriteSize;

static VOID RecordWriteAddrSize(ADDRINT addr, INT32 size)
{
WriteAddr = addr;
WriteSize = size;
}

static VOID RecordMemWrite(ADDRINT ip)
{
UINT8 memdump[256];
PIN_GetLock(&lock, ip);
PIN_SafeCopy(memdump, (void *)WriteAddr, WriteSize);
if (WriteSize==1)
TraceFile << static_cast<CHAR>(*memdump);
PIN_ReleaseLock(&lock);
}

VOID Instruction_cb(INS ins, VOID *v)
{
ADDRINT ip = INS_Address(ins);
if ((ip < main_begin) || (ip > main_end))
return;

if (INS_IsMemoryWrite(ins))
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)RecordWriteAddrSize,
IARG_MEMORYWRITE_EA,
IARG_MEMORYWRITE_SIZE,
IARG_END);
if (INS_HasFallThrough(ins))
{
INS_InsertCall(
ins, IPOINT_AFTER, (AFUNPTR)RecordMemWrite,
IARG_INST_PTR,
IARG_END);
}
}
}

void ImageLoad_cb(IMG Img, void *v)
{
PIN_GetLock(&lock, 0);
if(IMG_IsMainExecutable(Img))
{
main_begin = IMG_LowAddress(Img);
main_end = IMG_HighAddress(Img);
}
PIN_ReleaseLock(&lock);
}

VOID Fini(INT32 code, VOID *v)
{
TraceFile.close();
}

int main(int argc, char *argv[])
{
PIN_InitSymbols();
PIN_Init(argc,argv);
TraceFile.open("trace-1byte-writes.bin");
if(TraceFile == NULL)
return -1;
IMG_AddInstrumentFunction(ImageLoad_cb, 0);
INS_AddInstrumentFunction(Instruction_cb, 0);
PIN_AddFiniFunction(Fini, 0);
PIN_StartProgram();
return 0;
}

方法:可以使用pin进行1字节内存写入,并且编译使用tracer工具进行逐字节的猜测flag。

  • 首先将tracer.cpp文件放到/source/tools/ManualExamples目录下。
  • 然后使用命令:make obj-ia32/tracer.so TARGET=ia32编译工具。
  • 接着我们先尝试一个空字节写入程序并查壳命令:(echo "" | ../../../pin -t obj-ia32/tracer.so -- ./move); xxd trace-1byte-writes.bin
  • 观察输入数据对挑战文件数据的影响

  • 将flag格式逐个测试,首先是A

  • 接着是AL

  • 发现导出的1字节追踪有变化的部分,01010000好像对应了flag不正确的字节,而00000100对应了flag正确的部分。
  • 所以可以创建这样一个脚本利用tracer工具追踪爆破获取flag
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from string import ascii_lowercase, digits
import os

allChars = digits + '_}' + ascii_lowercase

flag = 'ALEXCTF{'
wrong = '\x01\x01\x00\x00'
right = '\x00\x00\x01\x00'
case = '\x00\x00\x00\x00'

def tryFlag(f):
os.system('(echo "{}" | ../../../pin -t obj-ia32/tracer.so -- ./move) > /dev/null'.format(f))
data = open('trace-1byte-writes.bin', 'rb').read()
offset = len(f) * 4
return data[offset - 4:offset]

while flag[:-1] != '}':
for c in allChars:
result = tryFlag(flag + c)
if result == case:
c = c.upper()
result = tryFlag(flag + c)

if result == right:
flag += c
print flag
break
  • 经过一段时间的爆破获取了flag:ALEXCTF{M0Vfusc4t0r_w0rk5_l1ke_m4g1c}

总结

通过这道题,我们学会了以movfuscator混淆程序的由来以及如何追踪破解,并且初次接触PIN针工具。