The Universal Loader for Go

As promised in the previous post, Go Assembly on the arm64, I have been working on a very special project for the past couple months, and I’m very pleased to announce the Universal Loader!

This Golang library provides a consistent interface across all platforms for loading shared libraries from memory and without using CGO. When I say “all platforms”, I mean Linux, Windows, and OSX including the new M1 Apple chip.

Until someone tells me differently, I am claiming that this is the very first loader to work on the M1, Golang or not. I haven’t tried it myself yet, but it will likely also work on any POSIX system, like BSD variants, without changes. If you try this on a new platform, let me know!

Additionally, the Linux backend of the loader does not use memfd, and I believe this is the first Golang loader to do that as well. The Linux ramdisk implementation memfd is very easy to use, but it’s also relatively easy to detect.

Consistent Interface

On all platforms, this is a basic example of how to use Universal to load a shared library from memory and call an exported symbol inside it:

Just pass in your library as a byte array, call LoadLibrary on it and give it a name, then you can Call() any exported symbol in that library with whatever arguments you want.

All you have to do differently on a different platform is load the right type of library for your platform. On OSX, you would load myLibrary.dyld, on Linux, and on Windows myLibrary.DLL.

Check out the examples/ folder in the repo and the tests for more details.

Algorithms and References

The Linux and Windows implementations are very straight-forward and based on the same basic algorithms that have been widely used for years, and I used malisal’s excellent repo as a reference, with some minor changes.

For the OSX loader, I referred heavily to MalwareUnicorn’s wonderful training, but I did have to make a few updates. For one thing, dyld is not guaranteed to be the next image in memory after the base image.

Also have to give a heartful thank you to C-Sto and lesnuages, who contributed code to the Windows and OSX loaders, respectively.

Last but not least, this library makes heavy use of our own fork of the debug library, so this would not have been possible without contributions over the years from the whole Binject crew, and it’s a perfect example of the power of the tools we’ve made.

This isn’t the first tool to come out of our work in Binject, and it definitely will not be the last.

Once you get locked into a serious m*****e collection, the tendency is to push it as far as you can.

Go Assembly on the arm64

If you have ever looked at Go’s assembly language, you were likely confused into giving up. Against all odds, I recently managed to implement some Go assembly that lets you call a cdecl function in a native library directly from Go on the new M1 Apple chip platform, which is arm64/POSIX based. My joy at never having to look at Go assembly again was short-lived, however, as several of my friends asked me to write something up to explain what I had learned about Go assembly, thus forcing me to relive this traumatic experience in some detail. So here it is friends, I hope it helps.

Why would you use Go Assembly?

Go assembly lets you define a function that you can call from Go that can use all of the low-level assembly operators available on your platform. This means you can avoid using CGO entirely, and avoid having to set up the heinous external cross-compiling toolchains that CGO requires. Go itself can assemble Go assembly on every platform without installing external tools.

What is Go Assembly?

plan9 was a particular set of design principles applied to recreating Unix at Bell Labs in the 80’s. Golang is essentially the plan9 re-implementation of C++, and the Go assembler is based directly on the plan9 assembler (notice the common thread from plan9 to Golang is Rob Pike). Much like plan9 itself, Golang at first seems confusing, but over time you start to see the method behind the madness. Everything is consistent in a way that C++ isn’t, and removing inconsistencies lets you write better code faster, once you get used to it.

This is not true of the Go assembler, however. If there is some underlying pattern that makes everything consistently understandable, I haven’t seen it. It’s best to understand upfront that you are marching into the mouth of madness and stop trying to force things to make sense. Just relax and accept that the underlying design decisions are probably beyond our ability to comprehend.

Try to remember HOW this works and don’t worry about WHY it is this way. Trust me on this. Take some deep breaths, and here we go.

Go assembly does not have a one-to-one correspondence with native assembly

The most confusing part about Go assembly is that it looks like native assembly, but it isn’t.

It renames almost all the registers

