一道中等难度的Reverse题,由蓝鲸安全平台提供。
题目:move! move! 答案格式:ALEXCTF{XXX} 地址:https://pan.baidu.com/s/1Ax3ppUOxVdwpBapugGlXfw
知识点:upx解压缩,pin—字节写入追踪
目录 题目解析 总结
题目解析 使用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
观察输入数据对挑战文件数据的影响
发现导出的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, digitsimport osallChars = 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针工具。