The entry for go programs is main.main
. In binary ninja, we get this for main:
This first block, will check if there is enough RAM to load the program. If so, then we go to the right-block.
The right block will initialize Go Channels, five of them, aswell as print a
>
and ask for user input with scanf.
Then, a simple flag length check is done before progressing forwards
Then, a series of Goroutines are called. A goroutine will have this structure of checking write barriers before every call.
At the very end, rbx recieves data from a channel, which is then compared to be equal to 0. If they are equal, then we get the winner flag.
So, in essence, the very last goroutine we call which is
main.main.func3
will determine if we win or not.
Again, there is the check for sufficient RAM for the stack, we can just ignore that.
Notice that there is a series of assignments to the stack. 24 in fact, perfect with the length of our flag. Lets call this list
f3list
The following loop will check iterate through all characters from 1-24 and do the following:
f3list[counter]
ourinput[counter]
, and send this to a channel that func2 listens to, and recieve that outputI used Delve to actually see which function was listening to which channel, since GDB had issues with concurrency.
firstecx = [0xcc, 0x02, 0x84,0xf2,0x1a, 0x42, 0xb1, 0x3e, 0x82, 0x5d, 0x30, 0x44, 0x12, 0x51, 0x6c, 0x6f, 0x2d, 0x4c, 0x51, 0xc1, 0x57, 0x47, 0xf,0xac]
lastecx = [0x5c, 0xef, 0x33, 0xd, 0x92, 0xd6, 0xc4, 0x58, 0xac, 0x23, 0x19, 0xf6, 0x76, 0x4, 0x25, 0x77, 0xd0, 0xdb, 0x1f, 0x90, 0x93, 0x3c, 0x90,0xf5]
func2 = [0xbe, 0xd6, 0x9b, 0xc4, 0xa6, 0xef, 0x12, 0x09, 0x46, 0x4d,0x44,0x83,0x05,0x3b,0x16,0x6a,0x95,0xa3,0x3e,0x64,0xf4,0x1f,0xe6,0x24]
listlen = len(firstecx)
for i in range(listlen):
print(chr(firstecx[i] ^ func2[i] ^ lastecx[i]),end="")
for i in range(24-listlen):
print("a",end="")
print("")