For example, on arm64, the main registers that the Arm spec calls X0-X7 are referred to as R0-R7 in Go assembly. You will have to check the Go assembler documentation for every platform you work on to see what the registers have been renamed to.

It renames some of the operators entirely

For example, on arm64, the branch-to-register-with-link operator BLR is renamed to CALL, but BL is not renamed. This means that for every operator you want to use, you will have to check and make sure that the Go version of that operator for your platform is the same operator.

It changes the order of some arguments to operators from native assembly, but not all of them

It’s not even as simple as changing the order from right-to-left to left-to-right. For many operators, such as the ones that assign to pairs of registers, the order of the first two arguments is the same as native assembly, but the order of the rest of the arguments is reversed!

So not only will you have to check that the operator names haven’t changed, you’ll also have to check to see how the order of the operands is different in Go assembly.

Go assembly is not meant for you

This is very clearly a tool meant for use mainly by developers on the Go language runtime itself. There are tons of useful Go assembly libraries inside Go, but they’re all restricted to being called only from inside the Go runtime.

Even though it’s an abstraction over native assembly, Go assembly is different for every platform anyway

The Go assemblers for different platforms have some things in common, but they’re all different and will have different register and operator renaming rules. This means that you will have to write a different version of your Go assembly function for every target platform that you want it to run on!

Be very careful, because the same instructions (like MOVD or CALL) might have the same names in Go assembly from one platform to the other, but they might have very different behavior! Go assembly is really totally different variants of Go assembly for every platform.

The only documentation you really have to work with are two long pages that describe the naming of registers and operator arguments for arm64 and ppc64. You can text-search on these pages for the native assembly operators you’re familiar with and figure out which Go assembly operator corresponds to them. For every other platform, the only documentation is short notes on the main page.

Go assembly files typically make heavy use of build constraints as file suffixes, so that the right Go assembly file will be automatically included when you build for a particular platform. For a typical function, you’ll want to target amd64, arm64, and maybe 386, resulting in three different implementations in the files myfunc_amd64.s, myfunc_arm64.s, and myfunc_386.s. This also means that you will need to look at the Go assembler documentation for three different platforms, check the register and operator renaming and also how the arguments have been re-ordered.

Nice things about Go Assembly

This is a short list, but there are a few nice things about Go assembly over native assembly.

By default, it automatically aligns to 16 bytes, same as arm64 requires. You can easily change alignment at any point inside a function with the PCALIGN instruction, and it will automatically align the whole function correctly for you.

You don’t need to monkey with the stack as much, because it handles some of this for you.

Go assembly adds a bunch of strange rules that you don’t have to deal with in native assembly

To illustrate this, let’s look at a simple arm64 function in Go assembly that you can call from Go that simply calls a native function using the C calling convention (cdecl) with no arguments. The Go assembly function takes one argument, which is the address of the native function to call.

Function Definition

Line 1 is the function definition. The good news is that the function definitions are the same for all platforms in Go assembly. They start with the TEXT statement, which says this function should be mapped into the TEXT segment, which just means that it’s executable code and not data. So far so good.

Following that, however, is the function name, which begins with the bizarre centered dot character “·“. This character is a special symbol that tells the Go assembler that this function should be exposed to Go code. It’s possible to create assembly functions without this character, but they won’t be callable from Go. I have no idea how to type this character, I have been copy-pasting it from place to place since the first Go assembly file I ever saw.

After the centered dot is the function name and the (SB) part. This is not syntax similar to function parameters in Go, this is actually part of Go assembly’s weird addressing scheme which goes SYMBOLNAME+offset(relative to register), which you’ll see more of later. Function names are always declared relative to a special make-believe register called SB for “stack base”. Functions always start at the stack base with no offset, so they’re always fn(SB), no matter how many arguments they take.

Next on Line 1, we have “,4,”. That argument to a function declaration tells the Go assembler how to set up the frame for this function call. The value 4 here means NOSPLIT, which tells the Go assembler not to add a frame preamble that lets the frame be split. The other possible values for this are described in the Go assembly documentation here, but NOSPLIT should be fine unless your function gets very large.

