Because I want to learn more about reverse engineering, I did the MalwareTech Reversing Challenges and made a write-up of it.

String 1

Opening the binary in Ghidra shows the following disassembly of the function entry.


The memory address of a string is pushed on the stack. This memory address points to the string FLAG{CAN-I-MAKE-IT-ANYMORE-OBVIOUS}.


Strings 2

Opening the binary in Ghidra shows the following disassembly of the function entry. The function shows a long string being created on the stack. After converting hex to character the string FLAG{STACK-STRINGS-ARE-BEST-STRINGS} was shown.


Strings 3

The following pseudo-code is showed by Ghidra based on the entry function.


The function FindResource determines the location of the resource identified by rc.rc. And then a string is loaded from the file identified by the identifier 272 (0x110) with use of the function LoadStringA which Ghidra resolves in the disassembly view based on references.




Turn on WindowsResourceReference so that Ghidra resolves the string in the disassembly view.


Shellcode 1

The entry function shows a string that’s moved to the heap.


After moving the string to the heap the entry function copies a few bytes renamed to shellcode to memory and calls it like a function with CALL dword pt r [ EBP + shellcode_in_virtualmem].


The shellcode bytes are shown in the disassembly view and byte viewer of Ghidra:


To view the bytes as disassembly, select the bytes->right-click->Clear Code Bytes -> Disassembly. Now Ghidra shows the bytes as disassembly.


