{
	"id": "0eda1877-2e07-44cf-aaa2-905f1e5c5c0e",
	"created_at": "2026-04-06T00:06:07.177105Z",
	"updated_at": "2026-04-10T03:31:13.713799Z",
	"deleted_at": null,
	"sha1_hash": "53707f6131286493d70823d1bbceb712a9f01c7d",
	"title": "Reversing GO binaries like a pro",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 2649261,
	"plain_text": "Reversing GO binaries like a pro\r\nPublished: 2016-09-21 · Archived: 2026-04-05 22:08:17 UTC\r\nGO binaries are weird, or at least, that is where this all started out. While delving into some Linux malware named Rex, I\r\ncame to the realization that I might need to understand more than I wanted to. Just the prior week I had been reversing Linux\r\nLady which was also written in GO, however it was not a stripped binary so it was pretty easy. Clearly the binary was rather\r\nlarge, many extra methods I didn’t care about - though I really just didn’t understand why. To be honest - I still haven’t fully\r\ndug into the Golang code and have yet to really write much code in Go, so take this information at face value as some of it\r\nmight be incorrect; this is just my experience while reversing some ELF Go binaries! If you don’t want to read the whole\r\npage, or scroll to the bottom to get a link to the full repo, just go here.\r\nTo illistrate some of my examples I’m going to use an extremely simple ‘Hello, World!’ example and also reference the Rex\r\nmalware. The code and a Make file are extremely simple;\r\nHello.go\r\n1\r\n2\r\n3\r\n4\r\n5\r\npackage main\r\nimport \"fmt\"\r\nfunc main() {\r\n fmt.Println(\"Hello, World!\")\r\n}\r\nMakefile\r\n1\r\n2\r\n3\r\nall:\r\nGOOS=linux GOARCH=386 go build -o hello-stripped -ldflags \"-s\" hello.go\r\nGOOS=linux GOARCH=386 go build -o hello-normal hello.go\r\nSince I’m working on an OSX machine, the above GOOS and GOARCH variables are explicitly needed to cross-compile this\r\ncorrectly. The first line also added the ldflags option to strip the binary. This way we can analyze the same executable\r\nboth stripped and without being stripped. Copy these files, run make and then open up the files in your disassembler of\r\nchoice, for this blog I’m going to use IDA Pro. If we open up the unstripped binary in IDA Pro we can notice a few quick\r\nthings;\r\nhttps://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nPage 1 of 16\n\nWell then - our 5 lines of code has turned into over 2058 functions. With all that overhead of what appears to be a runtime,\r\nwe also have nothing interesting in the main() function. If we dig in a bit further we can see that the actual code we’re\r\ninterested in is inside of main_main ;\r\nhttps://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nPage 2 of 16\n\nThis is, well, lots of code that I honestly don’t want to look at. The string loading also looks a bit weird - though IDA seems\r\nto have done a good job identifying the necessary bits. We can easily see that the string load is actually a set of three mov s;\r\nString load\r\n1\r\n2\r\nmov ebx, offset aHelloWorld ; \"Hello, World!\"\r\nmov [esp+3Ch+var_14], ebx ; Shove string into location\r\nhttps://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nPage 3 of 16\n\n3 mov [esp+3Ch+var_10], 0Dh ; length of string\r\nThis isn’t exactly revolutionary, though I can’t off the top of my head say that I’ve seen something like this before. We’re\r\nalso taking note of it as this will come in handle later on. The other tidbit of code which caught my eye was the\r\nruntime_morestack_context call;\r\nmorestack_context\r\n1\r\n2\r\n3\r\nloc_80490CB:\r\ncall runtime_morestack_noctxt\r\njmp main_main\r\nThis style block of code appears to always be at the end of functions and it also seems to always loop back up to the top of\r\nthe same function. This is verified by looking at the cross-references to this function. Ok, now that we know IDA Pro can\r\nhandle unstripped binaries, lets load the same code but the stripped version this time.\r\nImmediately we see some, well, lets just call them “differences”. We have 1329 functions defined and now see some\r\nundefined code by looking at the navigator toolbar. Luckily IDA has still been able to find the string load we are looking for,\r\nhowever this function now seems much less friendly to deal with.\r\nhttps://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nPage 4 of 16\n\nWe now have no more function names, however - the function names appear to be retained in a specific section of the binary\r\nif we do a string search for main.main (which would be repesented at main_main in the previous screen shots due to how\r\na . is interpreted by IDA);\r\nhttps://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nPage 5 of 16\n\n.gopclntab\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n.gopclntab:0813E174 db 6Dh ; m\r\n.gopclntab:0813E175 db 61h ; a\r\n.gopclntab:0813E176 db 69h ; i\r\n.gopclntab:0813E177 db 6Eh ; n\r\n.gopclntab:0813E178 db 2Eh ; .\r\n.gopclntab:0813E179 db 6Dh ; m\r\n.gopclntab:0813E17A db 61h ; a\r\n.gopclntab:0813E17B db 69h ; i\r\n.gopclntab:0813E17C db 6Eh ; n\r\nAlright, so it would appear that there is something left over here. After digging into some of the Google results into\r\ngopclntab and tweet about this - a friendly reverser George (Egor?) Zaytsev showed me his IDA Pro scripts for renaming\r\nfunction and adding type information. After skimming these it was pretty easy to figure out the format of this section so I\r\nthrew together some functionally to replicate his script. The essential code is shown below, very simply put, we look into the\r\nsegment .gopclntab and skip the first 8 bytes. We then create a pointer ( Qword or Dword dependant on whether the\r\nbinary is 64bit or not). The first set of data actually gives us the size of the .gopclntab table, so we know how far to go\r\ninto this structure. Now we can start processing the rest of the data which appears to be the function_offset followed by\r\nthe (function) name_offset ). As we create pointers to these offsets and also tell IDA to create the strings, we just need to\r\nensure we don’t pass MakeString any bad characters so we use the clean_function_name function to strip out any\r\nbadness.\r\nrenamer.py\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\ndef create_pointer(addr, force_size=None):\r\nif force_size is not 4 and (idaapi.get_inf_structure().is_64bit() or force_size is 8):\r\n MakeQword(addr)\r\nreturn Qword(addr), 8\r\nelse:\r\nMakeDword(addr)\r\nreturn Dword(addr), 4\r\nSTRIP_CHARS = [ '(', ')', '[', ']', '{', '}', ' ', '\"' ]\r\nREPLACE_CHARS = ['.', '*', '-', ',', ';', ':', '/', '\\xb7' ]\r\ndef clean_function_name(str):\r\n str = filter(lambda x: x in string.printable, str)\r\nfor c in STRIP_CHARS:\r\n str = str.replace(c, '')\r\nfor c in REPLACE_CHARS:\r\n str = str.replace(c, '_')\r\nreturn str\r\ndef renamer_init():\r\n renamed = 0\r\n gopclntab = ida_segment.get_segm_by_name('.gopclntab')\r\nif gopclntab is not None:\r\n addr = gopclntab.startEA + 8\r\n size, addr_size = create_pointer(addr)\r\nhttps://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nPage 6 of 16\n\n23\r\n24\r\n25\r\n26\r\n27\r\n28\r\n29\r\n30\r\n31\r\n32\r\n33\r\n34\r\n35\r\n36\r\n37\r\n38\r\n39\r\n40\r\n41\r\n42\r\n43\r\n44\r\n45\r\n46\r\n47\r\n48\r\n49\r\n50\r\n51\r\n52\r\n53\r\n54\r\n55\r\n56\r\n addr += addr_size\r\n early_end = addr + (size * addr_size * 2)\r\nwhile addr \u003c early_end:\r\n func_offset, addr_size = create_pointer(addr)\r\n name_offset, addr_size = create_pointer(addr + addr_size)\r\n addr += addr_size * 2\r\n func_name_addr = Dword(name_offset + gopclntab.startEA + addr_size) + gopclntab.startEA\r\n func_name = GetString(func_name_addr)\r\n MakeStr(func_name_addr, func_name_addr + len(func_name))\r\n appended = clean_func_name = clean_function_name(func_name)\r\n debug('Going to remap function at 0x%x with %s - cleaned up as %s' % (func_offset, func_name, clean_func_n\r\nif ida_funcs.get_func_name(func_offset) is not None:\r\nif MakeName(func_offset, clean_func_name):\r\n renamed += 1\r\nelse:\r\n error('clean_func_name error %s' % clean_func_name)\r\nreturn renamed\r\ndef main():\r\n renamed = renamer_init()\r\n info('Found and successfully renamed %d functions!' % renamed)\r\nThe above code won’t actually run yet (don’t worry full code available in this repo ) but it is hopefully simple enough to\r\nread through and understand the process. However, this still doesn’t solve the problem that IDA Pro doesn’t know all the\r\nfunctions. So this is going to create pointers which aren’t being referenced anywhere. We do know the beginning of\r\nfunctions now, however I ended up seeing (what I think is) an easier way to define all the functions in the application. We\r\ncan define all the functions by utilizing runtime_morestack_noctxt function. Since every function utilizes this (basically,\r\nthere is an edgecase it turns out), if we find this function and traverse backwards to the cross references to this function, then\r\nwe will know where every function exists. So what, right? We already know where every function started from the segment\r\nwe just parsed above, right? Ah, well - now we know the end of the function and the next instruction after the call to\r\nruntime_morestack_noctxt gives us a jump to the top of the function. This means we should quickly be able to give the\r\nbounds of the start and stop of a function, which is required by IDA, while seperating this from the parsing of the function\r\nnames. If we open up the window for cross references to the function runtime_morestack_noctxt we see there are many\r\nhttps://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nPage 7 of 16\n\nmore undefined sections calling into this. 1774 in total things reference this function, which is up from the 1329 functions\r\nIDA has already defined for us, this is highlighted by the image below;\r\nAfter digging into mutliple binaries we can see the runtime_morestack_noctext will always call into runtime_morestack\r\n(with context). This is the edgecase I was referencing before, so between these two functions we should be able to see cross\r\nrefereneces to ever other function used in the binary. Looking at the larger of the two functions, runtime_more_stack , of\r\nmultiple binaries tends to have an interesting layout;\r\nhttps://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nPage 8 of 16\n\nThe part which stuck out to me was mov large dword ptr ds:1003h, 0 - this appeared to be rather constant in all 64bit\r\nbinaries I saw. So after cross compiling a few more I noticed that 32bit binaries used mov qword ptr ds:1003h, 0 , so we\r\nwill be hunting for this pattern to create a “hook” for traversing backwards on. Lucky for us, I haven’t seen an instance\r\nwhere IDA Pro fails to define this specific function, we don’t really need to spend much brain power mapping it out or\r\ndefining it outselves. So, enough talk, lets write some code to find this function;\r\nfind_runtime_morestack.py\r\n1\r\n2\r\ndef create_runtime_ms():\r\n debug('Attempting to find runtime_morestack function for hooking on...')\r\nhttps://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nPage 9 of 16\n\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n text_seg = ida_segment.get_segm_by_name('.text')\r\n runtime_ms_end = ida_search.find_text(text_seg.startEA, 0, 0, \"word ptr ds:1003h, 0\", SEARCH_DOWN)\r\n runtime_ms = ida_funcs.get_func(runtime_ms_end)\r\nif idc.MakeNameEx(runtime_ms.startEA, \"runtime_morecontext\", SN_PUBLIC):\r\n debug('Successfully found runtime_morecontext')\r\nelse:\r\n debug('Failed to rename function @ 0x%x to runtime_morestack' % runtime_ms.startEA)\r\nreturn runtime_ms\r\nAfter finding the function, we can recursively traverse backwards through all the function calls, anything which is not inside\r\nan already defined function we can now define. This is because the structure always appears to be;\r\ngolang_undefined_function_example\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26\r\n27\r\n.text:08089910 ; Function start - however undefined currently according to IDA\r\n.text:08089910 loc_8089910: ; CODE XREF: .text:0808994B\r\n.text:08089910 ; DATA XREF: sub_804B250+1A1\r\n.text:08089910 mov ecx, large gs:0\r\n.text:08089917 mov ecx, [ecx-4]\r\n.text:0808991D cmp esp, [ecx+8]\r\n.text:08089920 jbe short loc_8089946\r\n.text:08089922 sub esp, 4\r\n.text:08089925 mov ebx, [edx+4]\r\n.text:08089928 mov [esp], ebx\r\n.text:0808992B cmp dword ptr [esp], 0\r\n.text:0808992F jz short loc_808993E\r\n.text:08089931\r\n.text:08089931 loc_8089931: ; CODE XREF: .text:08089944\r\n.text:08089931 add dword ptr [esp], 30h\r\n.text:08089935 call sub_8052CB0\r\n.text:0808993A add esp, 4\r\n.text:0808993D retn\r\n.text:0808993E ; ---------------------------------------------------------------------------\r\n.text:0808993E\r\n.text:0808993E loc_808993E: ; CODE XREF: .text:0808992F\r\n.text:0808993E mov large ds:0, eax\r\n.text:08089944 jmp short loc_8089931\r\n.text:08089946 ; ---------------------------------------------------------------------------\r\n.text:08089946\r\n.text:08089946 loc_8089946: ; CODE XREF: .text:08089920\r\n.text:08089946 call runtime_morestack ; \"Bottom\" of function, calls out to runtime_morestack\r\nhttps://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nPage 10 of 16\n\n28 .text:0808994B jmp short loc_8089910 ; Jump back to the \"top\" of the function\r\nThe above snippet is a random undefined function I pulled from the stripped example application we compiled already.\r\nEssentially by traversing backwards into every undefined function, we will land at something like line 0x0808994B which\r\nis the call runtime_morestack . From here we will skip to the next instruction and ensure it is a jump above where we\r\ncurrently are, if this is true, we can likely assume this is the start of a function. In this example (and almost every test case\r\nI’ve run) this is true. Jumping to 0x08089910 is the start of the function, so now we have the two parameters required by\r\nMakeFunction function;\r\ntraverse_functions.py\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26\r\n27\r\n28\r\n29\r\n30\r\n31\r\n32\r\n33\r\n34\r\n35\r\ndef is_simple_wrapper(addr):\r\nif GetMnem(addr) == 'xor' and GetOpnd(addr, 0) == 'edx' and GetOpnd(addr, 1) == 'edx':\r\n addr = FindCode(addr, SEARCH_DOWN)\r\nif GetMnem(addr) == 'jmp' and GetOpnd(addr, 0) == 'runtime_morestack':\r\nreturn True\r\nreturn False\r\ndef create_runtime_ms():\r\n debug('Attempting to find runtime_morestack function for hooking on...')\r\n text_seg = ida_segment.get_segm_by_name('.text')\r\n runtime_ms_end = ida_search.find_text(text_seg.startEA, 0, 0, \"word ptr ds:1003h, 0\", SEARCH_DOWN)\r\n runtime_ms = ida_funcs.get_func(runtime_ms_end)\r\nif idc.MakeNameEx(runtime_ms.startEA, \"runtime_morestack\", SN_PUBLIC):\r\n debug('Successfully found runtime_morestack')\r\nelse:\r\n debug('Failed to rename function @ 0x%x to runtime_morestack' % runtime_ms.startEA)\r\nreturn runtime_ms\r\ndef traverse_xrefs(func):\r\n func_created = 0\r\nif func is None:\r\nreturn func_created\r\n func_xref = ida_xref.get_first_cref_to(func.startEA)\r\nwhile func_xref != 0xffffffffffffffff:\r\nif ida_funcs.get_func(func_xref) is None:\r\n func_end = FindCode(func_xref, SEARCH_DOWN)\r\nif GetMnem(func_end) == \"jmp\":\r\n func_start = GetOperandValue(func_end, 0)\r\nif func_start \u003c func_xref:\r\nif idc.MakeFunction(func_start, func_end):\r\n func_created += 1\r\nelse:\r\n error('Error trying to create a function @ 0x%x - 0x%x' %(func_start, func_end))\r\nelse:\r\n xref_func = ida_funcs.get_func(func_xref)\r\nif is_simple_wrapper(xref_func.startEA):\r\n debug('Stepping into a simple wrapper')\r\nhttps://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nPage 11 of 16\n\n36\r\n37\r\n38\r\n39\r\n40\r\n41\r\n42\r\n43\r\n44\r\n45\r\n46\r\n47\r\n48\r\n49\r\n50\r\n51\r\n52\r\n53\r\n54\r\n55\r\n56\r\n57\r\n58\r\n59\r\n60\r\n61\r\n62\r\n63\r\n64\r\n65\r\n66\r\n67\r\n68\r\n69\r\n70\r\n71\r\n72\r\n73\r\n74\r\n75\r\n76\r\n77\r\n78\r\n79\r\n func_created += traverse_xrefs(xref_func)\r\nif ida_funcs.get_func_name(xref_func.startEA) is not None and 'sub_' not in ida_funcs.get_func_name(xref_func.startEA)\r\n debug('Function @0x%x already has a name of %s; skipping...' % (func_xref, ida_funcs.get_func_name(xre\r\nelse:\r\n debug('Function @ 0x%x already has a name %s' % (xref_func.startEA, ida_funcs.get_func_name(xref_func.\r\n func_xref = ida_xref.get_next_cref_to(func.startEA, func_xref)\r\nreturn func_created\r\ndef find_func_by_name(name):\r\n text_seg = ida_segment.get_segm_by_name('.text')\r\nfor addr in Functions(text_seg.startEA, text_seg.endEA):\r\nif name == ida_funcs.get_func_name(addr):\r\nreturn ida_funcs.get_func(addr)\r\nreturn None\r\ndef runtime_init():\r\n func_created = 0\r\nif find_func_by_name('runtime_morestack') is not None:\r\n func_created += traverse_xrefs(find_func_by_name('runtime_morestack'))\r\n func_created += traverse_xrefs(find_func_by_name('runtime_morestack_noctxt'))\r\nelse:\r\n runtime_ms = create_runtime_ms()\r\n func_created = traverse_xrefs(runtime_ms)\r\nreturn func_created\r\nhttps://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nPage 12 of 16\n\n80\r\n81\r\n82\r\nThat code bit is a bit lengthy, though hopefully the comments and concept is clear enough. It likely isn’t necessary to\r\nexplicitly traverse backwards recursively, however I wrote this prior to understanding that runtime_morestack_noctxt (the\r\nedgecase) is the only edgecase that I would encounter. This was being handled by the is_simple_wrapper function\r\noriginally. Regardless, running this style of code ended up finding all the extra functions IDA Pro was missing. We can see\r\nbelow, that this creates a much cleaner and easier experience to reverse;\r\nThis can allow us to use something like Diaphora as well since we can specifically target functions with the same names, if\r\nwe care too. I’ve personally found this is extremely useful for malware or other targets where you really don’t care about\r\nany of the framework/runtime functions. You can quiet easily differentiate between custom code written for the binary, for\r\nexample in the Linux malware “Rex” everything because with that name space! Now onto the last challenge that I wanted to\r\nsolve while reversing the malware, string loading! I’m honestly not 100% sure how IDA detects most string loads,\r\npotentially through idioms of some sort? Or maybe because it can detect strings based on the \\00 character at the end of it?\r\nRegardless, Go seems to use a string table of some sort, without requiring null character. The appear to be in alpha-numeric\r\norder, group by string length size as well. This means we see them all there, but often don’t come across them correctly\r\nasserted as strings, or we see them asserted as extremely large blobs of strings. The hello world example isn’t good at\r\nillistrating this, so I’ll pull open the main.main function of the Rex malware to show this;\r\nhttps://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nPage 13 of 16\n\nI didn’t want to add comments to everything, so I only commented the first few lines then pointed arrows to where there\r\nshould be pointers to a proper string. We can see a few different use cases and sometimes the destination registers seem to\r\nchange. However there is definitely a pattern which forms that we can look for. Moving of a pointer into a register, that\r\nregister is then used to push into a (d)word pointer, followed by a load of a lenght of the string. Cobbling together some\r\npython to hunt for the pattern we end with something like the pseudo code below;\r\nstring_hunting.py\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\nVALID_REGS = ['ebx', 'ebp']\r\nVALID_DEST = ['esp', 'eax', 'ecx', 'edx']\r\ndef is_string_load(addr):\r\n patterns = []\r\nif GetMnem(addr) == 'mov':\r\nif GetOpnd(addr, 0) in VALID_REGS and not ('[' in GetOpnd(addr, 1) or 'loc_' in GetOpnd(addr, 1)) and('offset ' in Get\r\n from_reg = GetOpnd(addr, 0)\r\n addr_2 = FindCode(addr, SEARCH_DOWN)\r\ntry:\r\n dest_reg = GetOpnd(addr_2, 0)[GetOpnd(addr_2, 0).index('[') + 1:GetOpnd(addr_2, 0).index('[') + 4]\r\nexcept ValueError:\r\nreturn False\r\nif GetMnem(addr_2) == 'mov' and dest_reg in VALID_DEST and ('[%s' % dest_reg) in GetOpnd(addr_2, 0) and GetOpnd(addr_2\r\n addr_3 = FindCode(addr_2, SEARCH_DOWN)\r\nif GetMnem(addr_3) == 'mov' and (('[%s+' % dest_reg) in GetOpnd(addr_3, 0) or GetOpnd(addr_3, 0) in VALID_DEST) and 'o\r\ntry:\r\n dumb_int_test = GetOperandValue(addr_3, 1)\r\nhttps://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nPage 14 of 16\n\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26\r\n27\r\n28\r\n29\r\n30\r\n31\r\n32\r\n33\r\n34\r\n35\r\n36\r\n37\r\n38\r\n39\r\n40\r\n41\r\n42\r\n43\r\n44\r\n45\r\n46\r\n47\r\nif dumb_int_test \u003e 0 and dumb_int_test \u003c sys.maxsize:\r\nreturn True\r\nexcept ValueError:\r\nreturn False\r\ndef create_string(addr, string_len):\r\n debug('Found string load @ 0x%x with length of %d' % (addr, string_len))\r\nif GetStringType(addr) is not None and GetString(addr) is not None and len(GetString(addr)) != string_len:\r\n debug('It appears that there is already a string present @ 0x%x' % addr)\r\n MakeUnknown(addr, string_len, DOUNK_SIMPLE)\r\nif GetString(addr) is None and MakeStr(addr, addr + string_len):\r\nreturn True\r\nelse:\r\n MakeUnknown(addr, string_len, DOUNK_SIMPLE)\r\nif MakeStr(addr, addr + string_len):\r\nreturn True\r\n debug('Unable to make a string @ 0x%x with length of %d' % (addr, string_len))\r\nreturn False\r\nThe above code could likely be optimized, however it was working for me on the samples I needed. All that would be left is\r\nto create another function which hunts through all the defined code segments to look for string loads. Then we can use the\r\npointer to the string and the string length to define a new string using the MakeStr . In the code I ended up using, you need\r\nto ensure that IDA Pro hasn’t mistakenly already create the string, as it sometimes tries to, incorrectly. This seems to happen\r\nsometimes when a string in the table contains a null character. However, after using code above, this is what we are left\r\nwith;\r\nhttps://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nPage 15 of 16\n\nThis is a much better piece of code to work with. After we throw together all these functions, we now have the\r\ngolang_loader_assist.py module for IDA Pro. A word of warning though, I have only had time to test this on a few versions\r\nof IDA Pro for OSX, the majority of testing on 6.95. There is also very likely optimizations which should be made or at a\r\nbare minimum some reworking of the code. With all that said, I wanted to open source this so others could use this and\r\nhopefully contribute back. Also be aware that this script can be painfully slow depending on how large the idb file is,\r\nworking on a OSX El Capitan (10.11.6) using a 2.2 GHz Intel Core i7 on IDA Pro 6.95 - the string discovery aspect itself\r\ncan take a while. I’ve often found that running the different methods seperately can prevent IDA from locking up. Hopefully\r\nthis blog and the code proves useful to someone though, enjoy!\r\nSource: https://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nhttps://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/\r\nPage 16 of 16",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/"
	],
	"report_names": [
		"reversing_go_binaries_like_a_pro"
	],
	"threat_actors": [
		{
			"id": "f4f16213-7a22-4527-aecb-b964c64c2c46",
			"created_at": "2024-06-19T02:03:08.090932Z",
			"updated_at": "2026-04-10T02:00:03.6289Z",
			"deleted_at": null,
			"main_name": "GOLD NIAGARA",
			"aliases": [
				"Calcium ",
				"Carbanak",
				"Carbon Spider ",
				"FIN7 ",
				"Navigator ",
				"Sangria Tempest ",
				"TelePort Crew "
			],
			"source_name": "Secureworks:GOLD NIAGARA",
			"tools": [
				"Bateleur",
				"Carbanak",
				"Cobalt Strike",
				"DICELOADER",
				"DRIFTPIN",
				"GGLDR",
				"GRIFFON",
				"JSSLoader",
				"Meterpreter",
				"OFFTRACK",
				"PILLOWMINT",
				"POWERTRASH",
				"SUPERSOFT",
				"TAKEOUT",
				"TinyMet"
			],
			"source_id": "Secureworks",
			"reports": null
		}
	],
	"ts_created_at": 1775433967,
	"ts_updated_at": 1775791873,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/53707f6131286493d70823d1bbceb712a9f01c7d.pdf",
		"text": "https://archive.orkl.eu/53707f6131286493d70823d1bbceb712a9f01c7d.txt",
		"img": "https://archive.orkl.eu/53707f6131286493d70823d1bbceb712a9f01c7d.jpg"
	}
}