Finally, on Line 1, we have this “$16-8” argument. The dollar sign is meaningless. The first number is the size of this function’s frame (16 bytes) and the second number is the size of the arguments to this function, which live in the caller’s frame (8 bytes, the length of the single 64-bit pointer argument to call0). If the function is set to NOSPLIT, these values are ignored. If the function is not set to NOSPLIT, they must be correct, so I try to set them correctly in case I turn NOSPLIT off at some point. The frame size appears to include the parameters from the caller’s frame as well as the return value from the current frame, so one way of thinking about this is that the frame extends from -8 (to include the argument passed to call0) forward 16 bytes (to include the return value from call0).

Now we’re finally off the first line and we get into the actual assembly!

Parameter and Return Naming

On the next line of assembly, we see that SYMBOLNAME+offset(relative to register) format again in a reference to another make-believe register FP (for “frame pointer”). The FP register points at the base of the current stack frame, and all references to the FP register are REQUIRED to have a symbol name associated with them or it’s a compile error. Those symbols correspond to the names of the arguments being passed in.

Go assembly is left-to-right, meaning that MOV instructions copy from the left to the right. The above line of assembly is copying the value found at 0 bytes from the base of the frame (FP), which is named “addr”, to the first arm64 register (R0).

As mentioned previously, the CALL operator in arm64 Go assembly maps to the BLR (branch-with-link-to-register) operator in native arm64 assembly. This calls the function whose address we passed in as an argument and copied to R0.

On the next line, we set up the return value. Since this is on arm64, we have to read the Parameter Passing part of the arm64 ABI to learn that function parameters are passed in on the R0-R8 registers and return values are passed back with the same registers. The first return value will come back on R0, just like the first argument was on R0. To copy this value to the last 8 bytes of our 16 byte frame, we again have to use the SYMBOLNAME+offset(relative to register) syntax. We assign it the name “ret” and copy it to an offset of +8 bytes relative to the frame pointer.

The final line is a RET instruction, which returns from the function.

Passing an Argument with CALL

Well, we’ve made it this far, let’s take a look at the call1 function, which takes two arguments from Go (the pointer to call and a single argument to pass to that function).

This is very similar to call0. Please notice that we’ve increased the frame size and size of arguments for call1 by 8 bytes each, because we’re taking one more argument from Go, which will be passed to the internal function.

Now we’re going to use R0 for the first argument to pass to the internal function, and R1 to hold the address of the internal function. So on the first line, we copy the first argument from Go (named “addr”) from the base of the frame pointer to R1. On the next line, we copy the second argument from Go (named “arg1”) from an offset of +8 from the frame pointer to R0. Then we CALL the function at the address in R1, which will use the argument we set up in R0 as its first argument. Then we copy the return result from that CALL from R0 to a value named “ret” at an offset of +16 from the frame pointer and return.

If that made sense to you, I regret to inform you that you have already gone mad. My condolences, but now you can join my support group.

Other Notes

symbolname+8(SP) is referring to a different register than 8(SP)!

Similar to the make-believe registers FP and SB, there is another make-believe Go assembly register SP for “stack pointer”. While this is a real register on some platforms, on arm64, it is emulated. What’s worse is that there is another register on arm64 called SP which is not the stack pointer.

To resolve this conflict, Go assembly does something truly confusing. When the SYMBOLNAME+offset(SP) syntax is used (required when referencing the stack pointer as well, where the names are for local variables), Go assembly interprets this as meaning the “stack pointer” SP register and not the native arm64 register named SP. However, if you use just the offset(SP) syntax, it will refer to the native arm64 register instead.

GodBolt for arm64

Using this link, you can load GodBolt in arm64 mode, where you can code in C and immediately see the arm64 native assembly that the compiler generates.

Then you can go and search for each operator from the native assembly in the Go docs for the arm64 assembler and check if they have been renamed and how the operator order has changed.

