对了, 我写的东西一般会比较放飞自我, 如果要严谨学习的话最好就别参考我的writeup了, 毕竟编程这种东西我懂不了一点.
介绍
flag格式:DUTCTF{xxxx}
使用工具
retdec
blackarch不知道是什么原因配置不上retdec,最后还是直接在github下载,不得不说这是个很强大的反编译工具,相比r2确实能少死很多脑细胞.(当然retdec也存在自己的问题,之前打codegate时电脑差点原地爆炸)
配置方法
主要使用的是retdec-decompiler工具(linux环境下,windows可能会在后面多一个.exe),二进制文件可在gitee获取,希望大家可以顺便关照以下我自己的仓库,我自己也写过一些有意思的东西,能给点star就更好了.
使用
可以通过官方wiki学习相关的使用教程. 我还在缓慢翻译部分文档, 其中可以注意一下Decompiler-outputs文件, 这里我只针对要用到的进行引用:
input.exe.c
: The decompiled C code. This is the main output.input.exe.ll
: Human-readable disassembly of LLVM bitcode in the LLVM IR format.
然后是个人的随缘翻译:
input.exe.c
:反编译的C代码, 这是反编译的核心文件input.exe.ll
:用人能看懂的方式反编译LLVM中间码。
程序
> retdec re
> ls
re re.bc re.c re.config.json re.dsm re.ll
本次主要观察re.c
与re.ll
文件:
re.c
主要观察的反编译文件,好像有人测试过可以用gcc强制运行部分反编译过的文件.主要内容如下:
在全局变量中定义g1, g2两组字符串,还有g3, g4两个整形变量
// --------------------- Global Variables ---------------------
char * g1 = "\xd5\xe2\xca\xc7\xd2\xbb\xb5\xc0\xba\xdc\xbf\xc9\xb0\xae\xba\xdc\xbc\xf2\xb5\xa5\xb5\xc4\xc4\xe6\xcf\xf2\xcc\xe2\xdf\xcf\n"; // 0x413e60
char * g2 = "\xca\xe4\xc8\xeb\x66lag\xb0\xc9:"; // 0x413e80
int32_t g3 = 0x67616c66; // 0x413e90
int32_t g4 = 0x67616c66; // 0x413e9c
进入主函数, 使用__asm_movdqu_2和__asm_movq_3等汇编指令初始化变量,
// ------------------------ Functions -------------------------
// Address range: 0x401000 - 0x4010c2
int main(int argc, char ** argv) {
int32_t v1 = __asm_movdqu_2(__asm_movdqu(0x3074656d30633165577b465443545544)); // bp-72, 0x40101f
int64_t v2; // 0x401000
__asm_movq_3(v2, __asm_movq(0x7d465443545544));
打印g1、g2, 将输入值赋予v3 -> v4, v1-> v5, v6赋予v5指针, g4地址指针什么v7阿巴阿巴, 这些乱七八糟的不想理了, 差不多知道大概意思就好.
_printf(NULL);
_printf((char *)&g1);
_printf((char *)&g2);
int32_t v3; // bp-40, 0x401000
_scanf("%s", &v3);
int32_t v4 = &v3; // 0x401065
int32_t v5 = &v1; // 0x401065
char v6 = *(char *)v5; // 0x401068
char * v7 = (char *)&g4; // 0x40106c
快乐while循环, 如果v6(即v5指向的字符) 等于v4循环, 反之break. 将v7设为g3, 判断v6是否为0, 是则跳出循环;接着获取v5和v4后一个字符, 将其赋值给v8, 再次将v7设为g4;最后判断v8是否等于v4后一个字符,如果不相等则跳出循环。 人话就是让输入的v3(v4)等于v6(v1).
while (v6 == *(char *)v4) {
// 0x40106e
v7 = (char *)&g3;
if (v6 == 0) {
// break -> 0x40108d
}
char v8 = *(char *)(v5 | 1); // 0x401072
v7 = (char *)&g4;
if (v8 != *(char *)(v4 || 1)) {
// break -> 0x40108d
break;
}
// 0x40107a
v4 += 2;
v5 += 2;
v7 = (char *)&g3;
if (v8 == 0) {
// break -> 0x40108d
break;
}
v6 = *(char *)v5;
v7 = (char *)&g4;
}
输出v7指向的地址,并调用_system函数使程序暂停。
// 0x40108d
_printf(v7);
_system("pause");
return ___report_gsfailure();
}
小插曲
其实如果有了解radare2的话可以试试用如下指令查看汇编指令, arch系用户可直接通过sudo pacman -S radare2
进行安装使用:
> r2 re
[0x004014a5]> aa
INFO: Analyze all flags starting with sym. and entry0 (aa)
INFO: Analyze all functions arguments/locals (afva@@@F)
[0x004014a5]> afl
0x004014a5 23 304 entry0
0x00401000 11 194 main
[0x004014a5]> pdc @ main
可以发现其实已经有指向我们要找的关键词DUTCTF大概在0x413e44位置了. 详细不过多介绍, 感兴趣可以自己试着折腾一下.
re.ll
现在来re.ll文件找while循环到底判定了个什么, 答案很明显在第2行, 不过从个人角度还是想安利一下vim编辑器, 打开快, 编写容易, 只需要一点学习成本, 便可以让自己在未来的很多内容中省掉很多不必要的操作.
由题目已知格式为BUTCTF{},在vim编辑器下可以使用:/BUTCTF
直接定位到所在行.
@global_var_415000 = local_unnamed_addr global i32 -1153374642
@global_var_413e34 = local_unnamed_addr constant [24 x i8] c"DUTCTF{We1c0met0DUTCTF}\00"
@global_var_413e44 = local_unnamed_addr constant [8 x i8] c"DUTCTF}\00"
@global_var_413e8c = constant [3 x i8] c"%s\00"
@global_var_413e90 = constant i32 1734437990
@global_var_413e9c = constant i32 1734437990
......
!21 = !{i64 4198528}
!22 = !{i64 4198530}
!23 = !{i64 4198557}
!24 = !{i64 4198570}
!25 = !{i64 4198585}
!26 = !{i64 4198593}
回代
打开程序, 是个用来给我们检验flag的小程序, 把拿到的flag带进去试试: