back to write-ups
PMA April 5, 2025

PMA Lab 19-1: Shellcode Analysis

Analyze the file Lab19-01.bin using shellcodelauncher.exe.

pmachapter-19

Lab 19-1

Analyze the file Lab19-01.bin using shellcode_launcher.exe.

Analysis

For this exercise I’ll use Ollydbg, since I couldn’t get x32dbg to run as a “just-in-time debugger”. In order to start the exercise we need to execute shellcode_launcher.exe as follows:

shellcode_launcher.exe -i Lab19-01.bin -bp

image-20250404004925268

Right away, Ollydbg opens and sets a breakpoint at the address 00350000, as mentioned in the command line output. Nothing really interesting so far, just the breakpoint and a jump to 00350100:

image-20250404004902858

As it is possible to see in the following image, there’s a big “nop-sled” which actually doesn’t use NOPs, but in a way its exactly the same, they have no impact in the code.

image-20250404005218810

These instructions start incrementing ecx, and in the end ecx is set to zero with a xor ecx, ecx. After that we see a decode stub. The code moves to CX the value 18D (it will be used a size counter). Then there’s an unconditional jump, followed by a call. When the call is made, the address of the next instruction is pushed into the stack. The call instruction moves EIP to 00350308:

image-20250404005406786

This is a rather simple decoding stub that uses the combination of call and pop, to locate itself in memory. The stub does the following:

  1. Obtains the encoded shellcode address using pop ESI;
  2. Pushes ESI to the stack, and then moves it to EDI. EDI will be used as a destination register, which means the shellcode will be decoded on top of itself (same address);
  3. ESI is loaded into AL using the instruction lods, this instruction moves the pointer to the string forward;
  4. The byte currently in AL is subtracted 41 (sub AL, 41), and then shifts DL to the left 4 bits (SHL DL, 4);
  5. Then, lods is called again, remember that the last lods instruction moved the pointer forward. Again this instruction loads ESI into AL;
  6. This time, AL is subtracted 41 (sub AL, 41), and added DL (ADD AL, DL);
  7. The stos copies the data item from AL, AX or EAX into EDI;
  8. The size counter (ecx) is decremented, and it will loop, by jumping back to 00350308, until ecx is zero.

This Stub is picking up the buffer that starts at 00350324, and every 2 encoded bytes become 1 decoded byte. Each decoded byte is the sum of the high nibble of the odd bytes and the low nibble of the even bytes.

At the end of the loop, the shellcode will be decoded, and the retn instruction will jump to it.

image-20250404112722981

This Shellcode uses the same technique as the decoder, there’s an unconditional jump, towards a call instruction that will basically push the following address (00350469) to the stack:

image-20250404114840116

When EIP gets to the address that was called, Ollydbg recognizes the string “URLMON”. Address 00350469, as seen in the previous image points to the beginning of the following bytes “55 52 4C 4D 4F 4E 00” (URLMON and a Null byte terminating the string):

image-20250404115120415

This string is going to be moved to EBX (pop ebx), and there’s a call to 0035039E:

image-20250404121627478

This function tries to look for the base address of kernel32.dll.

First, it gets a pointer to PEB through TEB (highlighted in yellow), and then accesses PEB_LDR_DATA (highlighted in red). The test eax, eax and the jump at 003503A7 will cause an infinite loop if a version of Win9x is detected.

Assuming the infected machine is running a supported version (Windows Vista or earlier), the second LDR_DATA_TABLE_ENTRY in the InInitializationOderLinks linked list is obtained. After that, EAX gets LDR_DATA_TABLE_ENTRY.DllBase (highlighted in blue).

Finally, the function will return. And start executing a function that is looking for an exported function using a hash. It will load the dllBase, and get an offset to the PE signature, and then loop over all the symbols, hashing them and comparing to the hash

image-20250404122650504

It is possible to see in the stack a big string (the hash) that is being used in the string compare:

image-20250404122854396

When this function finds the symbol that matches the hash, EIP will reach 0035037C. Here the address of the function the Shellcode is looking for in kernel32.dll is obtained using the AddressOfOrdinals array and the AddressOfFunctions array:

image-20250404123203197

All of this is explained in Pages 413 through 420 of PMA.

At some point it is possible to see a path being pushed into the stack. Part of that path is obtained by calling GetSystemDirectoryA:

image-20250404125323035

The full path can be seen in the following image:

image-20250404125355773

That path is used as a parameter to a call to URLDownloadToFileA. EAX holds the url being queried:

image-20250404123726977

Using InetSim it is possible to receive the HTTP Request:

image-20250404124247435

It is also possible to see that the Downloaded file is executed:

image-20250404132659720

This is done by calling WinExec:

image-20250404125426738

Finally, the shellcode obtains a handle to the current process:

image-20250404125554223

And terminates the process:

image-20250404125604768

Questions

1) How is the Shellcode encoded?

The Shellcode is encoded with a custom base-41-like encoding to stay within printable ASCII range. This Stub is picking up the buffer that starts at 00350324, and every 2 encoded bytes become 1 decoded byte. Each decoded byte is the sum of the high nibble of the odd bytes and the low nibble of the even bytes.even bytes.

2) Which function does the shellcode manually import?

There are several functions that the shellcode manually imports. It is easy to spot them after the Shellcode being decoded, since they will all be called with addresses that are pushed into the stack:

image-20250404132406564

In order, the functions that the shellcode manually import are:

  • LoadLibraryA

  • GetSystemDirectoryA

  • URLDownloadToFileA

  • WinExec

  • GetCurrentProcess

  • TerminateProcess

3) What network host does the shellcode communicate with?

The Network host used by the shellcode is http://www.practicalmalwareanalysis.com/shellcode/annoy_user.exe

4) What filesystem residue does the shellcode leave?

The Shellcode leaves a file on the filesystem (C:\Windows\system32\1.exe), which is the payload that has been downloaded and executed:

image-20250404132857131

5) What does the shellcode do?

This Shellcode finds kernel32.dll in memory, and manually imports some functions. Then it downloads a file from a C2 server and execute it.