valak: asm arg1 fix, NT_SUCCESS i32, byte scan off-by-one, etw guids

This commit is contained in:
2026-07-18 09:30:00 +01:00
committed by JYenn
parent cc60a24d98
commit a1f467199f
17 changed files with 552 additions and 910 deletions
+47 -35
View File
@@ -1,6 +1,7 @@
// PEB-based module and function resolution, everything resolved at runtime.
// peb-based runtime api resolution. walks InMemoryOrderModuleList via gs:[0x60].
// ror13 hash on module + export names, no GetProcAddress or GetModuleHandle needed.
const std = @import("std");
const win = @import("win32.zig");
const windows = std.os.windows;
const pe = @import("pe.zig");
pub const LIST_ENTRY = extern struct {
@@ -9,10 +10,10 @@ pub const LIST_ENTRY = extern struct {
};
pub const PEB_LDR_DATA = extern struct {
Length: win.ULONG,
Initialized: win.BYTE,
Padding: [3]win.BYTE,
SsHandle: win.LPVOID,
Length: windows.ULONG,
Initialized: windows.BYTE,
Padding: [3]windows.BYTE,
SsHandle: windows.LPVOID,
InLoadOrderModuleList: LIST_ENTRY,
InMemoryOrderModuleList: LIST_ENTRY,
InInitializationOrderModuleList: LIST_ENTRY,
@@ -22,23 +23,21 @@ pub const LDR_DATA_TABLE_ENTRY = extern struct {
InLoadOrderLinks: LIST_ENTRY,
InMemoryOrderLinks: LIST_ENTRY,
InInitializationOrderLinks: LIST_ENTRY,
DllBase: win.LPVOID,
EntryPoint: win.LPVOID,
SizeOfImage: win.ULONG,
FullDllName: win.UNICODE_STRING,
BaseDllName: win.UNICODE_STRING,
DllBase: windows.LPVOID,
EntryPoint: windows.LPVOID,
SizeOfImage: windows.ULONG,
FullDllName: windows.UNICODE_STRING,
BaseDllName: windows.UNICODE_STRING,
};
// PEB at GS:0x60 on x64.
pub const PEB = extern struct {
Reserved1: [2]win.BYTE,
BeingDebugged: win.BYTE,
Reserved2: [1]win.BYTE,
Reserved3: [2]win.LPVOID,
Reserved1: [2]windows.BYTE,
BeingDebugged: windows.BYTE,
Reserved2: [1]windows.BYTE,
Reserved3: [2]windows.LPVOID,
Ldr: *PEB_LDR_DATA,
};
// ror13 hash, case-insensitive.
pub fn hash_ror13(input: []const u8) u32 {
var hash: u32 = 0;
for (input) |c| {
@@ -60,19 +59,18 @@ pub fn get_peb() *PEB {
pub fn get_module_by_hash(hash: u32) ?*anyopaque {
const peb_ptr = get_peb();
const ldr = peb_ptr.Ldr;
const head = @as(*LIST_ENTRY, @ptrCast(&ldr.InMemoryOrderModuleList));
const head: *LIST_ENTRY = @ptrCast(&ldr.InMemoryOrderModuleList);
var entry = head.Flink;
while (entry != head) : (entry = entry.Flink) {
const le: *LDR_DATA_TABLE_ENTRY = @ptrCast(@alignCast(@as(*anyopaque, @ptrFromInt(@intFromPtr(entry) - @offsetOf(LDR_DATA_TABLE_ENTRY, "InMemoryOrderLinks")))));
const le: *LDR_DATA_TABLE_ENTRY = @fieldParentPtr("InMemoryOrderLinks", entry);
if (le.BaseDllName.Length == 0) continue;
const buf = le.BaseDllName.Buffer;
const buf = le.BaseDllName.Buffer orelse continue;
const len = le.BaseDllName.Length / 2;
var mod_hash: u32 = 0;
var i: usize = 0;
while (i < len) : (i += 1) {
for (0..len) |i| {
var c = buf[i];
if (c >= 'a' and c <= 'z') c -= 32;
mod_hash = (mod_hash >> 13) | (mod_hash << 19);
@@ -86,7 +84,9 @@ pub fn get_module_by_hash(hash: u32) ?*anyopaque {
}
fn resolve_forward_string(base_bytes: [*]u8, forward_rva: u32) ?*anyopaque {
const forward_str = @as([*:0]u8, @ptrCast(@alignCast(base_bytes + @as(usize, @intCast(forward_rva)))));
const forward_str = @as([*:0]u8, @ptrCast(@alignCast(
base_bytes + @as(usize, @intCast(forward_rva)),
)));
const str = std.mem.sliceTo(forward_str, 0);
const dot_pos = std.mem.indexOfScalar(u8, str, '.') orelse return null;
const dll_hash = hash_ror13(str[0..dot_pos]);
@@ -96,29 +96,42 @@ fn resolve_forward_string(base_bytes: [*]u8, forward_rva: u32) ?*anyopaque {
}
pub fn get_func_by_hash(base: *anyopaque, func_hash: u32) ?*anyopaque {
const base_bytes = @as([*]u8, @ptrCast(base));
const dos = @as(*pe.IMAGE_DOS_HEADER, @ptrCast(@alignCast(base_bytes)));
const base_bytes: [*]u8 = @ptrCast(base);
const dos: *align(1) const pe.IMAGE_DOS_HEADER = @ptrCast(@alignCast(base_bytes));
if (dos.e_magic != 0x5A4D) return null;
const nt = @as(*pe.IMAGE_NT_HEADERS64, @ptrCast(@alignCast(base_bytes + @as(usize, @intCast(dos.e_lfanew)))));
const nt: *align(1) const pe.IMAGE_NT_HEADERS64 = @ptrCast(@alignCast(
base_bytes + @as(usize, @intCast(dos.e_lfanew)),
));
if (nt.Signature != 0x00004550) return null;
const export_dir = nt.OptionalHeader.DataDirectory[0];
if (export_dir.VirtualAddress == 0 or export_dir.Size == 0) return null;
const exp = @as(*pe.IMAGE_EXPORT_DIRECTORY, @ptrCast(@alignCast(base_bytes + @as(usize, @intCast(export_dir.VirtualAddress)))));
const exp: *align(1) const pe.IMAGE_EXPORT_DIRECTORY = @ptrCast(@alignCast(
base_bytes + @as(usize, @intCast(export_dir.VirtualAddress)),
));
if (exp.NumberOfNames == 0 or exp.AddressOfFunctions == 0 or exp.AddressOfNames == 0 or exp.AddressOfNameOrdinals == 0) return null;
if (exp.NumberOfNames == 0 or exp.AddressOfFunctions == 0 or
exp.AddressOfNames == 0 or exp.AddressOfNameOrdinals == 0) return null;
const names = @as([*]u32, @ptrCast(@alignCast(base_bytes + @as(usize, @intCast(exp.AddressOfNames)))));
const funcs = @as([*]u32, @ptrCast(@alignCast(base_bytes + @as(usize, @intCast(exp.AddressOfFunctions)))));
const ords = @as([*]u16, @ptrCast(@alignCast(base_bytes + @as(usize, @intCast(exp.AddressOfNameOrdinals)))));
const names: [*]align(1) const u32 = @ptrCast(@alignCast(
base_bytes + @as(usize, @intCast(exp.AddressOfNames)),
));
const funcs: [*]align(1) const u32 = @ptrCast(@alignCast(
base_bytes + @as(usize, @intCast(exp.AddressOfFunctions)),
));
const ords: [*]align(1) const u16 = @ptrCast(@alignCast(
base_bytes + @as(usize, @intCast(exp.AddressOfNameOrdinals)),
));
var i: u32 = 0;
while (i < exp.NumberOfNames) : (i += 1) {
if (names[i] == 0) continue;
const name_ptr = @as([*:0]u8, @ptrCast(@alignCast(base_bytes + @as(usize, @intCast(names[i])))));
const name_ptr: [*:0]const u8 = @ptrCast(@alignCast(
base_bytes + @as(usize, @intCast(names[i])),
));
const name = std.mem.sliceTo(name_ptr, 0);
if (hash_ror13(name) == func_hash) {
@@ -138,7 +151,6 @@ pub fn get_func_by_hash(base: *anyopaque, func_hash: u32) ?*anyopaque {
var cached_kernel32: ?*anyopaque = null;
var cached_ntdll: ?*anyopaque = null;
// Three-tier: specific module, then kernel32, then ntdll, then full PEB walk.
pub fn resolve_api(module_hash: u32, func_hash: u32) ?*anyopaque {
if (module_hash != 0) {
if (get_module_by_hash(module_hash)) |base| {
@@ -158,11 +170,11 @@ pub fn resolve_api(module_hash: u32, func_hash: u32) ?*anyopaque {
const peb_ptr = get_peb();
const ldr = peb_ptr.Ldr;
const head = @as(*LIST_ENTRY, @ptrCast(&ldr.InMemoryOrderModuleList));
const head: *LIST_ENTRY = @ptrCast(&ldr.InMemoryOrderModuleList);
var entry = head.Flink;
while (entry != head) : (entry = entry.Flink) {
const le: *LDR_DATA_TABLE_ENTRY = @ptrCast(@alignCast(@as(*anyopaque, @ptrFromInt(@intFromPtr(entry) - @offsetOf(LDR_DATA_TABLE_ENTRY, "InMemoryOrderLinks")))));
const le: *LDR_DATA_TABLE_ENTRY = @fieldParentPtr("InMemoryOrderLinks", entry);
if (cached_kernel32) |ck| if (le.DllBase == ck) continue;
if (cached_ntdll) |cn| if (le.DllBase == cn) continue;