In this way, it is possible to slowly convert native assembly to Go assembly. However, you will also have to remove the alignment code that the native compiler generates, because Go assembly will do this for you. You also have to replace the native assembly that references stack values with named references to the FP and SP pointers, for function arguments and local variables respectively. It’s probably best to use this method of conversion only for certain parts of native functions and to manually write the parts that reference FP and SP.


Go assembly was an attempt to make a slightly more universal style of assembly back before the invention of the modern generation of platform-independent intermediate representations like LLVM-IR. I would guess that its existence as an integral part of Go’s implementation is a throwback to Go’s plan9 origins, rather than a Good Thing.

I hope that sharing my suffering and the small bit of knowledge I’ve gained will help you in getting through whatever it was that brought you to this page in the first place… and I’m quite impressed with your tolerance for utter madness if you’ve made it this far.

Stay tuned to this channel, my next post will be what I actually made with Go assembler, and it’s a doozy! Here’s a hint, it’s the Go arm64 assembly implementations of call0 up to call6.

“I find this type of hackery distasteful.” – Rob Pike, referring to my crew

Interview with egyp7

In this episode of the Hack the Planet Podcast:

We talk red-teaming and CCDC with egyp7, volunteer for the National CCDC Red Team.

We go over war stories from CCDC Nationals, the early days of Metasploit and browser autopwn, as well as what’s been working well on professional red team engagements in the cloud era, advice on building wordlists, fun shell one-liners, and favorite offensive tools and exploits.


egyp7’s links:

WebLogic CVE-2019-2725:
Sliver C2:
Linux Exploit Suggester:
hasherazade’s PE Bear:

Be a guest on the show! We want your hacker rants! Give us a call on the Hacker Helpline: PSTN 206-486-NARC (6272) and leave a message, or send an audio email to

Original music produced by Symbol Crash. Warning: Some explicit language and adult themes.

Interview with Lei

In this episode of the Hack the Planet Podcast:

We talk with Lei, long-time Defcon goon and founder of Disconnect Camp, about how to recover from infosec burnout, the origin story of Disconnect Camp, some war stories from his tenure as a Defcon goon, and how to keep your cool in a pandemic when you’ve already been dealing with burnout for years.

Lei’s links:
Disconnect Camp:

Frustration-Aggression Hypothesis:

Be a guest on the show! We want your hacker rants! Give us a call on the Hacker Helpline: PSTN 206-486-NARC (6272) and leave a message, or send an audio email to

Original music produced by Symbol Crash. Warning: Some explicit language and adult themes.

Disable Unmuted Autoplay in Chrome version 62 and above

Does it seem like Chrome used to do a better job at NOT automatically playing video? Having problems with unmuted video or audio automatically playing when you visit certain sites? Have you gone looking for the old autoplay settings only to discover they’re not in Chrome at all anymore?

You are not alone. In this post, we will first tell you how to fix it, then if you’re interested, keep reading for details about the changes made in Chrome, why they suck, and how we figured out how to disable them. That way, when they change things again, you’ll be able to work out how to handle it.

The Solution:

For Windows, right-click on the icon you click on to start Chrome. If this is on the taskbar on Windows 10, right-click on the taskbar icon, then move up to Google Chrome and right-click on that as well. Then click on Properties, this will open the Google Chrome Properties dialog.

In the Target field, you should see this:

Click to edit the Target field, Ctrl-A to select everything, hit Backspace to delete it all, and paste the following:

Click Apply, Click OK, and you’re done.

For Linux/OSX, the solution is the same. Find the icon you’re clicking on to start Chrome, edit the properties, and add the same command line flags after the location of the Chrome binary:

That’s it. Restart Chrome and you should have the old behavior back. Videos might still autoplay on some sites, but they should always be muted until you click on them.

You make sure that it’s off if the chrome://media-engagement/ link stops working! Without this fix, that link will show your current Media Engagement settings and what data has been logged.

What changed? Why does this fix work?

