Go
Go 语言逆向工程,指的是针对 Go 语言编译出的二进制程序进行分析,以理解其内部逻辑、数据结构和工作原理的过程
逆向 Go 程序时,经常会遇到以下几个 “拦路虎”:
巨大的二进制体积:Go 默认采用静态编译,会将运行时(runtime)、垃圾回收(GC)和所有依赖库都打包进一个文件。这使得文件体积通常很大(动辄十几MB),在反汇编工具中打开时,可能会看到成千上万个函数
特殊的调用约定:Go 的函数调用约定与传统的 C/C++ 不同。例如,它支持多返回值,在 x86-64 架构上可能通过多个寄存器来传递返回值,这很容易让传统的反编译工具 “懵圈”
独特的数据结构:Go 语言的数据结构在底层有独特的实现方式。最典型的是字符串,它并非以空字符
\0结尾的 C 字符串,而是由(起始地址, 长度)两个元素组成的结构体复杂的栈管理:Go 的栈管理由其运行时(runtime)负责,与操作系统线程的栈不同。其栈空间可以动态增长(例如从初始的 2KB 开始),并且函数参数的传递和返回值的存储在栈上也有自己的一套规则
实战案例
go(Go + Base64 换表)

Go 语言编写的程序,没有去符号,IDA 正常反编译

太难看了,我们取重点部分
1 | flag.str = "cbdb2c89f6800e6c93e1c1e541e1a89758f45fd988c6652fa955db2f00290da272454969d57b828ca80bd146ebe8c89d"; |
关键校验在这里
1 | *&typ[16] = main_encode(*typ); |
还原成 Go 代码就是
1 | // 对输入数据进行自定义编码,得到编码后的字符串 |
通过校验后,用输入当 AES key 解密
1 | memset(&coder, 0, sizeof(coder)); |
继续看 main.encode

很明显这里是 Base64 换表
1 | XYZFGHI2+/Jhi345jklmEnopuvwqrABCDKL6789abMNWcdefgstOPQRSTUVxyz01 |
编写脚本
1 | import base64 |
运行后得到
1 | b'V2hhdF9pc19nb19hX0FfSA==' |
接下来只需要继续分析 main.(*gogo).Decode,验证它如何用这个 key 解密

这里很明显可以看到使用的是 AES 加密

这里要求 IV 的长度必须是 16,而 What_is_go_a_A_H 刚好是 16

这里能看到使用的是 CBC 模式

成功拿到 flag
