In the previous blog post we have seen how to perform a shellcode process injection by finding a target process PID using several WinAPIs, in that case all the WinAPIs were called directly. Usually malwares resolve the WinAPI address at runtime in order to hide malicious behaviours during static analysis.
In this blog post we will see how to use two well-known WinAPIs to dynamically resolve the WinAPIs Address: GetModuleHandle used to get a module address and GetProcessAddress used to get a WinAPI address.
Runtime API Resolution
The API resolution we are going to develop is very simple and relies on two WinAPIs:
- GetModuleHandleA: retrieves a module handle of the specified module
- GetProcAddress: gets the address of a specified function in a DLL
Rustware Setup
Everything we need to develop a Rust program that leverages on WinAPI, is well described in the Microsoft “Developing with Rust on Windows”. In our case, we used the following software, plugins and crate:
- Visual Studio Code 1.83.0
- Rust-analyzer 0.3.1689
- CodeLLDB 1.10.0
- Crates 0.6.3
- Windows Crate 0.51.1
Rustware Development
First of all, it is necessary to add the Windows Crate and the features required by each WinAPI to the Cargo.toml file; we can see the features in the Windows Crate documentation.
The new two WinAPIs we are going to use require the following features:
- GetModuleHandleA: “Win32_System_LibraryLoader” and “Win32_Foundation”
- GetProcAddress: Win32_System_LibraryLoader” and “Win32_Foundation”
After adding the Windows Crate and all the WinAPIs features, the Cargo.toml file will look like this.
Each feature must also be imported in the code; we can achieve this with the use declaration as shown in the code below.
The resolve_api function takes two parameters, a HMODULE returned from GetModuleHandleAand the WinAPI name, it returns the function address or an error.
Since GetProcAddress is an unsafe function, we must use it in an unsafe block. In the following code we can see that the GetProcAddress takes the HMODULE and a PCSTR string (rappresenting the WinAPI name) as arguments and returns the WinAPI address or an error.
In order to use the return value from the resolve_api function, we need to define the WinAPI function pointer for all the WinAPIs we are going to use in the shellcode process injection; We can do it in Rust with the type keyword. Each WinAPI is defined as an unsafe extern “system”function.
For the OpenProcess, we can define a type OpenProcessAPI that is an unsafe extern “system”fuction, based on the Microsoft Documentation, it gets three parameters: a PROCESS_ACCESS_RIGHTS, a bool and a 32-bit unsigned integer, and return a HANDLE.
We can define the type for all the WinAPIs as shown in the code below.
I changed the get_pid and the inject functions in order to return a Result, because of this I used the ? operator in the expression that returns a Result (as GetModuleHandleA and transmute), in this way we can handle all the errors in the main function.
The main function was changed too, you can see the full code at the end of the blog post.
At this point we can get the kernel32 handle with GetModuleHandleA, if everything is ok the kernel32_module_handle variable will have the module address otherwise the main function will handle the error.
For each WinAPI, we must create a variable of the type we defined before and transmute the address returned by our resolve_api function.
At this point, we can call the WinAPIs, for example we can execute OpenProcess as shown below.
Using the target flag, we can specify the i686 architecture and compile the program into a 32bit binary.
Running it, it successfully resolves the WinAPI, finds the notepad.exe PID and injects our MessageBox into it.
Debugging
Using Process Hacker and x32dbg we can debug our binary to understand how it works under the hood. We set the breakpoints on the GetModuleHandleA and GetProcAddress WinAPI.
GetModuleHandleA
Running the debugger, we step on the GetModuleHandleA, we can see that it gets one parameter, the “kernel32.dll” string address.
GetModuleHandleA returns the value 0x76740000, so we can confirm it is the kernel32.dllmodule address by looking at its address using ProcessHacker.
GetProcAddress
Taking the CreateToolhelp32Snapshot as example, we can see the two parameters for GetProcAddress:
- 0x76740000 is the kernel32.dll module address
- 0x98F378 is the “CreateTool32helpSnapshot” string address.
The same happens for the remaining WinAPIs as shown in the following images.
We can see that our binary correctly resolved all the WinAPIs addresses.
Conclusion
Rust is a very powerful language; in the last years it found its way into the malware development, especially for ransomware because of its speed. The interaction with WinAPIs is not very easy because of the datatype mismatch.
I had several problems to define the correct function pointer type because I wasn’t defining it as external “system” and it wasn’t working.
The next steps are to encrypt all the strings and implement a custom version of GetModuleHandle and GetProcAddress.
Feel free to contact me, I’d appreciate any feedback.
References
- https://learn.microsoft.com/en-us/windows/dev-environment/rust/
- https://microsoft.github.io/windows-docs-rs/doc/windows/
- https://socradar.io/why-ransomware-groups-switch-to-rust-programming-language/
- https://crates.io/crates/windows
- https://doc.rust-lang.org/book/
- https://www.ired.team/offensive-security/code-injection-process-injection/process-injection
- https://institute.sektor7.net
- https://maldevacademy.com
By Raffaele Sabato
Team Leader – Cyber Offensive Solutions and Services
Consulthink S.p.A.