Back in version 62 of Chrome, they added a feature they called Media Engagement Index (MEI), which keeps a log of how many times you actually click on video and audio on various sites. Once you’ve actually clicked on a video on a site a certain number of times, it AUTOMATICALLY DISABLES AUTOPLAY PROTECTIONS for that site. What’s worse than that, they preload a list of sites that get a free bypass of autoplay protections, which includes many porn sites.

Deciding that they did such a good job with this feature, they then proceeded to remove the autoplay settings from the interface in the browser.

Kind of shitty behavior. I guess they never figured that people might want autoplay disabled all the time, even on sites they use frequently or even on the magical list of sites that Google decided get a free pass. Maybe they were just trying to get more people to accidentally blast the audio from porn sites? Otherwise I’m not sure why anyone thought this was a good idea.

Fortunately, you can still disable these features from the command line using the –disable-features flag.

Our recommended fix disables four features, which restore the old autoplay behavior, disable the preloaded bypass list, and completely disable the extra tracking of your media consumption:

PreloadMediaEngagementData – Disabling this feature will disable the list of sites that Google has pre-determined should be able to bypass autoplay protections.

MediaEngagementBypassAutoplayPolicies – Disabling this feature disallows sites that you use regularly to bypass autoplay protections.

RecordMediaEngagementScores – Disabling this feature turns off the Media Engagement tracking altogether.

RecordWebAudioEngagement – Disabling this feature turns off the Media Engagement tracking for web audio.

Try enabling and disabling those features individually if you want to further tune this behavior.

Don’t Take Our Word For It – Look at the Code!

You can search the Chromium source code here:

This can show you all the other features you might want to disable or enable from the command line. For example, searching for one of our flags, PreloadMediaEngagementData, brings us to a file called in the Chrome source. This is how we found the flags to disable the whole MEI system, and there are many other feature flags in there you might want to play with.

You can also use the Chromium code search to find out how these feature flags are actually used. Searching again for our flag, we can also see the file, which has all of the logic for the MEI features and exactly how and when these flags are used!

If things change in the future, check back on these two files to see if they’ve added more features or logic you need to disable.

What is up with the Preload List? Porn gets to bypass autoplay? Really?

From the Chromium code search, we searched for PreloadMediaEngagementData and found where it loads the list of sites that get to bypass autoplay. It’s coming from a protobuf file called preloaded_data.pb which you can find in your Chrome application folder. On our test machine (version 88), this was at:

Protobuf is a binary data encoding from Google, so you can’t just read it. Being lazy, we just searched Github for preloaded_data.pb, and found this nice Python script , courtesy of NeatMonster, to decode this file to plain text (mirror).

Included in that gist is the list of preloaded list of sites that can bypass autoplay, and you can see sites like pornhub and xhamster in there, among a bunch of other questionable sites for this privilege.

But again, don’t take our word for it, you can run this yourself. Copy your preloaded_data.pb file out of the Chrome folder and into a temporary folder (or Downloads, etc.), save the file to the same folder, and run it from the command line (requires Python):

That will spit out the current contents of the autoplay bypass list for your installed version of Chrome.

Not exactly a list of sites you want to have just blast audio without your explicit permission, is it?

Interview with Vi Grey

In this episode of the Hack the Planet Podcast:

We meet with Vi Grey who answers all the questions we’ve had about the Nintendo Entertainment System since we were kids but were too afraid to ask. A prolific developer of homebrew NES ROMs, Vi Grey helps us understand the present and future of innovation on the NES platform. We also discuss his work with polyglot files featured in PoC||GTFO. This episode itself is in fact a polyglot, check the mp3 metadata of the file on the RSS feed for more information.

Vi Grey’s links:
I Dream of Game Genies (HOPE 2018 talk):
Twitch Stream:
More at

Brad Smith on Light Guns on modern TV’s:
Damien Yerrick (more homebrew tools):
Tom7 (more NES hacks):


Be a guest on the show! We want your hacker rants! Give us a call on the Hacker Helpline: PSTN 206-486-NARC (6272) and leave a message, or send an audio email to

Original music produced by Symbol Crash. Warning: Some explicit language and adult themes.

Swarm Intelligence with Pongolyn

