Skip to content

Windows Emulation

xwings edited this page Jul 6, 2025 · 2 revisions

Windows Emulation in Qiling

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.

PE Loader

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.

Windows API Emulation

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.

How it Works

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')

Supported DLLs

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.

Registry Emulation

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()

Filesystem and Environment

  • 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 SystemRoot and USERPROFILE, is emulated and can be customized.

Running Drivers and Shellcode

  • Drivers (.sys): Qiling can load and emulate Windows drivers. You need to set the OS type to QL_OS.WINDOWS and 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.

Example: Hooking CreateFileA

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.")

How It Works

  1. @hook_api Decorator: 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.
  2. Callback Function: The hook_createfilea function is our implementation. It receives the Qiling object and a dictionary of the API's parameters, which Qiling helpfully parses for us.
  3. Intercept and Control: Inside the hook, we print the filename being accessed. We then demonstrate how to take control by setting the EAX register (which holds the return value for stdcall functions) to INVALID_HANDLE_VALUE and stopping the emulation. This effectively blocks the file operation.
Clone this wiki locally