πŸ“œ ⬆️ ⬇️

Facilitating the reversal of Golang binaries or why even write scripts in IDA

Golang is a great language. Strong typing, garbage collector, calling C functions via cgo, reflect, chan is just a fairy tale! Obviously not only I think so, because go is popular, which means it is used by many programmers, which means there is a high probability that sometime someone will need to reverse its binaries - this is what we are going to do now.

Training


First, take a fresh golang 1.8 under windows / amd64. For the binary, I'll take one of my crafts . Compile with go build . File size - 7256576 bytes, DWARF in place. Will not work. Most often, with the release, all unnecessary is cut out from the binary. Standard utilities like strip do not work well with go, we find in Google one of the popular options for cutting a binary: go build -ldflags "-w -s" . We look at what these flags do , find out that -w removes DWARF, and -s removes the symbol table and debugging information. We compile, we look at the file size - 4894720, well, it seems like no one promised what will be enough.

Looking for functions


We open our binary in ida and feel sad, because of the detected functions only the entrypoint. We dig a little deeper and see that ida rested against:

 lea rax, qword_452018+128h jmp rax 

Go to the address, declare the function, run the analysis - found 215 functions.
')


If you continue in this spirit, then the analysis will take a lot of time. It is time to think. We recall that reflect from the standard library allows us to call functions by their names , which means that the names of the functions, well, or at least their hashes should be stored somewhere, and we can get them to feed the idea.

We scroll to the beginning of the file in the hope of running into the pointers to the structures we need, but we meet the line Go build ID: "e07bfb8669c13efb74574c0ad220c5f2cfae5cd4" . We source golang sources, since they are available to us, and we find mention of this line. We notice a line in the linker code

 ctxt.Syms.Lookup("go.buildid", 0) 

which talks about searching in some go.buildid symbol table . We assume that we will find this line in the binary, and we find it.

We were looking for these structures.

Right above the line we see the number 401000h, which is a pointer to our line. Below we see the definitions of functions, and above - an array of pairs, in which one of the values ​​is similar to pointers. We check where these pointers point and do not miss - on the function.

Portion of an array converted to pointers

To find out what we saw above, we compile the code leaving the debug information. Open it in ida, find out that this array is called pclntab , and it is filled in the linker with the function func (ctxt * Link) pclntab () .

We have information about the pclntab table in which function names are stored and there is a way to access this table via buildid. Write the code:

Code that calls functions
 def go_find_pclntab(): pos = idaapi.get_segm_by_name(".text").endEA textstart = idaapi.get_segm_by_name(".text").startEA while True: # hex    "go.buildid"     , #     gobuilddefpos = FindBinary( pos, SEARCH_UP, "67 45 23 01 " + "00 "*20 + "67 6f 2e 62 75 69 6c 64 69 64") if gobuilddefpos < 100 or gobuilddefpos > pos: #  ,    # buildid pclntab entry   :( break #   buildid entry  if Dword(gobuilddefpos-0x10) == textstart: #         pclntab #     pclntab return gobuilddefpos + 24 - Dword(gobuilddefpos-0x8) pos = gobuilddefpos return None def go_pclntab_travel(pclntab): nfunc = Dword(pclntab+8) #       pclntab for i in xrange(nfunc): entry = pclntab + 0x10 + i * 0x10 sym = Qword(entry) info = Qword(entry + 8) + pclntab symnameoff = Dword(info + 8) + pclntab symname = GetString(symnameoff) #   go_pclntab_handle_function(sym, symname, info) 


Run and look at the results:

Kaef

Almost all 5728 functions have a name.

Names contain the full path to the package.

Looking for types


The second thing that interested me after the function name is type identification. First of all, we are interested in how dynamic memory is allocated for structures, which immediately leads us to the function newobject , which is passed a pointer to runtime._type .

Runtime._type structure
 type _type struct { size uintptr ptrdata uintptr // size of memory prefix holding all pointers hash uint32 tflag tflag align uint8 fieldalign uint8 kind uint8 alg *typeAlg // gcdata stores the GC type data for the garbage collector. // If the KindGCProg bit is set in kind, gcdata is a GC program. // Otherwise it is a ptrmask bitmap. See mbitmap.go for details. gcdata *byte str nameOff ptrToThis typeOff } 


Did you notice? Hint: str nameOff field. Hint: str is a string. Hint: nameOff β†’ name β†’ name β†’ name of the type structure β†’ name of the type. The type structure contains the type name! Only it is some kind of relative. We find out that str is the offset from the beginning of the types of the module section, a pointer to which can be obtained from the moduledata structure of the module. So in order to know the type, we need to find moduledata. For simplicity, we assume that we always have one module. We find in the runtime.resolveNameOff function that the first module is in the variable firstmoduledata, it must be found in our binary. To do this, you don’t need to go far, you can look at the same function resolveNameOff, because we already collected the addresses and names of all functions by the script above. It remains only to find the types for recognition, for this I simply took all the calls to the newobject function and took the pointers to the types from the parameters.

 lea rbx, _type_p_elliptic_p256Point_6dad60 ; *elliptic.p256Point mov [rsp+0A8h+var_A8], rbx call runtime_newobject ;          

Total


You can continue to continue to analyze this binary, but we have already achieved our goals and have shown that the golang binaries are fairly easy to explore, and the IDA scripting makes the work much easier.

Source Code: github.com/mogaika/golang_ida_scripts

The code is written on the rights of "proof of concept" and may contain flaws, all complaints can be expressed in personal messages here, or on github.

The text is written on the rights of "all good", claims to the quality of the text are expected in personal messages.

Source: https://habr.com/ru/post/325498/


All Articles