In this episode of the Hack the Planet Podcast:

We have a chat with Pongolyn, a community organizer and strategist for the Pacific Northwest Englightend, one of the largest teams in the augemented reality game Ingress. We discuss the key elements needed to develop swarm intelligence and how they were applied to continent-spanning efforts.

Pongo has spent years deconstructing her experience into a valuable set of strategies for anyone organzing large numbers of volunteers, and expertly up-levelling them into easily digestible lessons on swarm-based strategies, gamification, and game theory for people that never played Ingress.

If you’ve ever had to organize a protest or a podcast, this episode is for you!

Pongolyn’s talks:
BSides Portland 2019 –
Toorcamp 2018 –
Defcon 26 –

SwarmWise – The Tactical Manual to Changing the World by Rick Falkvinge

Hannah Fry Ted Talk – Is life really that complex?

Screeps –

Be a guest on the show! We want your hacker rants! Give us a call on the Hacker Helpline: PSTN 206-486-NARC (6272) and leave a message, or send an audio email to

Original music produced by Symbol Crash. Warning: Some explicit language and adult themes.

Threat Modeling: None of Your Security Tools Help me Get More Money for my Security Program

In this episode of the Hack the Planet Podcast:

For too long, the confusion caused by the Adam Shostack/MS threat modeling “methodology” has prevented security teams from doing any productive risk analysis. That ends now. We clear up the confusion around what a threat model is, what it’s for, how best to go about developing one, what is so very very wrong with the Adam Shostack/MS method of threat modeling, and how to achieve better results with less effort and arguing.

Check out the links for useful templates and examples. And remember: a dataflow diagram is an important piece of design documentation, but it is not and can never be an effective threat model.

Threat Modeling Template Examples from SymbolCrash, adjust these to suit!

Simple Threat Model Example:

CVSS 3.1 Auto-calculating Model with Automatic Coloring by Severity:

“How to measure anything in cybersecurity risk”

CVSS 3.1 Calculator at

Automated Secrets Detection:

Old-School SANS Threat Modeling Template Example:

Mentioned Tools:

C4 model:

What is the Actual Financial Impact of a Breach?

Threat Modeling Tools that uselessly force everything into a DFD (not recommended):
ThreatModeler –
Irius Risk –
OWASP ThreatDragon –
MS Threat Modeling Tool –

Be a guest on the show! We want your hacker rants! Give us a call on the Hacker Helpline: PSTN 206-486-NARC (6272) and leave a message, or send an audio email to

Original music produced by Symbol Crash. Warning: Some explicit language and adult themes.

Golang Offensive Tools with C-Sto and capnspacehook

In this episode of the Hack the Planet Podcast:

We talk with some of the most prolific developers of Golang offensive tools, from opposite points on the globe, about why they use Go, what they’ve been working on, how to work around some of Go’s challenges for red teams, and where things are going in the near future with Go malware. Featuring C-Sto (bananaphone/goWMIexec) and capnspacehook (pandorasbox/garble).

List of Golang Security Tools:




Command and Control:


Of interest, but breaks Docker & Terraform:

Be a guest on the show! We want your hacker rants! Give us a call on the Hacker Helpline: PSTN 206-486-NARC (6272) and leave a message, or send an audio email to

Original music produced by Symbol Crash. Warning: Some explicit language and adult themes.

Interview with Josh Pitts

In this episode of the Hack the Planet Podcast:

We talk with Josh Pitts, creator of The Backdoor Factory, ebowla, and SigThief, about the backstory of some of these tools and the offensive open-source tools debate. Featuring Vyrus and fast Dan.

Pitts Links:

Golang rewrite:

BananaPhone / Hell’s Gate:

More Code Signature Bypasses:
dylib TOCTOU:
linux by design:

Copy-Paste Compromises:


Be a guest on the show! We want your hacker rants! Give us a call on the Hacker Helpline: PSTN 206-486-NARC (6272) and leave a message, or send an audio email to

Original music produced by Symbol Crash. Warning: Some explicit language and adult themes.