
Reflective PE loader written in Zig. Loads and executes native and .NET PE files directly from memory.
Reflective PE loader written in Zig. Loads and executes native and .NET PE files directly from memory.
__security_cookie for /GS buffer security)SetProcessValidCallTargetsUtility functions for resolving exports from a loaded PE image:
getExportByName(base, ntheaders, name) — resolve by namegetExportByOrdinal(base, ntheaders, ordinal) — resolve by ordinalHandles forwarded exports automatically by loading the target DLL (e.g., "NTDLL.RtlAllocateHeap" resolves to the actual function in ntdll.dll).
Automatically resolves Windows API sets (api-ms-win-*, ext-ms-win-*) to their actual host DLLs using the PEB ApiSetMap. This enables loading PEs that import from virtual API set DLLs.
utils.detect_platform(buffer) — Detect if PE is 32-bit or 64-bitutils.is_dotnet_assembly(ntheaders) — Check if PE is a .NET assemblyutils.getDotNetVersion(buffer) — Extract .NET runtime version string from metadatautils.rvaToFileOffset(buffer, rva) — Convert RVA to raw file offsetClone the repository:
git clone https://github.com/Thoxy67/zig-pe.git
cd zig-pe
Build the project:
zig build
zig build -Ddotnet=false # Disable .NET support (enabled by default)
zig build # Build all targets
zig build run-putty64 # Run 64-bit native PE example
zig build run-putty32 # Run 32-bit native PE example
zig build run-dotnet # Run .NET assembly example
zig build test # Run unit tests
const pe = @import("pe");
pub fn main() !void {
// Load and execute an embedded PE file
try pe.RunPE.init(@embedFile("bin/app.exe")).run();
}
const pe = @import("pe");
pub fn main() !void {
var loader = pe.RunPE.init(@embedFile("bin/app.exe"));
try loader.runWithArgs(&.{ "arg1", "arg2", "arg3" });
}
const std = @import("std");
const pe = @import("pe");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const file_content = try std.fs.cwd().readFileAlloc(
allocator,
"path/to/executable.exe",
std.math.maxInt(usize),
);
defer allocator.free(file_content);
try pe.RunPE.init(file_content).run();
}
const pe = @import("pe");
// After loading a DLL into memory...
const base: [*]const u8 = @ptrCast(loaded_base);
const ntheaders = // ... get NT headers
// Resolve by name
if (pe.getExportByName(base, ntheaders, "ExportedFunction")) |func_ptr| {
const func: *const fn () void = @ptrCast(@alignCast(func_ptr));
func();
}
// Resolve by ordinal
if (pe.getExportByOrdinal(base, ntheaders, 42)) |func_ptr| {
// Use the function pointer
}
GetCommandLineW() returns the provided argumentsMain(string[] args) via CLR invocationThis project involves loading and executing arbitrary code, which can be potentially dangerous. Use this loader only with trusted PE files and in controlled environments. The authors are not responsible for any misuse or damage caused by this software.
Contributions to zig-pe are welcome! Please feel free to submit pull requests, create issues or spread the word.
git checkout -b feature/AmazingFeature)git commit -m 'Add some AmazingFeature')git push origin feature/AmazingFeature)This project is licensed under the MIT License - see the LICENSE file for details.
This project is for educational purposes only. Ensure you have the necessary rights and permissions before loading and executing any PE file.
| Architecture | Machine Code | Bitness |
|---|
| i386 | 0x014c | 32-bit |
| AMD64 | 0x8664 | 64-bit |
| IA64 | 0x0200 | 64-bit |
| ARM (ARMNT) | 0x01C4 | 32-bit |
| ARM64 | 0xAA64 | 64-bit |