
Go Big or Go Home (and Other Terrible Go Puns): Tips for Analyzing GoLang Malware
A few days ago, Dr. Josh Stroschein invited me on his livesteam channel to talk about Golang malware. I wanted to get a quick blog post up before I forget everything we talked about. So, here is a summary of the key points we discussed in the livestream. You can also just watch the livestream here if you’re feeling lazy. Ok, let’s get on with it.
Go (or Golang) has gained some traction over the past few years, not just among developers, but increasingly among malware authors also looking for flexibility and portability. As reverse engineers and malware analysts, that means we need to get more comfortable navigating Go binaries, understanding how they’re structured, and knowing what makes them different from traditional malware written in C, C++, or Delphi (vomit face).
What Is Go, and Why Does It Matter?
Go is a statically typed, compiled programming language developed by Google. It’s designed to be simple, fast to compile, and efficient. Some of the things Go does well:
- Cross-compilation: Go makes it easy to build binaries for different OS’s and architectures.
- Static linking: Most Go binaries are self-contained, meaning no external dependencies.
- Built-in concurrency: Go’s goroutines make it easy to write efficient networked applications.
From a developer’s perspective, it’s efficient and practical. From a malware analyst’s perspective, it presents some interesting challenges.
Why Use Go for Malware?
Go offers several advantages that make it appealing to malware authors:
- Portability: Malware authors can compile a single codebase for multiple platforms (Windows, Linux, macOS, ARM, etc.).
- Self-contained binaries: Go binaries include everything they need to run, which results in some seriously HUGE executable file sizes, but more on that later.
- Less tooling: Traditional reverse engineering tools aren’t as well-optimized for Go binaries, especially compared to C/C++ (but this is changing quickly).
- Rapid development: Go is relatively easy to write and maintain, which makes it efficient for malware development.
- Evasion by obscurity: Go binaries look different from typical malware, especially in static analysis, which may help them avoid basic detections (this is also changing rapidly).
Common Pitfalls in Analyzing Go Malware
1. Large Binary Sizes
Even a simple Go program can compile into a binary tens of megabytes in size, which you’ll see in a moment. This is due to static linking of the Go runtime and standard libraries. For analysts, this means more to sift through as it’s often not immediately obvious where the actual malicious code begins or ends.
2. Excess of Legitimate Code
Go’s standard library is extensive, and malware often makes use of common packages like net, os, crypto, and io. Most of the code in the binary is likely benign. The challenge is identifying the small percentage of custom or malicious logic within all the legitimate functionality. Your classic needle-in-a-pile-of-needles problem.
3. Obfuscation (Garble and Others)
Go malware is increasingly using obfuscation tools like Garble, which strip or randomize symbol names, re-order packages, and break common static analysis workflows. These techniques don’t necessarily make the malware more sophisticated, but they do add complexity to the reversing process.
Other common obfuscation techniques may include:
- Encrypted or encoded strings
- Control flow obfuscation
- Packing or compression
Let’s analyze a very basic Go binary. The best way to do this is to write our own code.
Analyzing a Basic Go Program
Go code is fairly straightforward and simple to write. Here is literally the most basic Go application you can write, printing our favorite “Hello World” (in this case, “Hello Earth”) string:

When compiled (using the go build command), the binary is a fairly large executable (2MB+). Since Go ships a lot of library code into each compiled executable, even this simple Hello World binary is substantial.
Let’s open this up in IDA, my dissasembler of choice for Golang. Newer versions of IDA (I think version 8+) are good at identifying Go standard library code. IDA nicely groups these libraries in “folders”, as you can see in the screenshot below:

Each of these folders represent a library. For example, “internal”, “runtime”, and “math” are all libraries being imported into this Go program. IDA is able to recognize these libraries and functions and name them appropriately. If your dissasembler is not designed for Golang use, you’ll see a bunch of generic names for these functions which makes analysis of Go binaries a lot more difficult. One tool (GoReSim) can help identify these functions, and the output of this tool can then be re-imported into some disassemblers like Ghidra.
Most of the time in un-obuscated Golang binaries, the main functionality of the program will reside in the function main.main or, main_main), which IDA identified for us:

Tip: Whenever I’m analyzing a Go binary, I first always look for main_main or other functions that contain the name “main_*”.
Inside main_main we can see our Hello World code. You may be able to spot the “Hello Earth!” string in the code below:

This “Hello Earth!” string also contains a bunch of other junk. These are also strings in the binary. One challenge when analyzing Golang code is that strings are not null-terminated like they are in C programs. Each string is actually a structure that contains the string itself and an integer representing the string’s length. I provided some terrible pseudocode for visualization of this:
struct string (
value = "Hello Earth!"
length = 12
)
In this case, IDA didn’t know that “Hello Earth!” is a separate string from “152587…” and the others. This is one thing you’ll need to take into account when analyzing Golang.
Ok, Hello World apps are cool and all, but let’s take it up a notch. Many malware binaries written in Go will be obfuscated. Garble is one such obfuscator. Garble… well… garbles the metadata of the Go binary. It does this by stripping symbols, function names, module and build information, and other metadata from the binary during compile-time.
If we open the same Hello World binary in IDA, but “Garbled” during compilation, it looks a lot different:

All our nice, beautiful Golang function names have been replaced with ugly, generic IDA function names (“sub_xxxxxx”). So how do we find our main function code now? We can’t – Golang won. Time to pack up and Go home.
No, just kidding. We just have to work a bit harder. I’ve found that Golang requires several critical libraries to correctly function, and one of those is the “runtime” library, which contains a lot of Go’s runtime code. Oftentimes, the runtime library names are not obfuscated, like in this case of my binary compiled with Garble (Note: I think Garble can also strip the module names from “runtime” as well, but I didn’t test this. In any case, the “runtime” module names are often not obfuscated). This means we can find cross-references to runtime functions in the code, and trace those back to the program’s main function! Let’s try this.
If we search the function list in IDA for “runtime”, we get the following:

One common runtime function is runtime_unlockOSThread. We can double-click on this function and select CTRL+X to see cross-references to it. Taking a look through all the cross-referenced functions will lead you to a block of code that looks like this:

When you spot functionality that contains a lot of “runtime” functions, you may be near the location of the program’s main code. In this case, our main code is not far away, in sub_49A9E0. You may be wondering: “Kyle, how are you so smart that you found that so fast?”. Well, intelligence aside, it was a lot of hunting around the code. No crazy tricks here.
And here we have our main code at sub_49A9E0:

Tip: Garble and other obfuscators can also obfuscate strings, not just the function names. I used the default Garble settings for this binary. The analysis methodology is the same, however.
Additional Resources
A few more resources on Golang I find extremely helpful:
- Ivan Kwiatkowski’s YouTube videos on GoLang analysis.
- Josh Stroschein’s PluralSight course on GoLang malware analysis. In this course, Josh covers the OT malware FrostyGoop.
Key Takeaways
Go malware is becoming more common, and it’s likely here to stay. While it presents some unique challenges, many of the same principles from other forms of reverse engineering still apply. You just need to adjust your approach and tools.
5 Tips for Reversing Go Malware
- Start with main.main (main_main) – This is (nearly) always the entry point for a Go binary and can give you a foothold into the rest of the logic.
- Use the right tooling – IDA, Ghidra with GoReSym (other disassemblers probably worked, but I haven’t tested them), and de-obfuscators like the appropriately named UnGarbler.
- Ignore the noise – Skip most of the standard library code unless it’s directly involved in malicious behavior.
- Look for key APIs – Even with obfuscation, patterns like “net.Dial”, “os/exec”, or “http.Get” can help narrow down suspicious areas.
- Combine static and dynamic analysis – Especially with obfuscated binaries, dynamic tracing or debugging can be the fastest way to understand real behavior. Ivan Kwiatkowski has some great tips on debugging Golang in this video.