We’re currently working on making Go binaries easier to understand using our ultra-fast Carbon disassembler. In the upcoming weeks we’ll be posting progress updates.
Let’s start with a basic hello world example.
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
Without additional logic, functions name are not available.
We can see the referenced string, although it is not null-terminated, so that we don’t know its length.
The ‘main.main’ function is not treated as a function and even if we define it as such by pressing ‘P’, the decompiled result is hardly intelligible.
void __stdcall sub_47D7B0(void)
{
uint32_t *puVar1;
int32_t in_FS_OFFSET;
undefined32 uStack8;
undefined32 uStack4;
while (puVar1 = (uint32_t *)(**(int32_t **)(in_FS_OFFSET + 0x14) + 8),
*(BADSPACEBASE **)0x10 < (undefined *)*puVar1 ||
(undefined *)*(BADSPACEBASE **)0x10 == (undefined *)*puVar1) {
uStack4 = 0x47D823;
sub_446900();
}
uStack8 = 0x491320;
uStack4 = 0x4BC178;
sub_478270(0x4BCEC0, *(undefined32 *)0x532A8C, &uStack8, 1, 1);
return;
}
So the first step is recognizing functions and retrieving their names.
void __stdcall main.main(void)
{
uint32_t *puVar1;
int32_t in_FS_OFFSET;
undefined32 uStack8;
undefined *puStack4;
while (puVar1 = (uint32_t *)(**(int32_t **)(in_FS_OFFSET + 0x14) + 8),
*(BADSPACEBASE **)0x10 < (undefined *)*puVar1 ||
(undefined *)*(BADSPACEBASE **)0x10 == (undefined *)*puVar1) {
puStack4 = &main.main;
runtime.morestack_noctxt();
}
uStack8 = 0x491320;
puStack4 = (undefined *)0x4BC178;
fmt.Fprintln(0x4BCEC0, *(undefined32 *)0x532A8C, &uStack8, 1, 1);
return;
While it's still not easy to read, we can grasp a bit more of its meaning.
To be continued...