Pkg.SiliconShellcodeEmulator — API for emulating Windows shellcodes

Overview

The Pkg.SiliconShellcodeEmulator module contains the API for emulating Windows shellcodes.

Extending the Emulator

Sometimes it may be necessary to instrument the emulator or to extend its functionality to add support for specific features or APIs. In this section we’ll show how to extend the emulator by adding a handler for an unsupported API.

As sample we use a shellcode which calls the ‘MessageBoxA’ API, which at the time of writing is not yet supported by the emulator.

We searched for a shellcode calling the ‘MessageBoxA’ API on the web and found the following:

31C9F7E1648B41308B400C8B7014AD96
AD8B58108B533C01DA8B527801DA8B72
2001DE31C941AD01D881384765745075
F4817804726F634175EB817808646472
6575E28B722401DE668B0C4E498B721C
01DE8B148E01DA89D531C95168617279
41684C696272684C6F61645453FFD268
6C6C616166816C240261616833322E64
685573657254FFD0686F78416166836C
2403616861676542684D6573735450FF
D583C41031D231C9526850776E6489E7
52685965737389E152575152FFD083C4
10686573736166836C2403616850726F
6368457869745453FFD531C951FFD0

To convert the text to data, just paste it into a text editor in Cerbero Suite and then press Ctrl+R → Conversion → Text to bytes.

Once we have the shellcode in a hex view, we can launch the Silicon Shellcode Emulator specifying the x86 architecture.

In the emulator workspace we open a new Python editor view and paste the following code.

def handler_MessageBoxA(emu):
  hwnd = text_addr = title_addr = mtype = None
  # handle both the x86 and x64 stack
  arch = emu.getArch()
  if arch == "x86":
    stack = emu.getRegister("esp")
    hwnd = emu.readValue(stack + 4, 4)
    text_addr = emu.readValue(stack + 8, 4)
    title_addr = emu.readValue(stack + 12, 4)
    mtype = emu.readValue(stack + 16, 4)
  elif arch == "x64":
    hwnd = emu.getRegister("rcx")
    text_addr = emu.getRegister("rdx")
    title_addr = emu.getRegister("r8")
    mtype = emu.getRegister("r9d")
  # read the captions and print them to the output view
  text = emu.tryStringRead(text_addr, encoding="ascii") if text_addr else ""
  title = emu.tryStringRead(title_addr, encoding="ascii") if title_addr else ""
  print('emulated MessageBoxA("%s", "%s")' % (text, title))
  # set the return value
  emu.setReturnValue(0)
  # readjust the stack and return
  if arch == "x86":
    emu.emulateReturn(0x10)
  elif arch == "x64":
    emu.emulateReturn()
  # if we return False, the emulator pauses the execution
  return True

# add the handler to the emulator
sseGetEmulator().addHook("MessageBoxA", handler_MessageBoxA)

We execute the code with Ctrl+E and then press F9 to emulate the shellcode.

The emulator will display the following text in the output view.

simulated API: GetProcAddress("kernel32.dll", "LoadLibraryA") = OK
simulated API: LoadLibraryA("User32.dll") = OK
simulated API: GetProcAddress("user32.dll", "MessageBoxA") = OK
emulated MessageBoxA("Yess", "Pwnd")
simulated API: GetProcAddress("kernel32.dll", "ExitProcess") = OK
simulated API: ExitProcess() = PAUSED

The handler correctly handled the ‘MessageBoxA’ API.

As can be observed, in the added code we check the current architecture and handle the stack accordingly. This approach is low-level but intuitive for reverse engineers. In the future we’ll add an optional higher level interface to automatically handle the stack.