bart1e 正确回答了您的问题,这只是他的回答的扩展,使用不同的工具ghidra并在不运行二进制文件的情况下完全静态地反转。
golang 使用不同的方法来调用函数,它在堆栈中提供一个内存槽用于函数的参数以及它可以从函数返回的多个值
例如,一个函数可以在同一个函数中返回两个整数的和和乘积,例如
func doMagic (x,y) { return x+y ,x*y )
所以粗略地反汇编看起来像
(它只是细节的简化,而不是绝对语法或使用确切类型)
它只是试图重申 golang 如何使用堆栈
而不是传统的x64 abi 寄存器或 x86 abi 内存/堆栈作为参数和rax/eax 中的常规
返回
mov [stack] , x
mov [stack] , y
mov [stack] , &fret1
mov [stack] , &fret2
call doMagic
and inside doMagic
t1 = x
t2 = y
t3 = t1 + t2
t4 = t1 * t2
fret1 = t3
fret2 = t4
ret
所以阅读我发现golang在.gopclntab部分中嵌入了函数名称及其地址,并且正在重命名函数并使用下面的脚本成功重命名函数但反编译仍然看起来很糟糕
from ghidra import *
mem = currentProgram.getMemory()
start = mem.getBlock(".gopclntab").getStart()
provider = ghidra.app.util.bin.MemoryByteProvider(mem,start)
bread = ghidra.app.util.bin.BinaryReader(provider,True)
numfun = bread.readInt(8)
for i in range(0x10,numfun*0x10,0x10):
faddr = bread.readInt(bread.readInt(i+8))
fname = bread.readAsciiString(bread.readInt(bread.readInt(i+8) + 8))
print( "creating function at " + hex(faddr) + "\t" + fname )
removeFunctionAt(toAddr(faddr))
createFunction(toAddr(faddr),fname)
所以我四处寻找并找到了felberj 的一个 GitHub 条目,一个用于 golang 的 ghidra 脚本
下载了 9.04 版本的脚本,按照项目窗口中的指示将其复制到 ghidra/extension 目录执行 File->installExtension 并重新启动 ghidra
现在我这次再次导入了二进制文件,确保我使用了新添加的 golang 语言条目而不是默认的 gcc x64 ghidra 建议
除了重命名函数之外,此扩展还解码了返回类型并将参数的存储类型更改为自定义存储,并为所有参数和返回值分配了适当的堆栈地址。
现在 main.main 的反编译看起来更具可读性
void main.main(void)
{
byte Wrong [17];
byte EPas [20];
byte good [30];
byte Ans [38];
byte retry [40];
ppbVar1 = *(in_FS_OFFSET + 0xfffffff8) + 0x10;
if (good + 4 < *ppbVar1 || good + 4 == *ppbVar1) {
runtime.morestack_noctxt();
main.main();
return;
}
EPas._0_8_ = 0xc5d38ad8cfdec4ef;EPas._8_4_ = 0xda8ad8df;EPas._12_4_ = 0xddd9d9cb;
EPas._16_4_ = 0x90ced8c5;
Wrong[0] = 0xf9; Wrong._1_4_ = 0xc2dec7c5;Wrong._5_4_ = 0x8acdc4c3;Wrong._9_4_ = 0xdd8ad9c3;
Wrong._13_4_ = 0xcdc4c5d8;
Ans._0_6_ = 0xd9cfcec3f9e8; Ans._6_2_ = 0xe6fe; Ans._8_2_ = 0xd1fc; Ans._10_4_ = 0xcfdccfd8;
Ans._14_4_ = 0x8acfcdc4; Ans._18_4_ = 0xc88ad9c3; Ans._22_4_ = 0x8aded9cf; Ans._26_4_ = 0xdcd8cfd9;
Ans._30_4_ = 0xc98acecf; Ans._34_4_ = 0xd7cec6c5;
good._0_4_ = 0x8adfc5f3; good._4_4_ = 0xc9cbd8e9; good._8_4_ = 0x8acecfc1; good._12_2_ = 0xdec3;
good._14_2_ = 0x8a86; good._16_2_ = 0x8aeb; good._18_4_ = 0xc5d8cfe2; good._22_4_ = 0x8ad9c38a;
good._26_4_ = 0xc4d8c5c8;
retry._0_8_ = 0xc5fd8ade8dc4c5ee; retry._8_4_ = 0x86d3d8d8; retry._12_4_ = 0xc6cff88a;
retry._16_4_ = 0x8a86d2cb; retry._20_4_ = 0xc6c3c2e9; retry._24_4_ = 0xc4cb8ac6;
retry._28_4_ = 0xd8fe8ace; retry._32_4_ = 0xcbc28ad3; retry._36_4_ = 0xd8cfced8;
uStack224 = 0x14;
rVar3 = main.ObfStr(EPas,0x14,0x14);
uStack0000000000000020 = SUB168(rVar3,0);
uStack0000000000000028 = SUB168(rVar3 >> 0x40,0);
uStack0000000000000018 = runtime.convTstring(in_stack_fffffffffffffe10,in_stack_fffffffffffffe18);
pdStack232 = &DAT_0049a600;
puVar7 = 0x1;
lVar9 = 1;
fmt.Fprint(&PTR_DAT_004d3a60,DAT_0055b7f0,&pdStack232,1,1);
uStack152 = DAT_0055b7e8;
apuStack96[0] = 0x0;
FUN_00451d15();
lVar5 = 0x1000;
lVar6 = 0x1000;
runtime.makeslice(&DAT_0049a740,0x1000,0x1000);
puStack184 = 0x0;
puVar8 = puVar7;
FUN_00451d15();
uStack176 = 0x1000;
uStack168 = 0x1000;
ppuStack160 = &PTR_DAT_004d3a40;
uStack112 = 0xffffffffffffffff;
uStack104 = 0xffffffffffffffff;
puStack184 = puVar7;
apuStack96[0] = puVar7;
FUN_0045207a();
rVar2 = bufio.(*Reader).ReadLine(apuStack96);
uStack0000000000000010 = SUB488(rVar2,0);
uStack0000000000000018 = SUB488(rVar2 >> 0x40,0);
uStack0000000000000020 = SUB488(rVar2 >> 0x80,0);
uStack0000000000000028 = SUB488(rVar2 >> 0xc0,0);
if (lStack480 != 0) {
uStack208 = 0x11;
rVar3 = main.ObfStr(Wrong,0x11,0x11);
uStack0000000000000020 = SUB168(rVar3,0);
uStack0000000000000028 = SUB168(rVar3 >> 0x40,0);
uStack0000000000000018 = runtime.convTstring(puVar8,lVar9);
if (lStack480 != 0) {
lStack480 = *(lStack480 + 8);
}
pdStack216 = &DAT_0049a600;
puVar8 = 0x2;
lVar9 = 2;
lStack200 = lStack480;
rVar4 = fmt.Fprintln(&PTR_DAT_004d3a60,DAT_0055b7f0,&pdStack216,2,2);
uStack0000000000000040 = SUB248(rVar4 >> 0x80,0);
}
rVar3 = main.ObfStr(Ans,0x26,0x26);
uStack0000000000000020 = SUB168(rVar3,0);
uStack0000000000000028 = SUB168(rVar3 >> 0x40,0);
if ((lVar9 == lVar6) &&
(uStack0000000000000020 = runtime.memequal(lVar5,puVar8,lVar6), puVar8 != '\0')) {
uStack240 = 0x1e;
rVar3 = main.ObfStr(good,0x1e,0x1e);
uStack0000000000000020 = SUB168(rVar3,0);
uStack0000000000000028 = SUB168(rVar3 >> 0x40,0);
uStack0000000000000018 = runtime.convTstring(puVar8,lVar9);
pdStack248 = &DAT_0049a600;
fmt.Fprintln(&PTR_DAT_004d3a60,DAT_0055b7f0,&pdStack248,1,1);
return;
}
uStack256 = 0x28;
rVar3 = main.ObfStr(retry,0x28,0x28);
uStack0000000000000020 = SUB168(rVar3,0);
uStack0000000000000028 = SUB168(rVar3 >> 0x40,0);
uStack0000000000000018 = runtime.convTstring(puVar8,lVar9);
pdStack264 = &DAT_0049a600;
fmt.Fprintln(&PTR_DAT_004d3a60,DAT_0055b7f0,&pdStack264,1,1);
return;
}
所以你可以看到它打印了一个字符串输入密码读取一行将输入的密码与一个混淆的(带有字节 0xaa 的简单异或)实际密码进行比较并打印好,你是英雄或打印错误重试
反混淆例程是 main.obfstr() 它需要一个字节数组和长度到异或并返回一个异或字符串
基于这些观察,我们可以编写一个简单的异或脚本
将字节地址与 xor 和 length 和 xor 的结果进行异或以在不运行二进制文件的情况下获得通行证
这是天真的异或脚本(天真,因为有一次我在我的机器上测试时它为我运行,并且可能有无数无法预料的极端情况错误)
#Desc xors a memory block of len unsigned bytes with a single unsigend byte like (0xff ^ 0xaa)
#@author blabb
#@category _NEW_
#@keybinding
#@menupath none
#@toolbar
import ghidra
def hexdump( a ):
for j in range(0,len(a),16):
for i in range(j,j+16,1):
if( i < len(a)):
print ( "%02x " % a[i]),
else:
print ( "%02x " % 0 ),
for i in range(j,j+16,1):
if( i < len(a)):
print ( "%c" % chr( a[i] ) ),
else:
print ( " " ),
print("\n")
baseaddr = askAddress( "XOR MEMORY","Enter Base Addreess")
xorby = askInt ( "XOR MEMORY","Enter Byte to xor with")
xorlen = askInt ( "XOR MEMORY","enter length of xor block")
res = []
provider = ghidra.app.util.bin.MemoryByteProvider(currentProgram.getMemory(),baseaddr)
br = ghidra.app.util.bin.BinaryReader(provider,True)
for i in range(0,xorlen,1):
res.append((br.readUnsignedByte(i) ^ xorby))
hexdump(res)
运行它并提供密码 0x4d3ae0,xorbyte 0xaa,len 0x26 的内存地址
我们可以轻松获得密码
xormem.py> Finished!
xormem.py> Running...
42 53 69 64 65 73 54 4c 56 7b 72 65 76 65 6e 67 B S i d e s T L V { r e v e n g
65 20 69 73 20 62 65 73 74 20 73 65 72 76 65 64 e i s b e s t s e r v e d
20 63 6f 6c 64 7d 00 00 00 00 00 00 00 00 00 00 c o l d }
xormem.py> Finished!