← Reports

Ransomware · Ransomworm · Worm

WannaCry - invoice_greenanimals.pdf.exe

Author
Moise Medici
Updated
15 Feb 2026 · Completed
Difficulty
Medium
Platform
Windows
Capabilities
Command and Control C2 CommunicationCommand Execution via Powershell Cmd BashFile EncryptionPersistence Mechanisms
Tags
C++WannaCryptor

Question 12

One idea at this stage is to run the sample in x32dbg until tasksche.exe is written to disk. Looking at resource_extraction_c_windows, the resource is loaded and then the file is written. The DAT_xxxxx variables have been renamed into PTR_<ApiCall> based on the syscall that created them. For example, the CreateProcessA string is pushed on the stack, GetProcAddress is called, and the resulting pointer, saved in EAX, is moved to a variable that is then renamed PTR_CreateProcess.

invoice_greenanimals.pdf.exe
00407d07 PUSH s_CreateProcessA_004313a4 ; LPCSTR lpProcName for GetProcAddress
00407d0c PUSH ESI ; HMODULE hModule for GetProcAddress
00407d0d CALL EDI=>KERNEL32.DLL::GetProcAddress
00407d0f PUSH s_CreateFileA_00431398 ; LPCSTR lpProcName for GetProcAddress
00407d14 PUSH ESI ; HMODULE hModule for GetProcAddress
00407d15 MOV [PTR_CreateProcess], EAX ; = 00000000

The full list of CALLs in the function is:

invoice_greenanimals.pdf.exe
00407cef CALL dword ptr [->KERNEL32.DLL::GetModuleHandleW] ; = 0000a5d8
00407d0d CALL EDI=>KERNEL32.DLL::GetProcAddress
00407d1a CALL EDI=>KERNEL32.DLL::GetProcAddress
00407d27 CALL EDI=>KERNEL32.DLL::GetProcAddress
00407d34 CALL EDI=>KERNEL32.DLL::GetProcAddress
00407d74 CALL dword ptr [->KERNEL32.DLL::FindResourceA] ; = 0000a5b6
00407d86 CALL dword ptr [->KERNEL32.DLL::LoadResource] ; = 0000a5a6
00407d95 CALL dword ptr [->KERNEL32.DLL::LockResource] ; = 0000a596
00407da9 CALL dword ptr [->KERNEL32.DLL::SizeofResource] ; = 0000a584
00407e01 CALL ESI=>MSVCRT.DLL::sprintf
00407e18 CALL ESI=>MSVCRT.DLL::sprintf
00407e2c CALL dword ptr [->KERNEL32.DLL::MoveFileExA] ; = 0000a576
00407e43 CALL dword ptr [PTR_CreateFile] ; = 00000000
00407e61 CALL dword ptr [PTR_WriteFile] ; = 00000000
00407e68 CALL dword ptr [PTR_CloseHandle] ; = 00000000
00407ee8 CALL dword ptr [PTR_CreateProcess] ; = 00000000
00407ef7 CALL dword ptr [PTR_CloseHandle] ; = 00000000
00407f02 CALL dword ptr [PTR_CloseHandle] ; = 00000000

So the MoveFileExA is the same code that has been discussed in question 7. The interesting part now is what happens before and after. Before that, the code follows a common pattern to extract resources from within the sample (see the Learn section Load Resources).

After MoveFileExA, a file is created and a process executed. The assumption is that the process execution will be a simple run of the new file. The key questions here are: is the file really the resource, and if so, can it be seen how it gets decrypted? Previously, attempts to unzip the dump from pestudio prompted for a password.

The steps taken are:

  • open the sample in x32dbg
  • set a breakpoint at WriteFile (bpx WriteFile)
  • Run
  • Run till return
  • run step by step until the end of the first CloseHandle, at instruction 00407E6E.

At that point, the file in C:\Windows has been written and still requires a password to be extracted. Stepping further down in the code, a potential command line being created can be seen:

x32dbg with EDX holding C:\WINDOWS\tasksche.exe /i, the command line built once the file has been written.
Fig. 16: x32dbg with EDX holding C:\WINDOWS\tasksche.exe /i, the command line built once the file has been written.