The shellcode loops over the string and executes the rol instruction ( Because the challenge did not allow debugging I recreated the function in Python.

string = [0x32, 0x62 ,0x0a ,0x3a ,0xdb ,0x9a ,0x42, 0x2a, 0x62, 0x62, 0x1a, 0x7a, 0x22, 0x2a, 0x69, 0x4a ,0x9a ,0x72 ,0xa2, 0x69 ,0x52, 0xaa, 0x9a, 0xa2, 0x69, 0x32 ,0x7a, 0x92, 0x69 , 0x2a, 0xc2, 0x82, 0x62, 0x7a,  0x4a, 0xa2, 0x9a, 0xeb]

rol = lambda val, r_bits, max_bits=8: \
    (val << r_bits%max_bits) & (2**max_bits-1) | \
    ((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))
flag = ""

for s in string:
    flag += chr(rol(s,5))

re@DESKTOP-S7VKEO8:/mnt/c/Users/re/Documents$ python

Shellcode 2

The function entry starts with a few characters that are moved to the stack. Then it creates a pointer to the heap and moves 4 values to it shown below.

  • heap_pointer[0] =LoadLibrary
  • heap_pointer[1] = GetProcAddress
  • heap_pointer[2]= flag
  • heap_pointer[3]= 0x24


After that, it copies shellcode from the data segment to memory and executes the shellcode with the heap_pointer as the first argument.


Let’s analyze the shellcode to retrieve the flag. To better analyze the shellcode in Ghidra I selected the shellcode -> right-click -> Create Function by doing this Ghidra names the characters that are moved to the stack. This will become handy when naming the variables for analysis later on. Here is the disassembly view of the string msvcrt.dll moved to the stack. This is the same for the strings below.

  • msvcrt.dll
  • kernel32.dll
  • fopen
  • fread
  • fseek
  • fclose
  • GetModuleFileNameA
  • rb

Without looking at remaining shellcode this already looks weird because kernel32.dll is already loaded to every process on Windows. The shellcode is trying to use library functions without begin caught by the imports windows of the binary.


After moving strings to the stack the same strings are used to import library functions. At the start it gets the address of msvcrt.dll and kernel32.dll with the user of GetProcAddress. After getting the addresses of the DLL’s it gets the addresses of the functions fopen, fseek, fread, fclose from the DLL’s in memory.


After the addresses of the functions are moved to a memory address, the file path of the current process that is executed is moved to file_name with use of GetModuleFileName. And then the file is opened on the offset 0x4e using fseek and reads 0x26 bytes from the file using fread. This resolves to the string This program cannot be run in DOS mode.


To get the string from the file I recreated the instructions in Python:

file = open("shellcode2", "rb")
string =
PS C:\Users\re\Documents\shellcode2> python .\
This program cannot be run in DOS mode

Before executing a while loop a string is moved from the heap_pointer to EDI.

The loop executes the instruction xor string[EDX], file_content[EDX] and increments the value in register EDX until EDX eqauls 0x24.


To get the flag from the file I recreated the instructions in Python:

file = open("shellcode2", "rb")
file_content =

string = [0x12, 0x24, 0x28, 0x34, 0x5b, 0x23, 0x26, 0x20, 0x35, 0x37, 0x4c, 0x28, 0x76, 0x26, 0x33, 0x37, 0x3a, 0x27, 0x3d, 0x6e, 0x25, 0x48, 0x6f, 0x3c, 0x58, 0x3a, 0x68, 0x2c, 0x43, 0x73, 0x10, 0xe, 0x10, 0x6b, 0x10, 0x06f]

flag = ""

for i in range(0x24):
    flag += chr(string[i] ^ ord(file_content[i]))

PS C:\Users\re\Documents\shellcode2> python .\
This program cannot be run in DOS mode


The entry function starts with copying 0x1fb bytes from the data segment to the heap. And then it calls unkown_function.


The function creates 3 variables arg_1, arg_2 and arg_3 based on the heap_pointer at offset 0xff.


The values moved to the variables are moved from the memory address [EDX + ECX * 0x1 + 0xff].

  • EDX=heap_pointer=0x404040
  • ECX=count=0
  • 0x404040 + 0xff = 0x40413f

Looking at the binary at the address 0x40413f shows the following.


Comparing the string_offset in the binary with the file ram.bin shows that they both contain the same values. ram.bin is a copy of the memory when vm1 got executed.


After creating the variables FUN_00402270 is called with the variables as arguments.


Based on the value of arg_1 it jumps to a specific memory address labelled with the value.



To better understand what the function does, I recreated the function in Python. But instead of opening the binary it opens ram.bin that was provided with the challenge.

file = open("ram.bin", "rb")
heap_pointer = bytearray(

counter = 0
random = 0

while True:
    arg_1 = heap_pointer[counter + 0xff]
    arg_2 = heap_pointer[1 + counter + 0xff]
    arg_3 = heap_pointer[2 + counter + 0xff]
    if arg_1 == 1:
        heap_pointer[arg_2] = arg_3
    if arg_1 == 2:
        random = heap_pointer[arg_2]
    if arg_1 == 3:
        heap_pointer[arg_2] ^= random
    if arg_1 > 3:

    counter += 3

PS C:\Users\re\Documents\vm1> python.exe .\


The entry function pushes two zero’s on to the stack and calls FUN_00401000. These are 2 function arguments that contain the value 0, but normally would be FUN_0040100(filename_unencrypted, encryption_key).


The function starts with creating a new filename called filename_encrypted. It opens filename_unecrypted copies the content to buffer_file1. So in short filename_unecrypted-> encryption_routine-> filename_encrypted.


The function loops over the bytes of the file and XOR’s the byte with a key buffer_file[i] ^= key[i] (i = increment). The key length is max 0x20 because EDX gets divided by 0x20.


I don’t have access to the key, but the challenge provided a few images (JPG) that we’re encrypted with the use of this binary. And because the encryption method uses XOR we could calculate some values and get the key. Like this key = encrypted file ^ possible value of unencrypted file.

Later on, I figured that the folder Sample Pictures is a standard folder in Windows 7. So I download the pictures from the


It opens the unencrypted file and XOR’s it with the encrypted file. The result of that is the encryption key. After that it uses the encrypton key to decrypt the encrypted flag flag.txt_encrypted.

koala_unencrypted =  bytearray(open("koala.jpg", "rb").read(0x20)) # unencrypted
koala_encrypted = bytearray(open("Koala.jpg_encrypted", "rb").read(0x20))

key = []
for i in range(0x20):
    key.append(koala_encrypted[i] ^ koala_unencrypted[i])
file = open("flag.txt_encrypted", "rb")
buffer_file = bytearray(

flag = ""
for i in range(len(buffer_file)):
    flag += chr(buffer_file[i] ^ key[i % 0x20])
re@DESKTOP-S7VKEO8:/mnt/c/Users/re/Documents/ransomware1/EncryptedFiles/Documents$ python