-
Notifications
You must be signed in to change notification settings - Fork 756
Windows Emulation
Qiling Framework provides extensive support for emulating Windows binaries (PE files). This includes handling the PE file format, emulating the Windows API, and managing the Windows-specific environment.
Qiling's PE loader is responsible for parsing Windows executables (.exe), dynamic-link libraries (.dll), and drivers (.sys). It correctly maps the different sections of the PE file into memory, resolves imports, and sets up the initial process state.
A significant part of Windows emulation is handling the vast Windows API. Qiling provides built-in emulation for hundreds of the most common API calls from core DLLs like kernel32.dll, user32.dll, advapi32.dll, and ntdll.dll.
When the emulated program calls a Windows API function (e.g., CreateFileW), Qiling intercepts the call. Instead of executing the real function from the host OS, Qiling runs its own Python-based implementation of that function. This implementation mimics the behavior of the real API, interacting with Qiling's virtual environment.
Example: Emulating GetVersionExA
By default, Qiling reports itself as a specific version of Windows (e.g., Windows 10). You can override this to trick a program that is looking for a particular version.
# Define a custom implementation for GetVersionExA
def my_getversion(ql, address, params):
# Get the address of the OSVERSIONINFOA structure
version_info_addr = params['lpVersionInformation']
# Write fake version info (e.g., Windows XP)
# dwMajorVersion = 5, dwMinorVersion = 1
ql.mem.write_int(version_info_addr + 4, 5, 4)
ql.mem.write_int(version_info_addr + 8, 1, 4)
# Set return value to 1 (success)
ql.reg.eax = 1
# Replace the default API handler
ql.os.set_api("GetVersionExA", my_getversion, 'kernel32.dll')Qiling has varying levels of support for many standard Windows DLLs. The implementation is constantly growing. You can find the implemented APIs in the qiling/os/windows directory of the Qiling source code.
Qiling includes a virtual Windows Registry. This is crucial for malware analysis, as many malicious programs use the registry for persistence or configuration.
- In-Memory: The virtual registry is held in memory and is reset for each emulation run.
- Pre-seeding: You can pre-populate the virtual registry with specific keys and values to control the malware's execution path.
-
Monitoring: You can hook registry access functions (
RegOpenKeyExA,RegQueryValueExA, etc.) to see what the program is looking for.
Example: Creating a fake registry key
# Create a key and set a value before running the emulation
ql.os.registry.create_key(
r"HKEY_LOCAL_MACHINE\Software\MyCompany",
{
"InstallPath": "C:\Program Files\MyCompany"
}
)
ql.run()- Rootfs: For Windows emulation, you need a Windows rootfs. This should contain the necessary system DLLs that your target program imports.
-
Drives: Qiling emulates Windows drive letters (e.g.,
C:). You can map host directories to these drives. -
Environment Variables: The Windows environment block, including variables like
SystemRootandUSERPROFILE, is emulated and can be customized.
-
Drivers (
.sys): Qiling can load and emulate Windows drivers. You need to set the OS type toQL_OS.WINDOWSand the profile to one that includes the driver-specific setup. - Shellcode: You can run Windows shellcode by specifying the OS type. Qiling will set up a minimal environment, including a PEB (Process Environment Block), to make the shellcode feel at home.
Qiling's Windows emulation provides a powerful and flexible sandbox for analyzing Windows software in a controlled and observable way.
Here is a complete, runnable example that emulates a simple Windows binary and uses an API hook to intercept file creation operations. This is a common technique for understanding a program's file I/O behavior.
Target Binary (test.exe): Imagine this binary attempts to create a file named C:\test.txt.
Qiling Script:
from qiling import Qiling
from qiling.const import QL_VERBOSE
from qiling.os.windows.api import hook_api
# This is the callback function that will replace the original CreateFileA
@hook_api("CreateFileA", "kernel32.dll")
def hook_createfilea(ql: Qiling, address: int, params: dict):
# Extract the filename from the function's parameters
filename = params["lpFileName"]
print(f"\n[+] Hooked CreateFileA! The program is trying to access: {filename}")
# To demonstrate control, we can deny the operation.
# We set the return value to INVALID_HANDLE_VALUE (-1)
# and stop the emulation.
invalid_handle = 0xFFFFFFFF
ql.reg.eax = invalid_handle
ql.emu_stop()
return invalid_handle
if __name__ == "__main__":
# Path to the Windows executable and its rootfs
# The rootfs must contain kernel32.dll
executable_path = 'C:\\path\\to\\your\\test.exe'
rootfs_path = 'path/to/your/rootfs/x86_windows'
ql = Qiling([executable_path], rootfs_path, verbose=QL_VERBOSE.OFF)
# The hook is already set using the @hook_api decorator, so we can just run
print("[*] Starting emulation...")
ql.run()
print("[*] Emulation finished.")-
@hook_apiDecorator: This is a convenient way to set an API hook. We tell it the function name (CreateFileA) and the DLL (kernel32.dll) it belongs to. -
Callback Function: The
hook_createfileafunction is our implementation. It receives theQilingobject and a dictionary of the API's parameters, which Qiling helpfully parses for us. -
Intercept and Control: Inside the hook, we print the filename being accessed. We then demonstrate how to take control by setting the
EAXregister (which holds the return value forstdcallfunctions) toINVALID_HANDLE_VALUEand stopping the emulation. This effectively blocks the file operation.
- Home
- Getting Started
- Core Concepts
- Usage
- Features
- Tutorials
- Development
- Resources