Unify heap API hooks, address debug CRT init issues

This commit is contained in:
Jacob Farnsworth
2025-04-02 16:47:04 +02:00
parent 92cd2d41db
commit 6adafd598b
2 changed files with 119 additions and 99 deletions
+46 -9
View File
@@ -49,6 +49,17 @@ def hook_HeapCreate(ql: Qiling, address: int, params):
return ql.os.heap.alloc(dwInitialSize)
def _HeapAlloc(ql: Qiling, address: int, params):
dwFlags = params["dwFlags"]
dwBytes = params["dwBytes"]
ptr = ql.os.heap.alloc(dwBytes)
if ptr and (dwFlags & HEAP_ZERO_MEMORY):
__zero_mem(ql.mem, ptr, dwBytes)
return ptr
# DECLSPEC_ALLOCATOR LPVOID HeapAlloc(
# HANDLE hHeap,
# DWORD dwFlags,
@@ -60,15 +71,7 @@ def hook_HeapCreate(ql: Qiling, address: int, params):
'dwBytes' : SIZE_T
})
def hook_HeapAlloc(ql: Qiling, address: int, params):
dwFlags = params["dwFlags"]
dwBytes = params["dwBytes"]
ptr = ql.os.heap.alloc(dwBytes)
if ptr and (dwFlags & HEAP_ZERO_MEMORY):
__zero_mem(ql.mem, ptr, dwBytes)
return ptr
return _HeapAlloc(ql, address, params)
# DECLSPEC_ALLOCATOR LPVOID HeapReAlloc(
# HANDLE hHeap,
@@ -86,6 +89,14 @@ def hook_HeapReAlloc(ql: Qiling, address: int, params):
base = params["lpMem"]
newSize = params["dwBytes"]
if not base:
return _HeapAlloc(ql, address, params)
if newSize == 0:
ql.os.heap.free(base)
return 0
oldSize = ql.os.heap.size(base)
oldData = bytes(ql.mem.read(base, oldSize))
@@ -150,3 +161,29 @@ def hook_HeapSetInformation(ql: Qiling, address: int, params):
@winsdkapi(cc=STDCALL, params={})
def hook_GetProcessHeap(ql: Qiling, address: int, params):
return ql.os.heap.start_address
# BOOL HeapValidate(
# HANDLE hHeap,
# DWORD dwFlags,
# LPCVOID lpMem
# );
@winsdkapi(cc=STDCALL, params={
'hHeap': PVOID,
'dwFlags': DWORD,
'lpMem': PVOID
})
def hook_HeapValidate(ql: Qiling, address: int, params):
hHeap = params['hHeap']
lpMem = params['lpMem']
if not hHeap:
return 0
# TODO: Maybe _find is a heap manager implementation
# detail, in which case we shouldn't rely on it.
chunk = ql.os.heap._find(lpMem)
if not chunk:
return 0
return chunk.inuse
+73 -90
View File
@@ -135,10 +135,9 @@ def hook__controlfp(ql: Qiling, address: int, params):
# );
@winsdkapi(cc=CDECL, params={
'func' : POINTER
})
}, passthru=True)
def hook_atexit(ql: Qiling, address: int, params):
ret = 0
return ret
return
# char*** __p__environ(void)
@winsdkapi(cc=CDECL, params={})
@@ -211,20 +210,12 @@ def hook___p___argc(ql: Qiling, address: int, params):
return ret
# TODO: this one belongs to ucrtbase.dll
@winsdkapi(cc=CDECL, params={})
@winsdkapi(cc=CDECL, params={}, passthru=True)
def hook__get_initial_narrow_environment(ql: Qiling, address: int, params):
ret = 0
for i, (k, v) in enumerate(ql.env.items()):
entry = bytes(f'{k}={v}', 'ascii') + b'\x00'
p_entry = ql.os.heap.alloc(len(entry))
ql.mem.write(p_entry, entry)
if i == 0:
ret = p_entry
return ret
# If the native version of this function does not
# get to run, then debug versions of the CRT DLLs can fail
# their initialization.
return
# int sprintf ( char * str, const char * format, ... );
@winsdkapi(cc=CDECL, params={
@@ -339,6 +330,18 @@ def __stdio_common_vsprintf(ql: Qiling, address: int, params, wstring: bool):
def hook___stdio_common_vsprintf(ql: Qiling, address: int, params):
return __stdio_common_vsprintf(ql, address, params, False)
@winsdkapi(cc=CDECL, params={
'_Options' : PARAM_INT64,
'_Buffer' : POINTER,
'_BufferCount' : SIZE_T,
'_MaxCount' : SIZE_T,
'_Format' : STRING,
'_Locale' : DWORD,
'_ArgList' : POINTER
})
def hook___stdio_common_vsnprintf(ql: Qiling, address: int, params):
return __stdio_common_vsprintf(ql, address, params, False)
@winsdkapi(cc=CDECL, params={
'_Options' : PARAM_INT64,
'_Buffer' : POINTER,
@@ -350,6 +353,18 @@ def hook___stdio_common_vsprintf(ql: Qiling, address: int, params):
def hook___stdio_common_vswprintf(ql: Qiling, address: int, params):
return __stdio_common_vsprintf(ql, address, params, True)
@winsdkapi(cc=CDECL, params={
'_Options' : PARAM_INT64,
'_Buffer' : POINTER,
'_BufferCount' : SIZE_T,
'_MaxCount' : SIZE_T,
'_Format' : WSTRING,
'_Locale' : DWORD,
'_ArgList' : POINTER
})
def hook___stdio_common_vsnwprintf(ql: Qiling, address: int, params):
return __stdio_common_vsprintf(ql, address, params, True)
# all the "_s" versions are aliases to their non-"_s" counterparts
@winsdkapi(cc=CDECL, params={
@@ -383,6 +398,18 @@ def hook___stdio_common_vfwprintf_s(ql: Qiling, address: int, params):
def hook___stdio_common_vsprintf_s(ql: Qiling, address: int, params):
return hook___stdio_common_vsprintf.__wrapped__(ql, address, params)
@winsdkapi(cc=CDECL, params={
'_Options' : PARAM_INT64,
'_Buffer' : POINTER,
'_BufferCount' : SIZE_T,
'_MaxCount' : SIZE_T,
'_Format' : STRING,
'_Locale' : DWORD,
'_ArgList' : POINTER
})
def hook___stdio_common_vsnprintf_s(ql: Qiling, address: int, params):
return hook___stdio_common_vsnprintf.__wrapped__(ql, address, params)
@winsdkapi(cc=CDECL, params={
'_Options' : PARAM_INT64,
'_Buffer' : POINTER,
@@ -394,6 +421,18 @@ def hook___stdio_common_vsprintf_s(ql: Qiling, address: int, params):
def hook___stdio_common_vswprintf_s(ql: Qiling, address: int, params):
return hook___stdio_common_vswprintf.__wrapped__(ql, address, params)
@winsdkapi(cc=CDECL, params={
'_Options' : PARAM_INT64,
'_Buffer' : POINTER,
'_BufferCount' : SIZE_T,
'_MaxCount' : SIZE_T,
'_Format' : WSTRING,
'_Locale' : DWORD,
'_ArgList' : POINTER
})
def hook___stdio_common_vsnwprintf_s(ql: Qiling, address: int, params):
return hook___stdio_common_vsnwprintf.__wrapped__(ql, address, params)
@winsdkapi(cc=CDECL, params={})
def hook___lconv_init(ql: Qiling, address: int, params):
return 0
@@ -449,50 +488,18 @@ def hook_strncmp(ql: Qiling, address: int, params):
return result
def __malloc(ql: Qiling, address: int, params):
size = params['size']
return ql.os.heap.alloc(size)
@winsdkapi(cc=CDECL, params={
'size' : UINT
})
}, passthru=True)
def hook__malloc_base(ql: Qiling, address: int, params):
return __malloc(ql, address, params)
return
# void* malloc(unsigned int size)
@winsdkapi(cc=CDECL, params={
'size' : UINT
})
}, passthru=True)
def hook_malloc(ql: Qiling, address: int, params):
size = params['size']
return ql.os.heap.alloc(size)
def __realloc(ql: Qiling, address: int, params):
block = params['block']
size = params['size']
if not block:
return ql.os.heap.alloc(size)
if size == 0:
ql.os.heap.free(block)
return 0
oldSize = ql.os.heap.size(block)
oldData = bytes(ql.mem.read(block, size))
ql.os.heap.free(block)
if size < oldSize:
oldData = oldData[0:size]
newBase = ql.os.heap.alloc(size)
if newBase:
ql.mem.write(newBase, oldData)
return newBase
return
# void* __cdecl _realloc_base(
# void* const block,
@@ -501,27 +508,22 @@ def __realloc(ql: Qiling, address: int, params):
@winsdkapi(cc=CDECL, params={
'block' : POINTER,
'size' : UINT
})
}, passthru=True)
def hook__realloc_base(ql: Qiling, address: int, params):
return __realloc(ql, address, params)
def __free(ql: Qiling, address: int, params):
address = params['address']
ql.os.heap.free(address)
return
@winsdkapi(cc=CDECL, params={
'address': POINTER
})
}, passthru=True)
def hook__free_base(ql: Qiling, address: int, params):
return __free(ql, address, params)
return
# void* free(void *address)
@winsdkapi(cc=CDECL, params={
'address': POINTER
})
}, passthru=True)
def hook_free(ql: Qiling, address: int, params):
return __free(ql, address, params)
return
# _onexit_t _onexit(
# _onexit_t function
@@ -546,32 +548,16 @@ def hook__onexit(ql: Qiling, address: int, params):
'dest' : POINTER,
'c' : INT,
'count' : SIZE_T
})
}, passthru=True)
def hook_memset(ql: Qiling, address: int, params):
dest = params["dest"]
c = params["c"]
count = params["count"]
ql.mem.write(dest, bytes([c] * count))
return dest
def __calloc(ql: Qiling, address: int, params):
num = params['num']
size = params['size']
count = num * size
ret = ql.os.heap.alloc(count)
ql.mem.write(ret, bytes([0] * count))
return ret
return
@winsdkapi(cc=CDECL, params={
'num' : SIZE_T,
'size' : SIZE_T
})
}, passthru=True)
def hook__calloc_base(ql: Qiling, address: int, params):
return __calloc(ql, address, params)
return
# void *calloc(
# size_t num,
@@ -580,9 +566,9 @@ def hook__calloc_base(ql: Qiling, address: int, params):
@winsdkapi(cc=CDECL, params={
'num' : SIZE_T,
'size' : SIZE_T
})
}, passthru=True)
def hook_calloc(ql: Qiling, address: int, params):
return __calloc(ql, address, params)
return
# void * memmove(
# void *dest,
@@ -593,12 +579,9 @@ def hook_calloc(ql: Qiling, address: int, params):
'dest' : POINTER,
'src' : POINTER,
'num' : SIZE_T
})
}, passthru=True)
def hook_memmove(ql: Qiling, address: int, params):
data = ql.mem.read(params['src'], params['num'])
ql.mem.write(params['dest'], bytes(data))
return params['dest']
return
# int _ismbblead(
# unsigned int c