Note the /i after the name in EDX.

Once the call to CreateProcess 13 is reached, the lpCommandLine parameter is set to only C:\Windows\tasksche.exe /i. Given the following signature:

BOOL CreateProcessA(
[in, optional] LPCSTR lpApplicationName,
[in, out, optional] LPSTR lpCommandLine,
[in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes,
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] BOOL bInheritHandles,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCSTR lpCurrentDirectory,
[in] LPSTARTUPINFOA lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);

in x32dbg the second parameter closest to the call is the one assigned from EDX.

This reveals a mistake in the earlier assumption that the file was only a password-protected zip file. Dropping the file into pestudio shows that it is not just a zip; it also contains a new resource:

pestudio on the dumped tasksche, where the XIA resource is a PKZIP self-extracting archive of 3,446,325 bytes.
Fig. 17: pestudio on the dumped tasksche, where the XIA resource is a PKZIP self-extracting archive of 3,446,325 bytes.

This time the resource is indeed a password-protected archive.

Exeinfo PE identifies the dump as a password-protected ZIP archive whose first entry is b.wnry.
Fig. 18: Exeinfo PE identifies the dump as a password-protected ZIP archive whose first entry is b.wnry.

The next step is to open tasksche.exe in Ghidra and find the zip password. There is no operational value in finding this password, but it is a good learning exercise.

To find the password, the analysis starts by looking for the usual pattern of resource loading. From Window → Symbol References, search for the LoadResource function, and jump to where it is called (at 00401dde). There the usual code pattern appears:

tasksche.exe
00401dc3 CALL dword ptr [->KERNEL32.DLL::FindResourceA] ; = 0000da6c
00401dc9 MOV ESI, EAX
00401dcb TEST ESI, ESI
00401dcd JZ LAB_00401e07
00401dcf PUSH ESI ; HRSRC hResInfo for LoadResource
00401dd0 PUSH dword ptr [EBP + param_1] ; HMODULE hModule for LoadResource
00401dd3 CALL dword ptr [->KERNEL32.DLL::LoadResource] ; = 0000da5c
00401dd9 TEST EAX, EAX
00401ddb JZ LAB_00401e07
00401ddd PUSH EAX ; HGLOBAL hResData for LockResource
00401dde CALL dword ptr [->KERNEL32.DLL::LockResource] ; = 0000da4c
00401de4 MOV EDI, EAX
00401de6 TEST EDI, EDI
00401de8 JZ LAB_00401e07
00401dea PUSH dword ptr [EBP + param_2]
00401ded PUSH ESI ; HRSRC hResInfo for SizeofResource
00401dee PUSH dword ptr [EBP + param_1] ; HMODULE hModule for SizeofResource
00401df1 CALL dword ptr [->KERNEL32.DLL::SizeofResource] ; = 0000da3a

After that there are a few calls to other functions. There are two options:

  • look for how this function has been called (by examining the caller function and what it is doing)
  • look at each of the calls made inside this function

To decide, Window → Function Call Graph is used on the current function, which is renamed load_resources for easier tracking. All the functions it calls (and those they call) are expanded to see how deep the call stack goes. It is quite extensive, so it is more efficient to first see how load_resources itself is called.

The function call graph of load_resources, which calls LoadResource, LockResource and SizeofResource among others.
Fig. 19: The function call graph of load_resources, which calls LoadResource, LockResource and SizeofResource among others.

Using Window → Function Call Tree, the analysis moves back to the function that called load_resources. The code that precedes the call is:

tasksche.exe
004020c8 MOV dword ptr [ESP]=>local_6f8, s_WNcry@2ol7_0040f ; = "WNcry@2ol7"
004020cf PUSH EBX
004020d0 CALL load_resources ; undefined load_resources(undefined4 param_1,
; undefined4 param_2)

The MOV moves the value WNcry@2ol7 to the top of the stack, and EBX is then pushed on the stack. The function is then called with these two arguments. The string WNcry@2ol7 strongly resembles a password; trying it on the protected archive confirms that it works.