Adventures in Shellcode Obfuscation Part 8: Shellcode as UUIDs
By Red Siege | August 6, 2024
by Mike Saunders, Principal Security Consultant
This blog is the eighth in a series of blogs on obfuscation techniques for hiding shellcode. You can find the rest of the series here. If you’d like to try these techniques out on your own, you can find the code we’ll be using on the Red Siege GitHub.
In January 2021, the Lazarus group was observed using a novel shellcode obfuscation technique. NCC Group’s analysis of the payload found Lazarus encoded their shellcode as UUIDs. A Universally Unique Identifier, or UUID, is a 128-bit label for information. UUIDs are also known as GUIDs, or Globally Unique Identifiers. When generated properly, a UUID is effectively a unique identifier.
Before we talk about why the Lazarus group chose to use UUIDs, it might be helpful to take a look at how we translate shellcode into a UUID. In the example below, we can see the first 16 bytes of our shellcode printed out by xxd. Below that is a helpful diagram taken from Bobby Cooke’s Ninja_UUID_Runner project. As you can see, the first 8 bytes are placed in reverse byte order in groups of 4, 2, and 2.
// xxd met.bin // 00000000: fc 48 83 e4 f0 e8 cc 00 00 00 41 51 41 50 52 51 // _______|__|__|__| | | // | ____|__|__| __|__| // | | __|__| | | // | | | |_ | | // | | | | | | // "e4 83 48 fc - e8 f0 - 00 c0 -0000-415141505251",
As each UUID is constructed of 16 bytes of shellcode, if the number of bytes of shellcode is not evenly divisible by 16, then we’ll need to pad out the UUID using NOP bytes (x90). If our shellcode is 598 bytes, then we will have 37 full UUIDs (598 / 16 = 37.375). That means we’ll need to pad our remaining bytes with 10 NOPs (598 % 16 = 6). Our final UUID will look like this: 56a2b5f0-d5ff-9090-9090-909090909090.
We know Lazarus used UUIDs to store shellcode, but why did they choose to do that? The answer is UuidFromStringA. UUIDFromStringA is a function included in Rpcrt4.dll that will convert a string into a UUID and returns a pointer to a UUID. The following code is from the Windows API reference page for UuidFromStringA.
RPC_STATUS UuidFromStringA( RPC_CSTR StringUuid, UUID *Uuid );
The function takes two parameters. StringUuid is a string representation of a UUID. *Uuid is a pointer to a UUID in binary form. This function allows us to call UuidFromStringA, pass it a string representation of a UUID and a pointer to a memory location, effectively copying our shellcode into a memory buffer! This allows us to avoid the normal copying signatures associated with the allocate memory > copy > execute workflow associated with shellcode loaders.
The following will take our UUIDs, convert them to shellcode, and copy them to a memory buffer we can use to execute. The first line calculates the size of the buffer needed to store our recovered shellcode. The second line declares a new variable, buffer, where we will store our recovered shellcode. The third line declares a new void pointer. We’ll use bufferBaseAddress to keep track of where we are in our buffer as we’re copying in shellcode. Next, we have an integer, i , that we’ll use to keep track of how much shellcode we’ve written to the buffer.
We use a for loop to iterate through each position in our array of UUIDs. Each array position contains one UUID. The first line inside the loop gets the base address of our buffer. As i will be 0 on the first iteration of the loops, this gives us the base address of the buffer. We then call UuidFromStringA, pass it the first UUID string from our array, and a pointer to the base address of our buffer. Because each UUID represents 16 bytes of shellcode, we increment i by 16, shifting bufferBaseAddress by 16 bytes. This is necessary to prevent overwriting any of the bytes we just wrote to the buffer.
// get the size of our shellcode stored as UUIDs
unsigned int shellcode_size = (unsigned int)sizeof(UUIDs) * 2;
// Declare a buffer for storing our shellcode
void * buffer = VirtualAlloc(NULL, shellcode_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
// This keeps track of our current position in the allocated buffer
void * bufferBaseAddress = NULL;
// This keeps track of how many bytes we've written into the buffer
int i = 0;
// Loop through our list of UUIDs and use UuidFromStringA to convert and load into memory
for (int count = 0; count < sizeof(UUIDs) / sizeof(UUIDs[0]); count++) {
bufferBaseAddress = ((ULONG_PTR)buffer + i);
RPC_STATUS status = UuidFromStringA((RPC_CSTR)UUIDs[count], bufferBaseAddress);
i += 16;
}
And that’s it! This technique has been around for a few years, so it’s likely that some vendors will recognize that if it looks like a UUID, it might not be one. Looking at the results on VirusTotal, that certainly appears to be the case.

UUID Shellcode VT Score
If you’re concerned your target’s AV/EDR might detect your shellcode, you could take your shellcode, encrypt it, convert it to UUID strings, and then call your decryption routine immediately before passing it UuidFromStringA.
If you’ve read the NCC Group analysis of the Lazarus Group’s loader, you’ll know that Lazarus used an interesting callback function, EnumSystemLocalesA. Executing shellcode isn’t within the scope of this series, but I’d encourage you to read up on this callback function to understand why they used it and how it works. If you want to know about other callback functions you could use to execute shellcode, check out Ali Ahmad’s excellent AlternativeShellcodeExec repo and Karttoon’s Trigen project.
You can find the example code for this article as well as the other articles in this series at the Red Siege GitHub.
This blog is part of a larger series on obfuscation techniques. Stay tuned for our next installment!
About Principal Security Consultant Mike Saunders

Mike Saunders is Red Siege Information Security’s Principal Consultant. Mike has over 25 years of IT and security expertise, having worked in the ISP, banking, insurance, and agriculture businesses. Mike gained knowledge in a range of roles throughout his career, including system and network administration, development, and security architecture. Mike is a highly regarded and experienced international speaker with notable cybersecurity talks at conferences such as DerbyCon, Circle City Con, SANS Enterprise Summit, and NorthSec, in addition to having more than a decade of experience as a penetration tester. You can find Mike’s in-depth technical blogs and tool releases online and learn from his several offensive and defensive-focused SiegeCasts. He has been a member of the NCCCDC Red Team on several occasions and is the Lead Red Team Operator for Red Siege Information Security.
Certifications:
GCIH, GPEN, GWAPT, GMOB, CISSP, and OSCP
Related Stories
View MoreBy Red Siege | October 5, 2026
by Stuart Rorer Most weeks, the tests I run are fairly routine: web applications, externals, and internals with a tight scope. But mobile application assessments have a way of getting […]
Learn MoreBy Red Siege | October 1, 2026
by Mike Saunders The AWS ReadOnlyAccess policy might be providing more access than you’re intending to grant. On a recent assessment, we observed widespread use of IAM roles with the […]
Learn MoreBy Red Siege | September 24, 2026
As proud sponsors of Wild West Hackin’ Fest, Red Siege is heading back to Deadwood! WWHF is always a big one for us, and this year we’re bringing training, talks, […]
Learn More