mirror of
https://github.com/radareorg/radare2.git
synced 2024-11-23 21:29:49 +00:00
Initial implementation of heap parsing for Windows (#14218) ##debug
This commit is contained in:
parent
0e7b37601a
commit
7a10af4057
@ -1340,48 +1340,28 @@ R_API int r_cons_get_size(int *rows) {
|
||||
}
|
||||
|
||||
#if __WINDOWS__
|
||||
R_API os_info *r_sys_get_osinfo();
|
||||
R_API int r_cons_get_ansicon() {
|
||||
HKEY key;
|
||||
DWORD type;
|
||||
DWORD size;
|
||||
DWORD major;
|
||||
DWORD minor;
|
||||
char release[25];
|
||||
bool win_support = false;
|
||||
if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0,
|
||||
KEY_QUERY_VALUE, &key) != ERROR_SUCCESS) {
|
||||
goto ANSICON;
|
||||
os_info *info = r_sys_get_osinfo ();
|
||||
if (info) {
|
||||
major = info->major;
|
||||
minor = info->minor;
|
||||
if (major > 10
|
||||
|| major == 10 && minor > 0
|
||||
|| major == 10 && minor == 0 && info->compilation >= 1703) {
|
||||
win_support = true;
|
||||
}
|
||||
}
|
||||
size = sizeof (major);
|
||||
if (RegQueryValueExA (key, "CurrentMajorVersionNumber", NULL, &type,
|
||||
(LPBYTE)&major, &size) != ERROR_SUCCESS
|
||||
|| type != REG_DWORD) {
|
||||
goto beach;
|
||||
}
|
||||
size = sizeof (minor);
|
||||
if (RegQueryValueExA (key, "CurrentMinorVersionNumber", NULL, &type,
|
||||
(LPBYTE)&minor, &size) != ERROR_SUCCESS
|
||||
|| type != REG_DWORD) {
|
||||
goto beach;
|
||||
}
|
||||
size = sizeof (release);
|
||||
if (RegQueryValueExA (key, "ReleaseId", NULL, &type,
|
||||
(LPBYTE)release, &size) != ERROR_SUCCESS
|
||||
|| type != REG_SZ) {
|
||||
goto beach;
|
||||
}
|
||||
if (major > 10
|
||||
|| major == 10 && minor > 0
|
||||
|| major == 10 && minor == 0 && atoi (release) >= 1703) {
|
||||
free (info);
|
||||
char *ansicon = r_sys_getenv ("ANSICON");
|
||||
if (ansicon) {
|
||||
free (ansicon);
|
||||
win_support = true;
|
||||
}
|
||||
beach:
|
||||
RegCloseKey (key);
|
||||
ANSICON:
|
||||
char *env_ansicon_str = r_sys_getenv ("ANSICON");
|
||||
bool env_ansicon = !!env_ansicon_str;
|
||||
free (env_ansicon_str);
|
||||
return win_support || env_ansicon;
|
||||
return win_support;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1430,6 +1430,10 @@ beach:
|
||||
static int cmd_dbg_map_heap_glibc_32(RCore *core, const char *input);
|
||||
static int cmd_dbg_map_heap_glibc_64(RCore *core, const char *input);
|
||||
#endif // __linux__ && __GNU_LIBRARY__ && __GLIBC__ && __GLIBC_MINOR__
|
||||
#if __WINDOWS__
|
||||
static int cmd_debug_map_heap_win(RCore *core, const char *input);
|
||||
#endif // __WINDOWS__
|
||||
|
||||
|
||||
static ut64 addroflib(RCore *core, const char *libname) {
|
||||
RListIter *iter;
|
||||
@ -1493,8 +1497,12 @@ static int r_debug_heap(RCore *core, const char *input) {
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
#if __WINDOWS__
|
||||
cmd_debug_map_heap_win (core, input + 1);
|
||||
#else
|
||||
eprintf ("MALLOC algorithm not supported\n");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1837,6 +1845,8 @@ static int cmd_debug_map(RCore *core, const char *input) {
|
||||
|
||||
#if __linux__ && __GNU_LIBRARY__ && __GLIBC__ && __GLIBC_MINOR__
|
||||
#include "linux_heap_glibc.c"
|
||||
#elif __WINDOWS__
|
||||
#include "windows_heap.c"
|
||||
#endif
|
||||
|
||||
// move into basic_types.h
|
||||
|
@ -70,6 +70,9 @@ r_core_sources = [
|
||||
r_core_inc = []
|
||||
if host_machine.system() != 'windows'
|
||||
r_core_inc += ['../../shlr/heap/include']
|
||||
else
|
||||
r_core_sources += 'windows_heap.c'
|
||||
r_core_inc += ['../../shlr/heap/include/r_windows']
|
||||
endif
|
||||
r_core_inc = [platform_inc, include_directories(r_core_inc)]
|
||||
|
||||
|
894
libr/core/windows_heap.c
Normal file
894
libr/core/windows_heap.c
Normal file
@ -0,0 +1,894 @@
|
||||
#include <r_core.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <windows_heap.h>
|
||||
#include "..\..\debug\p\native\maps\windows_maps.h"
|
||||
|
||||
/*
|
||||
* Viewer discretion advised: Spaghetti code ahead
|
||||
* Some Code references:
|
||||
* https://securityxploded.com/enumheaps.php
|
||||
* https://bitbucket.org/evolution536/crysearch-memory-scanner/
|
||||
* https://processhacker.sourceforge.io
|
||||
* http://www.tssc.de/winint
|
||||
* https://www.nirsoft.net/kernel_struct/vista/
|
||||
* https://github.com/yoichi/HeapStat/blob/master/heapstat.cpp
|
||||
* https://doxygen.reactos.org/
|
||||
*
|
||||
* References:
|
||||
* Windows NT(2000) Native API Reference (Book)
|
||||
* Papers:
|
||||
* http://illmatics.com/Understanding_the_LFH.pdf
|
||||
* http://illmatics.com/Windows%208%20Heap%20Internals.pdf
|
||||
* https://www.blackhat.com/docs/us-16/materials/us-16-Yason-Windows-10-Segment-Heap-Internals-wp.pdf
|
||||
*
|
||||
* This code has 2 different approaches to getting the heap info:
|
||||
* 1) Calling InitHeapInfo with both PDI_HEAPS and PDI_HEAP_BLOCKS.
|
||||
* This will fill a buffer with HeapBlockBasicInfo like structures which
|
||||
* is then walked through by calling GetFirstHeapBlock and subsequently GetNextHeapBlock
|
||||
* (see 1st link). This approach is the more generic one as it uses Windows functions.
|
||||
* Unfortunately it fails to offer more detailed information about each block (although it is possible to get this info later) and
|
||||
* also fails misteriously once the count of allocated blocks reach a certain threshold (1mil or so) or if segment heap is active for the
|
||||
* program (in this case everything locks in the next call for the function)
|
||||
* 2) In case 1 fails, Calling GetHeapBlocks, which will manually read and parse (poorly :[ ) each block.
|
||||
* First it calls InitHeapInfo with only the PDI_HEAPS flag, with the only objective of getting a list of heap header addresses. It will then
|
||||
* do the job that InitHeapInfo would do if it was called with PDI_HEAP_BLOCKS as well, filling a buffer with HeapBlockBasicInfo structures that
|
||||
* can also be walked with GetFirstHeapBlock and GetNextHeapBlock (and HeapBlockExtraInfo when needed).
|
||||
*
|
||||
* TODO:
|
||||
* Var to select algorithm?
|
||||
* x86 vs x64 vs WOW64
|
||||
* Graphs
|
||||
* Print structures
|
||||
* Make sure GetHeapBlocks actually works
|
||||
* Maybe instead of using hardcoded structs we can get the offsets from ntdll.pdb
|
||||
*/
|
||||
|
||||
#define PDI_MODULES 0x01
|
||||
#define PDI_HEAPS 0x04
|
||||
#define PDI_HEAP_TAGS 0x08
|
||||
#define PDI_HEAP_BLOCKS 0x10
|
||||
#define PDI_HEAP_ENTRIES_EX 0x200
|
||||
|
||||
#define CHECK_INFO(heapInfo)\
|
||||
if (!heapInfo) {\
|
||||
eprintf ("It wasn't possible to get the heap information\n");\
|
||||
return;\
|
||||
}\
|
||||
if (!heapInfo->count) {\
|
||||
r_cons_print ("No heaps for this process\n");\
|
||||
return;\
|
||||
}
|
||||
|
||||
#define UPDATE_FLAGS(hb, flags)\
|
||||
if ((flags & 0xf1) || (flags & 0x0200)) {\
|
||||
hb->dwFlags = LF32_FIXED;\
|
||||
} else if ((flags & 0x20)) {\
|
||||
hb->dwFlags = LF32_MOVEABLE;\
|
||||
} else if ((flags & 0x0100)) {\
|
||||
hb->dwFlags = LF32_FREE;\
|
||||
}\
|
||||
hb->dwFlags |= (flags >> SHIFT) << SHIFT;\
|
||||
|
||||
static bool init_func() {
|
||||
HANDLE ntdll = LoadLibrary (TEXT ("ntdll.dll"));
|
||||
if (!ntdll) {
|
||||
return false;
|
||||
}
|
||||
if (!RtlCreateQueryDebugBuffer) {
|
||||
RtlCreateQueryDebugBuffer = (PDEBUG_BUFFER (NTAPI *)(DWORD, BOOLEAN))GetProcAddress (ntdll, "RtlCreateQueryDebugBuffer");
|
||||
}
|
||||
if (!RtlQueryProcessDebugInformation) {
|
||||
RtlQueryProcessDebugInformation = (NTSTATUS (NTAPI *)(DWORD, DWORD, PDEBUG_BUFFER))GetProcAddress (ntdll, "RtlQueryProcessDebugInformation");
|
||||
}
|
||||
if (!RtlDestroyQueryDebugBuffer) {
|
||||
RtlDestroyQueryDebugBuffer = (NTSTATUS (NTAPI *)(PDEBUG_BUFFER))GetProcAddress (ntdll, "RtlDestroyQueryDebugBuffer");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_segment_heap(HANDLE h_proc, PVOID heapBase) {
|
||||
HEAP heap;
|
||||
if (ReadProcessMemory (h_proc, heapBase, &heap, sizeof (HEAP), NULL)) {
|
||||
if (heap.SegmentSignature == 0xddeeddee) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// These functions are basically Heap32First and Heap32Next but faster
|
||||
static bool GetFirstHeapBlock(PDEBUG_HEAP_INFORMATION heapInfo, PHeapBlock hb) {
|
||||
r_return_val_if_fail (heapInfo && hb, false);
|
||||
PHeapBlockBasicInfo block;
|
||||
|
||||
hb->index = 0;
|
||||
hb->dwAddress = 0;
|
||||
hb->dwFlags = 0;
|
||||
hb->extraInfo = NULL;
|
||||
|
||||
block = (PHeapBlockBasicInfo)heapInfo->Blocks;
|
||||
if (!block) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SIZE_T index = hb->index;
|
||||
do {
|
||||
if (index > heapInfo->BlockCount) {
|
||||
return false;
|
||||
}
|
||||
hb->dwAddress = (void *)block[index].address;
|
||||
hb->dwSize = block->size;
|
||||
if (block[index].extra & EXTRA_FLAG) {
|
||||
PHeapBlockExtraInfo extra = (PHeapBlockExtraInfo)(block[index].extra & ~EXTRA_FLAG);
|
||||
hb->dwSize -= extra->unusedBytes;
|
||||
hb->extraInfo = extra;
|
||||
(WPARAM)hb->dwAddress += extra->granularity;
|
||||
} else {
|
||||
(WPARAM)hb->dwAddress += heapInfo->Granularity;
|
||||
hb->extraInfo = NULL;
|
||||
}
|
||||
index++;
|
||||
} while (block[index].flags & 2);
|
||||
|
||||
hb->index = index;
|
||||
|
||||
WPARAM flags = block[hb->index].flags;
|
||||
UPDATE_FLAGS (hb, flags);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool GetNextHeapBlock(PDEBUG_HEAP_INFORMATION heapInfo, PHeapBlock hb) {
|
||||
r_return_val_if_fail (heapInfo && hb, false);
|
||||
PHeapBlockBasicInfo block;
|
||||
|
||||
block = (PHeapBlockBasicInfo)heapInfo->Blocks;
|
||||
SIZE_T index = hb->index;
|
||||
|
||||
if (index > heapInfo->BlockCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (block[index].flags & 2) {
|
||||
do {
|
||||
if (index > heapInfo->BlockCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// new address = curBlockAddress + Granularity;
|
||||
hb->dwAddress = (void *)(block[index].address + heapInfo->Granularity);
|
||||
|
||||
index++;
|
||||
hb->dwSize = block->size;
|
||||
} while (block[index].flags & 2);
|
||||
hb->index = index;
|
||||
} else {
|
||||
hb->dwSize = block[index].size;
|
||||
if (block[index].extra & EXTRA_FLAG) {
|
||||
PHeapBlockExtraInfo extra = (PHeapBlockExtraInfo)(block[index].extra & ~EXTRA_FLAG);
|
||||
hb->extraInfo = extra;
|
||||
hb->dwSize -= extra->unusedBytes;
|
||||
hb->dwAddress = (void *)(block[index].address + extra->granularity);
|
||||
} else {
|
||||
hb->extraInfo = NULL;
|
||||
(WPARAM)hb->dwAddress += hb->dwSize;
|
||||
}
|
||||
hb->index++;
|
||||
}
|
||||
|
||||
WPARAM flags = block[index].flags;
|
||||
UPDATE_FLAGS (hb, flags);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void free_extra_info(PDEBUG_HEAP_INFORMATION heap) {
|
||||
r_return_if_fail (heap);
|
||||
HeapBlock hb;
|
||||
if (GetFirstHeapBlock (heap, &hb)) {
|
||||
do {
|
||||
R_FREE (hb.extraInfo);
|
||||
} while (GetNextHeapBlock (heap, &hb));
|
||||
}
|
||||
}
|
||||
|
||||
static bool DecodeHeapEntry(PHEAP heap, PHEAP_ENTRY entry) {
|
||||
#if defined(_M_X64)
|
||||
(WPARAM)entry += sizeof (PVOID);
|
||||
#endif
|
||||
if (heap->EncodeFlagMask && (*(UINT32 *)entry & heap->EncodeFlagMask)) {
|
||||
#if defined(_M_X64)
|
||||
(WPARAM)heap += sizeof (PVOID);
|
||||
#endif
|
||||
*(WPARAM *)entry ^= *(WPARAM *)&heap->Encoding;
|
||||
}
|
||||
return !(((BYTE *)entry)[0] ^ ((BYTE *)entry)[1] ^ ((BYTE *)entry)[2] ^ ((BYTE *)entry)[3]);
|
||||
}
|
||||
|
||||
// Is this right?
|
||||
static bool DecodeLFHEntry(PHEAP heap, PHEAP_ENTRY entry, PHEAP_USERDATA_HEADER userBlocks, WPARAM key, WPARAM addr) {
|
||||
#if defined(_M_X64)
|
||||
(WPARAM)entry += sizeof (PVOID);
|
||||
#endif
|
||||
if (heap->EncodeFlagMask) {
|
||||
*(DWORD *)entry ^= PtrToInt (heap->BaseAddress) ^ (DWORD)(((DWORD)addr - PtrToInt (userBlocks)) << 0xC) ^ (DWORD)key ^ ((DWORD)addr >> 4);
|
||||
}
|
||||
return !(((BYTE *)entry)[0] ^ ((BYTE *)entry)[1] ^ ((BYTE *)entry)[2] ^ ((BYTE *)entry)[3]);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function may fail with PDI_HEAP_BLOCKS if:
|
||||
* There's too many allocations
|
||||
* The Segment Heap is activated (will block next time called)
|
||||
* Notes:
|
||||
* Some LFH allocations seem misaligned
|
||||
*/
|
||||
static PDEBUG_BUFFER InitHeapInfo(DWORD pid, DWORD mask) {
|
||||
// Make sure it is not segment heap to avoid lockups
|
||||
if (mask & PDI_HEAP_BLOCKS) {
|
||||
PDEBUG_BUFFER db = InitHeapInfo (pid, PDI_HEAPS);
|
||||
if (db) {
|
||||
HANDLE h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
|
||||
PHeapInformation heaps = db->HeapInformation;
|
||||
for (int i = 0; i < heaps->count; i++) {
|
||||
DEBUG_HEAP_INFORMATION heap = heaps->heaps[i];
|
||||
if (is_segment_heap (h_proc, heap.Base)) {
|
||||
RtlDestroyQueryDebugBuffer (db);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
RtlDestroyQueryDebugBuffer (db);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
int res;
|
||||
PDEBUG_BUFFER db = RtlCreateQueryDebugBuffer (0, FALSE);
|
||||
res = RtlQueryProcessDebugInformation (pid, mask, db);
|
||||
if (res) {
|
||||
// why after it fails the first time it blocks on the second? Thats annoying
|
||||
// It stops blocking if i pause radare in the debugger. is it a race?
|
||||
// why it fails with 1000000 allocs? also with processes with segment heap enabled?
|
||||
RtlDestroyQueryDebugBuffer (db);
|
||||
r_sys_perror ("InitHeapInfo");
|
||||
return NULL;
|
||||
}
|
||||
return db;
|
||||
}
|
||||
|
||||
#define GROW_BLOCKS()\
|
||||
if (allocated <= count * sizeof (HeapBlockBasicInfo)) {\
|
||||
SIZE_T old_alloc = allocated;\
|
||||
allocated *= 2;\
|
||||
PVOID tmp = blocks;\
|
||||
blocks = realloc (blocks, allocated);\
|
||||
if (!blocks) {\
|
||||
blocks = tmp;\
|
||||
goto err;\
|
||||
}\
|
||||
memset ((BYTE *)blocks + old_alloc, 0, old_alloc);\
|
||||
}
|
||||
|
||||
#define GROW_PBLOCKS()\
|
||||
if (*allocated <= *count * sizeof (HeapBlockBasicInfo)) {\
|
||||
SIZE_T old_alloc = *allocated;\
|
||||
*allocated *= 2;\
|
||||
PVOID tmp = *blocks;\
|
||||
tmp = realloc (*blocks, *allocated);\
|
||||
if (!tmp) {\
|
||||
return false;\
|
||||
}\
|
||||
*blocks = tmp;\
|
||||
memset ((BYTE *)(*blocks) + old_alloc, 0, old_alloc);\
|
||||
}
|
||||
|
||||
static bool __lfh_segment_loop(HANDLE h_proc, PHeapBlockBasicInfo *blocks, SIZE_T *allocated, WPARAM lfhKey, WPARAM *count, WPARAM first, WPARAM next) {
|
||||
while ((first != next) && next) {
|
||||
HEAP_LFH_SUBSEGMENT subsegment;
|
||||
ReadProcessMemory (h_proc, (void *)next, &subsegment, sizeof (HEAP_LFH_SUBSEGMENT), NULL);
|
||||
subsegment.BlockOffsets.EncodedData ^= (DWORD)lfhKey ^ ((DWORD)next >> 0xC);
|
||||
WPARAM mask = 1, offset = 0;
|
||||
for (int l = 0; l < subsegment.BlockCount; l++) {
|
||||
if (!mask) {
|
||||
mask = 1;
|
||||
offset++;
|
||||
ReadProcessMemory (h_proc, (WPARAM *)(next + offsetof (HEAP_LFH_SUBSEGMENT, BlockBitmap)) + offset,
|
||||
&subsegment.BlockBitmap, sizeof (WPARAM), NULL);
|
||||
}
|
||||
if (subsegment.BlockBitmap[0] & mask) {
|
||||
GROW_PBLOCKS ();
|
||||
WPARAM off = subsegment.BlockOffsets.FirstBlockOffset + l * subsegment.BlockOffsets.BlockSize;
|
||||
(*blocks)[*count].address = next + off;
|
||||
(*blocks)[*count].size = subsegment.BlockOffsets.BlockSize;
|
||||
(*blocks)[*count].flags = 1 | SEGMENT_HEAP_BLOCK | LFH_BLOCK;
|
||||
PHeapBlockExtraInfo extra = calloc (1, sizeof (HeapBlockExtraInfo));
|
||||
if (!extra) {
|
||||
return false;
|
||||
}
|
||||
extra->segment = next;
|
||||
extra->granularity = sizeof (HEAP_ENTRY);
|
||||
(*blocks)[*count].extra = EXTRA_FLAG | (WPARAM)extra;
|
||||
*count += 1;
|
||||
}
|
||||
mask <<= 2;
|
||||
}
|
||||
next = (WPARAM)subsegment.ListEntry.Flink;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool GetSegmentHeapBlocks(HANDLE h_proc, PVOID heapBase, PHeapBlockBasicInfo *blocks, WPARAM *count, SIZE_T *allocated, WPARAM ntdllOffset) {
|
||||
r_return_val_if_fail (h_proc && blocks && count && allocated, false);
|
||||
WPARAM bytesRead;
|
||||
SEGMENT_HEAP segheapHeader;
|
||||
ReadProcessMemory (h_proc, heapBase, &segheapHeader, sizeof (SEGMENT_HEAP), &bytesRead);
|
||||
|
||||
if (segheapHeader.Signature != 0xddeeddee) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WPARAM RtlpHpHeapGlobalsOffset = ntdllOffset + 0x44A0; // ntdll!RtlpHpHeapGlobals
|
||||
|
||||
WPARAM lfhKey;
|
||||
WPARAM lfhKeyLocation = RtlpHpHeapGlobalsOffset + sizeof (WPARAM);
|
||||
if (!ReadProcessMemory (h_proc, (PVOID)lfhKeyLocation, &lfhKey, sizeof (WPARAM), &bytesRead)) {
|
||||
r_sys_perror ("ReadProcessMemory");
|
||||
eprintf ("LFH key not found.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// LFH
|
||||
byte numBuckets = _countof (segheapHeader.LfhContext.Buckets);
|
||||
for (int j = 0; j < numBuckets; j++) {
|
||||
if ((WPARAM)segheapHeader.LfhContext.Buckets[j] & 1) continue;
|
||||
HEAP_LFH_BUCKET bucket;
|
||||
ReadProcessMemory (h_proc, segheapHeader.LfhContext.Buckets[j], &bucket, sizeof (HEAP_LFH_BUCKET), &bytesRead);
|
||||
HEAP_LFH_AFFINITY_SLOT affinitySlot, *paffinitySlot;
|
||||
ReadProcessMemory (h_proc, bucket.AffinitySlots, &paffinitySlot, sizeof (PHEAP_LFH_AFFINITY_SLOT), &bytesRead);
|
||||
bucket.AffinitySlots++;
|
||||
ReadProcessMemory (h_proc, paffinitySlot, &affinitySlot, sizeof (HEAP_LFH_AFFINITY_SLOT), &bytesRead);
|
||||
WPARAM first = (WPARAM)paffinitySlot + offsetof (HEAP_LFH_SUBSEGMENT_OWNER, AvailableSubsegmentList);
|
||||
WPARAM next = (WPARAM)affinitySlot.State.AvailableSubsegmentList.Flink;
|
||||
if (!__lfh_segment_loop (h_proc, blocks, allocated, lfhKey, count, first, next)) {
|
||||
return false;
|
||||
}
|
||||
first = (WPARAM)paffinitySlot + offsetof (HEAP_LFH_SUBSEGMENT_OWNER, FullSubsegmentList);
|
||||
next = (WPARAM)affinitySlot.State.FullSubsegmentList.Flink;
|
||||
if (!__lfh_segment_loop (h_proc, blocks, allocated, lfhKey, count, first, next)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Large Blocks
|
||||
if (segheapHeader.LargeAllocMetadata.Root) {
|
||||
PRTL_BALANCED_NODE node = malloc (sizeof (RTL_BALANCED_NODE));
|
||||
RStack *s = r_stack_new (segheapHeader.LargeReservedPages);
|
||||
PRTL_BALANCED_NODE curr = segheapHeader.LargeAllocMetadata.Root;
|
||||
do { // while (!r_stack_is_empty(s));
|
||||
GROW_PBLOCKS ();
|
||||
while (curr) {
|
||||
r_stack_push (s, curr);
|
||||
ReadProcessMemory (h_proc, curr, node, sizeof (RTL_BALANCED_NODE), &bytesRead);
|
||||
curr = node->Left;
|
||||
};
|
||||
curr = (PRTL_BALANCED_NODE)r_stack_pop (s);
|
||||
HEAP_LARGE_ALLOC_DATA entry;
|
||||
ReadProcessMemory (h_proc, curr, &entry, sizeof (HEAP_LARGE_ALLOC_DATA), &bytesRead);
|
||||
(*blocks)[*count].address = entry.VirtualAddess - entry.UnusedBytes;
|
||||
(*blocks)[*count].flags = 1 | SEGMENT_HEAP_BLOCK | LARGE_BLOCK;
|
||||
(*blocks)[*count].size = ((entry.AllocatedPages >> 12) << 12);
|
||||
PHeapBlockExtraInfo extra = calloc (1, sizeof (HeapBlockExtraInfo));
|
||||
if (!extra) {
|
||||
return false;
|
||||
}
|
||||
extra->unusedBytes = entry.UnusedBytes;
|
||||
ReadProcessMemory(h_proc, (void *)(*blocks)[*count].address, &extra->granularity, sizeof (USHORT), &bytesRead);
|
||||
(*blocks)[*count].extra = EXTRA_FLAG | (WPARAM)extra;
|
||||
curr = entry.TreeNode.Right;
|
||||
*count += 1;
|
||||
} while (curr || !r_stack_is_empty(s));
|
||||
r_stack_free (s);
|
||||
free (node);
|
||||
}
|
||||
|
||||
WPARAM RtlpHpHeapGlobal;
|
||||
ReadProcessMemory (h_proc, (PVOID)(RtlpHpHeapGlobalsOffset), &RtlpHpHeapGlobal, sizeof (WPARAM), &bytesRead);
|
||||
// Backend Blocks (And VS)
|
||||
for (int i = 0; i < 2; i++) {
|
||||
HEAP_SEG_CONTEXT ctx = segheapHeader.SegContexts[i];
|
||||
WPARAM ctxFirstEntry = (WPARAM)heapBase + offsetof (SEGMENT_HEAP, SegContexts) + sizeof (HEAP_SEG_CONTEXT) * i + offsetof (HEAP_SEG_CONTEXT, SegmentListHead);
|
||||
HEAP_PAGE_SEGMENT pageSegment;
|
||||
WPARAM currPageSegment = (WPARAM)ctx.SegmentListHead.Flink;
|
||||
do {
|
||||
if (!ReadProcessMemory (h_proc, (PVOID)currPageSegment, &pageSegment, sizeof (HEAP_PAGE_SEGMENT), &bytesRead)) {
|
||||
break;
|
||||
}
|
||||
for (int j = 2; j < 256; j++) {
|
||||
if ((pageSegment.DescArray[j].RangeFlags &
|
||||
(PAGE_RANGE_FLAGS_FIRST | PAGE_RANGE_FLAGS_ALLOCATED)) ==
|
||||
(PAGE_RANGE_FLAGS_FIRST | PAGE_RANGE_FLAGS_ALLOCATED)) {
|
||||
GROW_PBLOCKS ();
|
||||
(*blocks)[*count].address = currPageSegment + j * 0x1000;
|
||||
(*blocks)[*count].size = pageSegment.DescArray[j].UnitSize * 0x1000;
|
||||
(*blocks)[*count].flags = SEGMENT_HEAP_BLOCK | BACKEND_BLOCK | 1;
|
||||
PHeapBlockExtraInfo extra = calloc (1, sizeof (HeapBlockExtraInfo));
|
||||
if (!extra) {
|
||||
return false;
|
||||
}
|
||||
extra->segment = currPageSegment;
|
||||
extra->unusedBytes = pageSegment.DescArray[j].UnusedBytes;
|
||||
(*blocks)[*count].extra = EXTRA_FLAG | (WPARAM)extra;
|
||||
*count += 1;
|
||||
}
|
||||
// Hack (i dont know if all blocks like this are VS or not)
|
||||
if (pageSegment.DescArray[j].RangeFlags & 0xF && pageSegment.DescArray[j].UnusedBytes == 0x1000) {
|
||||
HEAP_VS_SUBSEGMENT vsSubsegment;
|
||||
WPARAM start, from = currPageSegment + j * 0x1000;
|
||||
ReadProcessMemory (h_proc, (PVOID)from, &vsSubsegment, sizeof (HEAP_VS_SUBSEGMENT), &bytesRead);
|
||||
// Walk through subsegment
|
||||
start = from += sizeof (HEAP_VS_SUBSEGMENT);
|
||||
while (from < (WPARAM)start + vsSubsegment.Size * sizeof (HEAP_VS_CHUNK_HEADER)) {
|
||||
HEAP_VS_CHUNK_HEADER vsChunk;
|
||||
ReadProcessMemory (h_proc, (PVOID)from, &vsChunk, sizeof (HEAP_VS_CHUNK_HEADER), &bytesRead);
|
||||
vsChunk.Sizes.HeaderBits ^= from ^ RtlpHpHeapGlobal;
|
||||
WPARAM sz = vsChunk.Sizes.UnsafeSize * sizeof (HEAP_VS_CHUNK_HEADER);
|
||||
if (vsChunk.Sizes.Allocated) {
|
||||
GROW_PBLOCKS ();
|
||||
(*blocks)[*count].address = from;
|
||||
(*blocks)[*count].size = sz;
|
||||
(*blocks)[*count].flags = VS_BLOCK | SEGMENT_HEAP_BLOCK | 1;
|
||||
PHeapBlockExtraInfo extra = calloc (1, sizeof (HeapBlockExtraInfo));
|
||||
if (!extra) {
|
||||
return false;
|
||||
}
|
||||
extra->granularity = sizeof (HEAP_VS_CHUNK_HEADER) * 2;
|
||||
(*blocks)[*count].extra = EXTRA_FLAG | (WPARAM)extra;
|
||||
*count += 1;
|
||||
}
|
||||
from += sz;
|
||||
}
|
||||
}
|
||||
}
|
||||
currPageSegment = (WPARAM)pageSegment.ListEntry.Flink;
|
||||
} while (currPageSegment && currPageSegment != ctxFirstEntry);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static PDEBUG_BUFFER GetHeapBlocks(DWORD pid, RDebug *dbg) {
|
||||
/*
|
||||
TODO:
|
||||
Break this behemoth
|
||||
x86 vs x64 vs WOW64 (use dbg->bits or new structs or just a big union with both versions)
|
||||
*/
|
||||
if (_M_X64 && dbg->bits == R_SYS_BITS_32) {
|
||||
return NULL; // Nope nope nope
|
||||
}
|
||||
WPARAM bytesRead, ntdllOffset = 0;
|
||||
HANDLE h_proc = NULL;
|
||||
PDEBUG_BUFFER db = InitHeapInfo (pid, PDI_HEAPS);
|
||||
if (!db || !db->HeapInformation) {
|
||||
R_LOG_ERROR ("InitHeapInfo Failed\n");
|
||||
goto err;
|
||||
}
|
||||
h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
|
||||
if (!h_proc) {
|
||||
R_LOG_ERROR ("OpenProcess failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
WPARAM lfhKey;
|
||||
RList *map_list = r_w32_dbg_maps (dbg);
|
||||
RListIter *iter;
|
||||
RDebugMap *map;
|
||||
WPARAM lfhKeyLocation;
|
||||
// Get ntdll .data location
|
||||
r_list_foreach (map_list, iter, map) {
|
||||
if (strstr (map->name, "ntdll.dll | .data")) {
|
||||
ntdllOffset = map->addr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
r_list_free (map_list);
|
||||
if (!ntdllOffset) {
|
||||
eprintf ("ntdll not loaded\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
PHeapInformation heapInfo = db->HeapInformation;
|
||||
int i;
|
||||
for (i = 0; i < heapInfo->count; i++) {
|
||||
WPARAM from = 0;
|
||||
ut64 count = 0;
|
||||
PDEBUG_HEAP_INFORMATION heap = &heapInfo->heaps[i];
|
||||
HEAP_ENTRY heapEntry;
|
||||
HEAP heapHeader;
|
||||
const SIZE_T sz_entry = sizeof (HEAP_ENTRY);
|
||||
ReadProcessMemory (h_proc, heap->Base, &heapHeader, sizeof (HEAP), &bytesRead);
|
||||
|
||||
SIZE_T allocated = 128 * sizeof (HeapBlockBasicInfo);
|
||||
PHeapBlockBasicInfo blocks = calloc (allocated, 1);
|
||||
if (!blocks) {
|
||||
R_LOG_ERROR ("Memory Allocation failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
// SEGMENT_HEAP
|
||||
if (heapHeader.SegmentSignature == 0xddeeddee) {
|
||||
bool ret = GetSegmentHeapBlocks (h_proc, heap->Base, &blocks, &count, &allocated, ntdllOffset);
|
||||
heap->Blocks = blocks;
|
||||
heap->BlockCount = count;
|
||||
if (!ret) {
|
||||
goto err;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// VirtualAlloc'd blocks
|
||||
PLIST_ENTRY fentry = (PVOID)((WPARAM)heapHeader.BaseAddress + offsetof (HEAP, VirtualAllocdBlocks));
|
||||
PLIST_ENTRY entry = heapHeader.VirtualAllocdBlocks.Flink;
|
||||
while (entry && (entry != fentry)) {
|
||||
HEAP_VIRTUAL_ALLOC_ENTRY vAlloc;
|
||||
ReadProcessMemory (h_proc, entry, &vAlloc, sizeof (HEAP_VIRTUAL_ALLOC_ENTRY), &bytesRead);
|
||||
DecodeHeapEntry (&heapHeader, &vAlloc.BusyBlock);
|
||||
GROW_BLOCKS ();
|
||||
blocks[count].address = (WPARAM)entry + offsetof (HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
|
||||
blocks[count].flags = 1 | (vAlloc.BusyBlock.Flags | NT_BLOCK | LARGE_BLOCK) & ~2ULL;
|
||||
blocks[count].size = vAlloc.CommitSize;
|
||||
PHeapBlockExtraInfo extra = calloc (1, sizeof (HeapBlockExtraInfo));
|
||||
if (!extra) {
|
||||
goto err;
|
||||
}
|
||||
extra->granularity = sizeof (HEAP_VIRTUAL_ALLOC_ENTRY);
|
||||
blocks[count].extra = EXTRA_FLAG | (WPARAM)extra;
|
||||
count++;
|
||||
entry = vAlloc.Entry.Flink;
|
||||
}
|
||||
|
||||
// LFH Activated
|
||||
if (heapHeader.FrontEndHeap && heapHeader.FrontEndHeapType == 0x2) {
|
||||
lfhKeyLocation = ntdllOffset + 0x7508; // ntdll!RtlpLFHKey
|
||||
if (!ReadProcessMemory (h_proc, (PVOID)lfhKeyLocation, &lfhKey, sizeof (WPARAM), &bytesRead)) {
|
||||
r_sys_perror ("ReadProcessMemory");
|
||||
eprintf ("LFH key not found.\n");
|
||||
goto err;
|
||||
}
|
||||
LFH_HEAP lfhHeader;
|
||||
if (!ReadProcessMemory (h_proc, heapHeader.FrontEndHeap, &lfhHeader, sizeof (LFH_HEAP), &bytesRead)) {
|
||||
r_sys_perror ("ReadProcessMemory");
|
||||
goto err;
|
||||
}
|
||||
|
||||
PLIST_ENTRY curEntry, firstEntry = (PVOID)((WPARAM)heapHeader.FrontEndHeap + offsetof (LFH_HEAP, SubSegmentZones));
|
||||
curEntry = lfhHeader.SubSegmentZones.Flink;
|
||||
|
||||
// Loops through all _HEAP_SUBSEGMENTs
|
||||
do { // (curEntry != firstEntry)
|
||||
HEAP_LOCAL_SEGMENT_INFO info;
|
||||
HEAP_LOCAL_DATA localData;
|
||||
HEAP_SUBSEGMENT subsegment;
|
||||
HEAP_USERDATA_HEADER userdata;
|
||||
LFH_BLOCK_ZONE blockZone;
|
||||
|
||||
WPARAM curSubsegment = (WPARAM)(curEntry + 2);
|
||||
int next = 0;
|
||||
do { // (next < blockZone.NextIndex)
|
||||
if (!ReadProcessMemory (h_proc, (PVOID)curSubsegment, &subsegment, sizeof (HEAP_SUBSEGMENT), &bytesRead)
|
||||
|| !subsegment.BlockSize
|
||||
|| !ReadProcessMemory (h_proc, subsegment.LocalInfo, &info, sizeof (HEAP_LOCAL_SEGMENT_INFO), &bytesRead)
|
||||
|| !ReadProcessMemory (h_proc, info.LocalData, &localData, sizeof (HEAP_LOCAL_DATA), &bytesRead)
|
||||
|| !ReadProcessMemory (h_proc, localData.CrtZone, &blockZone, sizeof (LFH_BLOCK_ZONE), &bytesRead)) {
|
||||
break;
|
||||
}
|
||||
|
||||
size_t sz = subsegment.BlockSize * sizeof (HEAP_ENTRY);
|
||||
ReadProcessMemory (h_proc, subsegment.UserBlocks, &userdata, sizeof (HEAP_USERDATA_HEADER), &bytesRead);
|
||||
userdata.EncodedOffsets.StrideAndOffset ^= PtrToInt (subsegment.UserBlocks) ^ PtrToInt (heapHeader.FrontEndHeap) ^ (WPARAM)lfhKey;
|
||||
size_t bitmapsz = (userdata.BusyBitmap.SizeOfBitMap + 8 - userdata.BusyBitmap.SizeOfBitMap % 8) / 8;
|
||||
WPARAM *bitmap = calloc (bitmapsz > sizeof (WPARAM) ? bitmapsz : sizeof (WPARAM), 1);
|
||||
if (!bitmap) {
|
||||
goto err;
|
||||
}
|
||||
ReadProcessMemory (h_proc, userdata.BusyBitmap.Buffer, bitmap, bitmapsz, &bytesRead);
|
||||
WPARAM mask = 1;
|
||||
// Walk through the busy bitmap
|
||||
for (int j = 0, offset = 0; j < userdata.BusyBitmap.SizeOfBitMap; j++) {
|
||||
if (!mask) {
|
||||
mask = 1;
|
||||
offset++;
|
||||
}
|
||||
// Only if block is busy
|
||||
if (*(bitmap + offset) & mask) {
|
||||
GROW_BLOCKS ();
|
||||
WPARAM off = userdata.EncodedOffsets.FirstAllocationOffset + sz * j;
|
||||
from = (WPARAM)subsegment.UserBlocks + off;
|
||||
ReadProcessMemory (h_proc, (PVOID)from, &heapEntry, sz_entry, &bytesRead);
|
||||
DecodeLFHEntry (&heapHeader, &heapEntry, subsegment.UserBlocks, lfhKey, from);
|
||||
blocks[count].address = from;
|
||||
blocks[count].flags = 1 | NT_BLOCK | LFH_BLOCK;
|
||||
blocks[count].size = sz;
|
||||
PHeapBlockExtraInfo extra = calloc (1, sizeof (HeapBlockExtraInfo));
|
||||
if (!extra) {
|
||||
goto err;
|
||||
}
|
||||
extra->granularity = sizeof (HEAP_ENTRY);
|
||||
extra->segment = curSubsegment;
|
||||
blocks[count].extra = EXTRA_FLAG | (WPARAM)extra;
|
||||
count++;
|
||||
}
|
||||
mask <<= 1;
|
||||
}
|
||||
free (bitmap);
|
||||
curSubsegment += sizeof (HEAP_SUBSEGMENT);
|
||||
next++;
|
||||
} while (next < blockZone.NextIndex || subsegment.BlockSize);
|
||||
|
||||
LIST_ENTRY entry;
|
||||
ReadProcessMemory (h_proc, curEntry, &entry, sizeof (entry), &bytesRead);
|
||||
curEntry = entry.Flink;
|
||||
} while (curEntry != firstEntry);
|
||||
}
|
||||
|
||||
HEAP_SEGMENT oldSegment, segment;
|
||||
WPARAM firstSegment = (WPARAM)heapHeader.SegmentList.Flink;
|
||||
ReadProcessMemory (h_proc, (PVOID)(firstSegment - offsetof (HEAP_SEGMENT, SegmentListEntry)), &segment, sizeof (HEAP_SEGMENT), &bytesRead);
|
||||
// NT Blocks (Loops through all _HEAP_SEGMENTs)
|
||||
do {
|
||||
from = (WPARAM)segment.FirstEntry;
|
||||
if (!from) {
|
||||
goto next;
|
||||
}
|
||||
do {
|
||||
if (!ReadProcessMemory (h_proc, (PVOID)from, &heapEntry, sz_entry, &bytesRead)) {
|
||||
break;
|
||||
}
|
||||
DecodeHeapEntry (&heapHeader, &heapEntry);
|
||||
if (!heapEntry.Size) {
|
||||
// Last Heap block
|
||||
count--;
|
||||
break;
|
||||
}
|
||||
|
||||
SIZE_T real_sz = heapEntry.Size * sz_entry;
|
||||
|
||||
GROW_BLOCKS ();
|
||||
PHeapBlockExtraInfo extra = calloc (1, sizeof (HeapBlockExtraInfo));
|
||||
if (!extra) {
|
||||
goto err;
|
||||
}
|
||||
extra->granularity = sizeof (HEAP_ENTRY);
|
||||
extra->segment = (WPARAM)segment.BaseAddress;
|
||||
blocks[count].extra = EXTRA_FLAG | (WPARAM)extra;
|
||||
blocks[count].address = from;
|
||||
blocks[count].flags = heapEntry.Flags | NT_BLOCK | BACKEND_BLOCK;
|
||||
blocks[count].size = real_sz;
|
||||
from += real_sz;
|
||||
count++;
|
||||
} while (from <= (WPARAM)segment.LastValidEntry);
|
||||
next:
|
||||
oldSegment = segment;
|
||||
from = (WPARAM)segment.SegmentListEntry.Flink - offsetof (HEAP_SEGMENT, SegmentListEntry);
|
||||
ReadProcessMemory (h_proc, (PVOID)from, &segment, sizeof (HEAP_SEGMENT), &bytesRead);
|
||||
} while ((WPARAM)oldSegment.SegmentListEntry.Flink != firstSegment);
|
||||
heap->Blocks = blocks;
|
||||
heap->BlockCount = count;
|
||||
}
|
||||
CloseHandle (h_proc);
|
||||
return db;
|
||||
err:
|
||||
if (h_proc) {
|
||||
CloseHandle (h_proc);
|
||||
}
|
||||
if (db) {
|
||||
for (int i = 0; i < heapInfo->count; i++) {
|
||||
PDEBUG_HEAP_INFORMATION heap = &heapInfo->heaps[i];
|
||||
free_extra_info (heap);
|
||||
R_FREE (heap->Blocks);
|
||||
}
|
||||
RtlDestroyQueryDebugBuffer (db);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void w32_list_heaps(RCore *core, const char format) {
|
||||
ULONG pid = core->dbg->pid;
|
||||
PDEBUG_BUFFER db = InitHeapInfo (pid, PDI_HEAPS | PDI_HEAP_BLOCKS);
|
||||
if (!db) {
|
||||
os_info *info = r_sys_get_osinfo ();
|
||||
if (info->major >= 10) {
|
||||
db = GetHeapBlocks (pid, core->dbg);
|
||||
}
|
||||
free (info);
|
||||
if (!db) {
|
||||
eprintf ("Couldn't get heap info.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
PHeapInformation heapInfo = db->HeapInformation;
|
||||
CHECK_INFO (heapInfo);
|
||||
int i;
|
||||
PJ *pj = pj_new ();
|
||||
pj_a (pj);
|
||||
for (i = 0; i < heapInfo->count; i++) {
|
||||
DEBUG_HEAP_INFORMATION heap = heapInfo->heaps[i];
|
||||
switch (format) {
|
||||
case 'j':
|
||||
pj_o (pj);
|
||||
pj_kN (pj, "address", (WPARAM)heap.Base);
|
||||
pj_kN (pj, "count", (WPARAM)heap.BlockCount);
|
||||
pj_kN (pj, "allocated", (WPARAM)heap.Allocated);
|
||||
pj_kN (pj, "commited", (WPARAM)heap.Committed);
|
||||
pj_end (pj);
|
||||
break;
|
||||
default:
|
||||
r_cons_printf ("Heap @ 0x%08"PFMT64x":\n", (WPARAM)heap.Base);
|
||||
r_cons_printf ("\tBlocks: %"PFMT64u"\n", (WPARAM)heap.BlockCount);
|
||||
r_cons_printf ("\tAllocated: %"PFMT64u"\n", (WPARAM)heap.Allocated);
|
||||
r_cons_printf ("\tCommited: %"PFMT64u"\n", (WPARAM)heap.Committed);
|
||||
break;
|
||||
}
|
||||
if (!(db->InfoClassMask & PDI_HEAP_BLOCKS)) {
|
||||
free_extra_info (&heap);
|
||||
R_FREE (heap.Blocks);
|
||||
}
|
||||
}
|
||||
if (format == 'j') {
|
||||
pj_end (pj);
|
||||
r_cons_println (pj_string (pj));
|
||||
}
|
||||
pj_free (pj);
|
||||
RtlDestroyQueryDebugBuffer (db);
|
||||
}
|
||||
|
||||
static void w32_list_heaps_blocks(RCore *core, const char format) {
|
||||
DWORD pid = core->dbg->pid;
|
||||
PDEBUG_BUFFER db = InitHeapInfo (pid, PDI_HEAPS | PDI_HEAP_BLOCKS | PDI_HEAP_ENTRIES_EX);
|
||||
if (!db) {
|
||||
// Too many blocks or segment heap (will block if segment heap)
|
||||
os_info *info = r_sys_get_osinfo ();
|
||||
if (info->major >= 10) { // Only tested on 10. Maybe works on 8
|
||||
db = GetHeapBlocks (pid, core->dbg);
|
||||
}
|
||||
free (info);
|
||||
if (!db) {
|
||||
eprintf ("Couldn't get heap info.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
PHeapInformation heapInfo = db->HeapInformation;
|
||||
HeapBlock *block = malloc (sizeof (HeapBlock));
|
||||
CHECK_INFO (heapInfo);
|
||||
int i;
|
||||
PJ *pj = pj_new ();
|
||||
pj_a (pj);
|
||||
for (i = 0; i < heapInfo->count; i++) {
|
||||
bool go = true;
|
||||
switch (format) {
|
||||
case 'f':
|
||||
if (heapInfo->heaps[i].BlockCount > 50000) {
|
||||
go = r_cons_yesno ('n', "Are you sure you want to add %"PFMT64u" flags? (y/N)", heapInfo->heaps[i].BlockCount);
|
||||
}
|
||||
break;
|
||||
case 'j':
|
||||
pj_o (pj);
|
||||
pj_kN (pj, "heap", (WPARAM)heapInfo->heaps[i].Base);
|
||||
pj_k (pj, "blocks");
|
||||
pj_a (pj);
|
||||
break;
|
||||
default:
|
||||
r_cons_printf ("Heap @ 0x%"PFMT64x":\n", heapInfo->heaps[i].Base);
|
||||
}
|
||||
char type[128];
|
||||
if (GetFirstHeapBlock (&heapInfo->heaps[i], block) & go) {
|
||||
do {
|
||||
memset (type, 0, sizeof (type));
|
||||
switch (block->dwFlags & 0xFFFF) {
|
||||
case LF32_FIXED:
|
||||
strncpy (type, "(FIXED)", 8);
|
||||
break;
|
||||
case LF32_FREE:
|
||||
strncpy (type, "(FREE)", 7);
|
||||
break;
|
||||
case LF32_MOVEABLE:
|
||||
strncpy (type, "(MOVEABLE)", 11);
|
||||
break;
|
||||
}
|
||||
if (block->dwFlags & SEGMENT_HEAP_BLOCK) {
|
||||
strncat (type, "Segment", 8);
|
||||
} else if (block->dwFlags & NT_BLOCK) {
|
||||
strncat (type, "NT", 3);
|
||||
}
|
||||
if (block->dwFlags & LFH_BLOCK) {
|
||||
strncat (type, "/LFH", 5);
|
||||
} else if (block->dwFlags & LARGE_BLOCK) {
|
||||
strncat (type, "/LARGE", 7);
|
||||
} else if (block->dwFlags & BACKEND_BLOCK) {
|
||||
strncat (type, "/BACKEND", 9);
|
||||
} else if (block->dwFlags & VS_BLOCK) {
|
||||
strncat (type, "/VS", 4);
|
||||
}
|
||||
unsigned short granularity = block->extraInfo ? block->extraInfo->granularity : heapInfo->heaps[i].Granularity;
|
||||
switch (format) {
|
||||
case 'f':
|
||||
{
|
||||
ut64 addr = (ut64)block->dwAddress - granularity;
|
||||
char *name = r_str_newf ("alloc.%"PFMT64x"", addr);
|
||||
r_flag_set (core->flags, name, addr, block->dwSize);
|
||||
free (name);
|
||||
break;
|
||||
}
|
||||
case 'j':
|
||||
pj_o (pj);
|
||||
pj_kN (pj, "address", (ut64)block->dwAddress - granularity);
|
||||
pj_kN (pj, "data_address", (ut64)block->dwAddress);
|
||||
pj_kN (pj, "size", block->dwSize);
|
||||
pj_ks (pj, "type", type);
|
||||
pj_end (pj);
|
||||
break;
|
||||
default:
|
||||
r_cons_printf ("\tBlock @ 0x%"PFMT64x" %s:\n", (ut64)block->dwAddress - granularity, type);
|
||||
r_cons_printf ("\t\tSize 0x%"PFMT64x"\n", (ut64)block->dwSize);
|
||||
r_cons_printf ("\t\tData address @ 0x%"PFMT64x"\n", (ut64)block->dwAddress);
|
||||
break;
|
||||
}
|
||||
} while (GetNextHeapBlock (&heapInfo->heaps[i], block));
|
||||
}
|
||||
if (format == 'j') {
|
||||
pj_end (pj);
|
||||
pj_end (pj);
|
||||
}
|
||||
if (!(db->InfoClassMask & PDI_HEAP_BLOCKS)) {
|
||||
// RtlDestroyQueryDebugBuffer wont free this for some reason
|
||||
free_extra_info (&heapInfo->heaps[i]);
|
||||
R_FREE (heapInfo->heaps[i].Blocks);
|
||||
}
|
||||
}
|
||||
if (format == 'j') {
|
||||
pj_end (pj);
|
||||
r_cons_println (pj_string (pj));
|
||||
}
|
||||
pj_free (pj);
|
||||
RtlDestroyQueryDebugBuffer (db);
|
||||
}
|
||||
|
||||
static const char* help_msg[] = {
|
||||
"Usage:", " dmh[?|b][f|j]", " # Memory map heap",
|
||||
"dmh[j]", "", "List process heaps",
|
||||
"dmhb[?]", "", "List process heap blocks",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char* help_msg_block[] = {
|
||||
"Usage:", " dmhb[f|j]", " # Memory map heap",
|
||||
"dmhb", "", "List all allocated heap blocks",
|
||||
"dmhbf", "", "Create flags for each allocated block",
|
||||
"dmhbj", "", "Print output in JSON format",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void cmd_debug_map_heap_block_win(RCore *core, const char *input) {
|
||||
switch (input[0]) {
|
||||
case '\0':
|
||||
case 'f':
|
||||
case 'j':
|
||||
w32_list_heaps_blocks (core, input[0]);
|
||||
break;
|
||||
default:
|
||||
r_core_cmd_help (core, help_msg_block);
|
||||
}
|
||||
}
|
||||
|
||||
static int cmd_debug_map_heap_win(RCore *core, const char *input) {
|
||||
init_func ();
|
||||
switch (input[0]) {
|
||||
case '?': // dmh?
|
||||
r_core_cmd_help (core, help_msg);
|
||||
break;
|
||||
case 'b': // dmhb
|
||||
cmd_debug_map_heap_block_win (core, input + 1);
|
||||
break;
|
||||
default:
|
||||
w32_list_heaps (core, input[0]);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
@ -60,6 +60,12 @@ if host_machine.system() == 'linux'
|
||||
]
|
||||
endif
|
||||
|
||||
if host_machine.system() == 'windows'
|
||||
r_debug_sources += [
|
||||
'p/native/maps/windows_maps.c'
|
||||
]
|
||||
endif
|
||||
|
||||
if host_machine.system() != 'windows'
|
||||
r_debug_sources += [
|
||||
'p/native/procfs.c'
|
||||
|
@ -1274,7 +1274,7 @@ static RList *r_debug_native_map_get (RDebug *dbg) {
|
||||
#if __APPLE__
|
||||
list = xnu_dbg_maps (dbg, 0);
|
||||
#elif __WINDOWS__
|
||||
list = w32_dbg_maps (dbg); // TODO: moar?
|
||||
list = r_w32_dbg_maps (dbg);
|
||||
#else
|
||||
#if __sun
|
||||
char path[1024];
|
||||
@ -1427,7 +1427,7 @@ static RList *r_debug_native_modules_get (RDebug *dbg) {
|
||||
}
|
||||
#endif
|
||||
#if __WINDOWS__
|
||||
list = w32_dbg_modules (dbg);
|
||||
list = r_w32_dbg_modules (dbg);
|
||||
if (list && !r_list_empty (list)) {
|
||||
return list;
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
#include "windows_maps.h"
|
||||
#include "../w32.h"
|
||||
|
||||
typedef struct {
|
||||
RDebugMap *map;
|
||||
@ -73,7 +75,7 @@ static inline RDebugMap *add_map_reg(RList *list, const char *name, MEMORY_BASIC
|
||||
return add_map (list, name, (ut64)(size_t)mbi->BaseAddress, (ut64)mbi->RegionSize, mbi);
|
||||
}
|
||||
|
||||
static RList *w32_dbg_modules(RDebug *dbg) {
|
||||
R_API RList *r_w32_dbg_modules(RDebug *dbg) {
|
||||
MODULEENTRY32 me32;
|
||||
RDebugMap *mr;
|
||||
RList *list = r_list_new ();
|
||||
@ -81,7 +83,7 @@ static RList *w32_dbg_modules(RDebug *dbg) {
|
||||
HANDLE h_mod_snap = w32_CreateToolhelp32Snapshot (flags, dbg->pid);
|
||||
|
||||
if (!h_mod_snap) {
|
||||
r_sys_perror ("w32_dbg_modules/CreateToolhelp32Snapshot");
|
||||
r_sys_perror ("r_w32_dbg_modules/CreateToolhelp32Snapshot");
|
||||
goto err_w32_dbg_modules;
|
||||
}
|
||||
me32.dwSize = sizeof (MODULEENTRY32);
|
||||
@ -232,7 +234,7 @@ static void proc_mem_map(HANDLE h_proc, RList *map_list, MEMORY_BASIC_INFORMATIO
|
||||
}
|
||||
}
|
||||
|
||||
static RList *w32_dbg_maps(RDebug *dbg) {
|
||||
R_API RList *r_w32_dbg_maps(RDebug *dbg) {
|
||||
SYSTEM_INFO si = {0};
|
||||
LPVOID cur_addr;
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
@ -243,12 +245,12 @@ static RList *w32_dbg_maps(RDebug *dbg) {
|
||||
GetSystemInfo (&si);
|
||||
h_proc = w32_OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dbg->pid);
|
||||
if (!h_proc) {
|
||||
r_sys_perror ("w32_dbg_maps/w32_OpenProcess");
|
||||
r_sys_perror ("r_w32_dbg_maps/w32_OpenProcess");
|
||||
goto err_w32_dbg_maps;
|
||||
}
|
||||
cur_addr = si.lpMinimumApplicationAddress;
|
||||
/* get process modules list */
|
||||
mod_list = w32_dbg_modules (dbg);
|
||||
mod_list = r_w32_dbg_modules (dbg);
|
||||
/* process memory map */
|
||||
while (cur_addr < si.lpMaximumApplicationAddress &&
|
||||
VirtualQueryEx (h_proc, cur_addr, &mbi, sizeof (mbi)) != 0) {
|
8
libr/debug/p/native/maps/windows_maps.h
Normal file
8
libr/debug/p/native/maps/windows_maps.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef WINDOWS_MAPS_H
|
||||
#define WINDOWS_MAPS_H
|
||||
#include <r_core.h>
|
||||
|
||||
R_API RList *r_w32_dbg_modules(RDebug *dbg);
|
||||
R_API RList *r_w32_dbg_maps(RDebug *dbg);
|
||||
|
||||
#endif
|
@ -1,202 +1,5 @@
|
||||
#include <windows.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <stdio.h>
|
||||
#include <psapi.h>
|
||||
#include <tchar.h>
|
||||
#include "w32.h"
|
||||
|
||||
#ifndef NTSTATUS
|
||||
#define NTSTATUS DWORD
|
||||
#endif
|
||||
#ifndef WINAPI
|
||||
#define WINAPI
|
||||
#endif
|
||||
|
||||
// XXX remove
|
||||
#define WIN32_PI(x) x
|
||||
#if 0
|
||||
// list windows.. required to get list of windows for current pid and send kill signals
|
||||
BOOL CALLBACK enumWindowsProc (HWND hwnd, LPARAM lParam) {
|
||||
|
||||
DWORD procid;
|
||||
|
||||
GetWindowThreadProcessId (hwnd, &procid);
|
||||
|
||||
if ((HANDLE)procid == g_hProc) { staticchar module[1024];
|
||||
module[0] = 0;
|
||||
|
||||
if (g_softPhoneTitle.Size() > 0) { int rc = GetWindowText (hwnd, module, 1023);
|
||||
module[rc] = 0;
|
||||
|
||||
}
|
||||
|
||||
if (IsWindow(hwnd) && ((g_appTitle.Size() == 0) || (g_appTitle.EqualsNoCase(module)))) {
|
||||
g_hWnd = hwnd;
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
findApplicationWindow (void) {
|
||||
g_hWnd = NULL;
|
||||
|
||||
EnumWindows (enumWindowsProc, 0);
|
||||
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
|
||||
1860 typedef struct _FLOATING_SAVE_AREA {
|
||||
1861 DWORD ControlWord;
|
||||
1862 DWORD StatusWord;
|
||||
1863 DWORD TagWord;
|
||||
1864 DWORD ErrorOffset;
|
||||
|
||||
1865 DWORD ErrorSelector;
|
||||
1866 DWORD DataOffset;
|
||||
1867 DWORD DataSelector;
|
||||
1868 BYTE RegArea[80];
|
||||
1869 DWORD Cr0NpxState;
|
||||
1870 } FLOATING_SAVE_AREA;
|
||||
|
||||
1871 typedef struct _CONTEXT {
|
||||
1872 DWORD ContextFlags;
|
||||
1873 DWORD Dr0;
|
||||
1874 DWORD Dr1;
|
||||
1875 DWORD Dr2;
|
||||
1876 DWORD Dr3;
|
||||
1877 DWORD Dr6;
|
||||
1878 DWORD Dr7;
|
||||
1879 FLOATING_SAVE_AREA FloatSave;
|
||||
1880 DWORD SegGs;
|
||||
1881 DWORD SegFs;
|
||||
1882 DWORD SegEs;
|
||||
1883 DWORD SegDs;
|
||||
1884 DWORD Edi;
|
||||
1885 DWORD Esi;
|
||||
1886 DWORD Ebx;
|
||||
1887 DWORD Edx;
|
||||
1888 DWORD Ecx;
|
||||
1889 DWORD Eax;
|
||||
1890 DWORD Ebp;
|
||||
1891 DWORD Eip;
|
||||
1892 DWORD SegCs;
|
||||
1893 DWORD EFlags;
|
||||
1894 DWORD Esp;
|
||||
1895 DWORD SegSs;
|
||||
1896 BYTE ExtendedRegs[MAXIMUM_SUPPORTED_EXTENSION];
|
||||
1897 } CONTEXT;
|
||||
#endif
|
||||
|
||||
typedef struct _SYSTEM_HANDLE
|
||||
{
|
||||
ULONG ProcessId;
|
||||
BYTE ObjectTypeNumber;
|
||||
BYTE Flags;
|
||||
USHORT Handle;
|
||||
PVOID Object;
|
||||
ACCESS_MASK GrantedAccess;
|
||||
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;
|
||||
typedef struct _SYSTEM_HANDLE_INFORMATION
|
||||
{
|
||||
ULONG HandleCount;
|
||||
SYSTEM_HANDLE Handles[1];
|
||||
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
|
||||
typedef enum _POOL_TYPE
|
||||
{
|
||||
NonPagedPool,
|
||||
PagedPool,
|
||||
NonPagedPoolMustSucceed,
|
||||
DontUseThisType,
|
||||
NonPagedPoolCacheAligned,
|
||||
PagedPoolCacheAligned,
|
||||
NonPagedPoolCacheAlignedMustS
|
||||
} POOL_TYPE, *PPOOL_TYPE;
|
||||
typedef struct _UNICODE_STRING
|
||||
{
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
PWSTR Buffer;
|
||||
} UNICODE_STRING, *PUNICODE_STRING;
|
||||
typedef struct _OBJECT_TYPE_INFORMATION
|
||||
{
|
||||
UNICODE_STRING Name;
|
||||
ULONG TotalNumberOfObjects;
|
||||
ULONG TotalNumberOfHandles;
|
||||
ULONG TotalPagedPoolUsage;
|
||||
ULONG TotalNonPagedPoolUsage;
|
||||
ULONG TotalNamePoolUsage;
|
||||
ULONG TotalHandleTableUsage;
|
||||
ULONG HighWaterNumberOfObjects;
|
||||
ULONG HighWaterNumberOfHandles;
|
||||
ULONG HighWaterPagedPoolUsage;
|
||||
ULONG HighWaterNonPagedPoolUsage;
|
||||
ULONG HighWaterNamePoolUsage;
|
||||
ULONG HighWaterHandleTableUsage;
|
||||
ULONG InvalidAttributes;
|
||||
GENERIC_MAPPING GenericMapping;
|
||||
ULONG ValidAccess;
|
||||
BOOLEAN SecurityRequired;
|
||||
BOOLEAN MaintainHandleCount;
|
||||
USHORT MaintainTypeList;
|
||||
POOL_TYPE PoolType;
|
||||
ULONG PagedPoolUsage;
|
||||
ULONG NonPagedPoolUsage;
|
||||
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
|
||||
|
||||
static DWORD (WINAPI *w32_GetModuleBaseName)(HANDLE, HMODULE, LPTSTR, DWORD) = NULL;
|
||||
static BOOL (WINAPI *w32_GetModuleInformation)(HANDLE, HMODULE, LPMODULEINFO, DWORD) = NULL;
|
||||
static BOOL (WINAPI *w32_DebugActiveProcessStop)(DWORD) = NULL;
|
||||
static HANDLE (WINAPI *w32_OpenThread)(DWORD, BOOL, DWORD) = NULL;
|
||||
static BOOL (WINAPI *w32_DebugBreakProcess)(HANDLE) = NULL;
|
||||
static DWORD (WINAPI *w32_GetThreadId)(HANDLE) = NULL; // Vista
|
||||
static DWORD (WINAPI *w32_GetProcessId)(HANDLE) = NULL; // XP
|
||||
static HANDLE (WINAPI *w32_OpenProcess)(DWORD, BOOL, DWORD) = NULL;
|
||||
static BOOL (WINAPI *w32_QueryFullProcessImageName)(HANDLE, DWORD, LPTSTR, PDWORD) = NULL;
|
||||
static DWORD (WINAPI *w32_GetMappedFileName)(HANDLE, LPVOID, LPTSTR, DWORD) = NULL;
|
||||
static NTSTATUS (WINAPI *w32_NtQuerySystemInformation)(ULONG, PVOID, ULONG, PULONG) = NULL;
|
||||
static NTSTATUS (WINAPI *w32_NtQueryInformationThread)(HANDLE, ULONG, PVOID, ULONG, PULONG) = NULL;
|
||||
static NTSTATUS (WINAPI *w32_NtDuplicateObject)(HANDLE, HANDLE, HANDLE, PHANDLE, ACCESS_MASK, ULONG, ULONG) = NULL;
|
||||
static NTSTATUS (WINAPI *w32_NtQueryObject)(HANDLE, ULONG, PVOID, ULONG, PULONG) = NULL;
|
||||
// fpu access API
|
||||
static ut64 (WINAPI *w32_GetEnabledXStateFeatures)() = NULL;
|
||||
static BOOL (WINAPI *w32_InitializeContext)(PVOID, DWORD, PCONTEXT*, PDWORD) = NULL;
|
||||
static BOOL (WINAPI *w32_GetXStateFeaturesMask)(PCONTEXT Context, PDWORD64) = NULL;
|
||||
static PVOID(WINAPI *w32_LocateXStateFeature)(PCONTEXT Context, DWORD, PDWORD) = NULL;
|
||||
static BOOL (WINAPI *w32_SetXStateFeaturesMask)(PCONTEXT Context, DWORD64) = NULL;
|
||||
static DWORD (WINAPI *w32_GetModuleFileNameEx)(HANDLE, HMODULE, LPTSTR, DWORD) = NULL;
|
||||
static HANDLE (WINAPI *w32_CreateToolhelp32Snapshot)(DWORD, DWORD) = NULL;
|
||||
|
||||
#ifndef XSTATE_GSSE
|
||||
#define XSTATE_GSSE 2
|
||||
#endif
|
||||
|
||||
#ifndef XSTATE_LEGACY_SSE
|
||||
#define XSTATE_LEGACY_SSE 1
|
||||
#endif
|
||||
|
||||
#if !defined(XSTATE_MASK_GSSE)
|
||||
#define XSTATE_MASK_GSSE (1LLU << (XSTATE_GSSE))
|
||||
#endif
|
||||
|
||||
#undef CONTEXT_XSTATE
|
||||
#if defined(_M_X64)
|
||||
#define CONTEXT_XSTATE (0x00100040)
|
||||
#else
|
||||
#define CONTEXT_XSTATE (0x00010040)
|
||||
#endif
|
||||
#define XSTATE_AVX (XSTATE_GSSE)
|
||||
#define XSTATE_MASK_AVX (XSTATE_MASK_GSSE)
|
||||
#ifndef CONTEXT_ALL
|
||||
#define CONTEXT_ALL 1048607
|
||||
#endif
|
||||
static bool w32dbg_SeDebugPrivilege() {
|
||||
/////////////////////////////////////////////////////////
|
||||
// Note: Enabling SeDebugPrivilege adapted from sample
|
||||
@ -233,7 +36,7 @@ static bool w32dbg_SeDebugPrivilege() {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int w32_dbg_init() {
|
||||
int w32_dbg_init() {
|
||||
HANDLE lib;
|
||||
|
||||
/* escalate privs (required for win7/vista) */
|
||||
@ -334,7 +137,7 @@ static inline int w32_h2p(HANDLE h) {
|
||||
return w32_GetProcessId (h);
|
||||
}
|
||||
|
||||
static int w32_first_thread(int pid) {
|
||||
int w32_first_thread(int pid) {
|
||||
HANDLE th;
|
||||
HANDLE thid;
|
||||
THREADENTRY32 te32;
|
||||
@ -491,14 +294,6 @@ err_get_file_name_from_handle:
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedef struct{
|
||||
int pid;
|
||||
HANDLE hFile;
|
||||
void* BaseOfDll;
|
||||
char Path[MAX_PATH];
|
||||
char Name[MAX_PATH];
|
||||
} LIB_ITEM, *PLIB_ITEM;
|
||||
LPVOID lstLib = 0;
|
||||
PLIB_ITEM lstLibPtr = 0;
|
||||
/*
|
||||
@ -547,17 +342,6 @@ static void * r_debug_findlib (void * BaseOfDll) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// thread list
|
||||
typedef struct {
|
||||
int pid;
|
||||
int tid;
|
||||
BOOL bFinished;
|
||||
HANDLE hThread;
|
||||
LPVOID lpThreadLocalBase;
|
||||
LPVOID lpStartAddress;
|
||||
PVOID lpThreadEntryPoint;
|
||||
DWORD dwExitCode;
|
||||
} THREAD_ITEM, *PTHREAD_ITEM;
|
||||
LPVOID lstThread = 0;
|
||||
PTHREAD_ITEM lstThreadPtr = 0;
|
||||
static PTHREAD_ITEM r_debug_get_thread_item () {
|
||||
@ -604,7 +388,7 @@ static void * r_debug_findthread (int pid, int tid) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int w32_dbg_wait(RDebug *dbg, int pid) {
|
||||
int w32_dbg_wait(RDebug *dbg, int pid) {
|
||||
DEBUG_EVENT de;
|
||||
int tid, next_event = 0;
|
||||
unsigned int code;
|
||||
@ -743,7 +527,7 @@ static int w32_dbg_wait(RDebug *dbg, int pid) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int is_pe_hdr(unsigned char *pe_hdr) {
|
||||
bool is_pe_hdr(unsigned char *pe_hdr) {
|
||||
IMAGE_DOS_HEADER *dos_header = (IMAGE_DOS_HEADER *)pe_hdr;
|
||||
IMAGE_NT_HEADERS *nt_headers;
|
||||
|
||||
@ -751,12 +535,12 @@ static inline int is_pe_hdr(unsigned char *pe_hdr) {
|
||||
nt_headers = (IMAGE_NT_HEADERS *)((char *)dos_header
|
||||
+ dos_header->e_lfanew);
|
||||
if (nt_headers->Signature==IMAGE_NT_SIGNATURE)
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static HANDLE w32_open_thread (int pid, int tid) {
|
||||
static HANDLE w32_open_thread(int pid, int tid) {
|
||||
HANDLE thread = w32_OpenThread (THREAD_ALL_ACCESS, 0, tid);
|
||||
if (thread == INVALID_HANDLE_VALUE) {
|
||||
r_sys_perror ("w32_open_thread/OpenThread");
|
||||
@ -764,7 +548,7 @@ static HANDLE w32_open_thread (int pid, int tid) {
|
||||
return thread;
|
||||
}
|
||||
|
||||
RList *w32_thread_list (int pid, RList *list) {
|
||||
RList *w32_thread_list(int pid, RList *list) {
|
||||
HANDLE th;
|
||||
HANDLE thid;
|
||||
THREADENTRY32 te32;
|
||||
@ -833,11 +617,11 @@ static RDebugPid *build_debug_pid(PROCESSENTRY32 *pe) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
RList *w32_pids (int pid, RList *list) {
|
||||
RList *w32_pids(int pid, RList *list) {
|
||||
HANDLE process_snapshot;
|
||||
PROCESSENTRY32 pe;
|
||||
pe.dwSize = sizeof (PROCESSENTRY32);
|
||||
int show_all_pids = pid == 0;
|
||||
bool show_all_pids = pid == 0;
|
||||
|
||||
process_snapshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, pid);
|
||||
if (process_snapshot == INVALID_HANDLE_VALUE) {
|
||||
@ -865,7 +649,7 @@ RList *w32_pids (int pid, RList *list) {
|
||||
return list;
|
||||
}
|
||||
|
||||
bool w32_terminate_process (RDebug *dbg, int pid) {
|
||||
bool w32_terminate_process(RDebug *dbg, int pid) {
|
||||
HANDLE h_proc = w32_OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE , FALSE, pid);
|
||||
bool ret = false;
|
||||
if (!h_proc) {
|
||||
@ -899,7 +683,7 @@ err_w32_terminate_process:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void w32_break_process (void *d) {
|
||||
void w32_break_process(void *d) {
|
||||
RDebug *dbg = (RDebug *)d;
|
||||
HANDLE h_proc = w32_OpenProcess (PROCESS_ALL_ACCESS, FALSE, dbg->pid);
|
||||
if (!h_proc) {
|
||||
@ -916,7 +700,7 @@ err_w32_break_process:
|
||||
}
|
||||
}
|
||||
|
||||
static int GetAVX (HANDLE hThread, ut128 xmm[16], ut128 ymm[16]) {
|
||||
static int GetAVX(HANDLE hThread, ut128 xmm[16], ut128 ymm[16]) {
|
||||
BOOL Success;
|
||||
int nRegs = 0, Index = 0;
|
||||
DWORD ContextSize = 0;
|
||||
@ -1064,7 +848,7 @@ static void printwincontext(HANDLE hThread, CONTEXT * ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
static int w32_reg_read (RDebug *dbg, int type, ut8 *buf, int size) {
|
||||
static int w32_reg_read(RDebug *dbg, int type, ut8 *buf, int size) {
|
||||
#ifdef _MSC_VER
|
||||
CONTEXT ctx;
|
||||
#else
|
||||
@ -1102,7 +886,7 @@ static int w32_reg_read (RDebug *dbg, int type, ut8 *buf, int size) {
|
||||
return size;
|
||||
}
|
||||
|
||||
static int w32_reg_write (RDebug *dbg, int type, const ut8* buf, int size) {
|
||||
static int w32_reg_write(RDebug *dbg, int type, const ut8* buf, int size) {
|
||||
BOOL ret = false;
|
||||
HANDLE thread;
|
||||
#if _MSC_VER
|
||||
@ -1221,7 +1005,7 @@ err_w32_info_exe:
|
||||
free (path);
|
||||
}
|
||||
|
||||
static RDebugInfo* w32_info (RDebug *dbg, const char *arg) {
|
||||
RDebugInfo *w32_info(RDebug *dbg, const char *arg) {
|
||||
RDebugInfo *rdi = R_NEW0 (RDebugInfo);
|
||||
rdi->status = R_DBG_PROC_SLEEP; // TODO: Fix this
|
||||
rdi->pid = dbg->pid;
|
||||
@ -1237,6 +1021,4 @@ static RDebugInfo* w32_info (RDebug *dbg, const char *arg) {
|
||||
w32_info_user (dbg, rdi);
|
||||
w32_info_exe (dbg, rdi);
|
||||
return rdi;
|
||||
}
|
||||
|
||||
#include "maps/windows.c"
|
||||
}
|
148
libr/debug/p/native/w32.h
Normal file
148
libr/debug/p/native/w32.h
Normal file
@ -0,0 +1,148 @@
|
||||
#ifndef W32_H
|
||||
#define W32_H
|
||||
#include <windows.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <stdio.h>
|
||||
#include <psapi.h>
|
||||
#include <tchar.h>
|
||||
#ifndef NTSTATUS
|
||||
#define NTSTATUS DWORD
|
||||
#endif
|
||||
#ifndef WINAPI
|
||||
#define WINAPI
|
||||
#endif
|
||||
|
||||
// XXX remove
|
||||
#define WIN32_PI(x) x
|
||||
|
||||
typedef struct _SYSTEM_HANDLE {
|
||||
ULONG ProcessId;
|
||||
BYTE ObjectTypeNumber;
|
||||
BYTE Flags;
|
||||
USHORT Handle;
|
||||
PVOID Object;
|
||||
ACCESS_MASK GrantedAccess;
|
||||
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;
|
||||
|
||||
typedef struct _SYSTEM_HANDLE_INFORMATION {
|
||||
ULONG HandleCount;
|
||||
SYSTEM_HANDLE Handles[1];
|
||||
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
|
||||
|
||||
typedef enum _POOL_TYPE {
|
||||
NonPagedPool,
|
||||
PagedPool,
|
||||
NonPagedPoolMustSucceed,
|
||||
DontUseThisType,
|
||||
NonPagedPoolCacheAligned,
|
||||
PagedPoolCacheAligned,
|
||||
NonPagedPoolCacheAlignedMustS
|
||||
} POOL_TYPE, *PPOOL_TYPE;
|
||||
|
||||
typedef struct _UNICODE_STRING {
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
PWSTR Buffer;
|
||||
} UNICODE_STRING, *PUNICODE_STRING;
|
||||
|
||||
typedef struct _OBJECT_TYPE_INFORMATION {
|
||||
UNICODE_STRING Name;
|
||||
ULONG TotalNumberOfObjects;
|
||||
ULONG TotalNumberOfHandles;
|
||||
ULONG TotalPagedPoolUsage;
|
||||
ULONG TotalNonPagedPoolUsage;
|
||||
ULONG TotalNamePoolUsage;
|
||||
ULONG TotalHandleTableUsage;
|
||||
ULONG HighWaterNumberOfObjects;
|
||||
ULONG HighWaterNumberOfHandles;
|
||||
ULONG HighWaterPagedPoolUsage;
|
||||
ULONG HighWaterNonPagedPoolUsage;
|
||||
ULONG HighWaterNamePoolUsage;
|
||||
ULONG HighWaterHandleTableUsage;
|
||||
ULONG InvalidAttributes;
|
||||
GENERIC_MAPPING GenericMapping;
|
||||
ULONG ValidAccess;
|
||||
BOOLEAN SecurityRequired;
|
||||
BOOLEAN MaintainHandleCount;
|
||||
USHORT MaintainTypeList;
|
||||
POOL_TYPE PoolType;
|
||||
ULONG PagedPoolUsage;
|
||||
ULONG NonPagedPoolUsage;
|
||||
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
|
||||
|
||||
typedef struct {
|
||||
int pid;
|
||||
HANDLE hFile;
|
||||
void* BaseOfDll;
|
||||
char Path[MAX_PATH];
|
||||
char Name[MAX_PATH];
|
||||
} LIB_ITEM, *PLIB_ITEM;
|
||||
|
||||
// thread list
|
||||
typedef struct {
|
||||
int pid;
|
||||
int tid;
|
||||
BOOL bFinished;
|
||||
HANDLE hThread;
|
||||
LPVOID lpThreadLocalBase;
|
||||
LPVOID lpStartAddress;
|
||||
PVOID lpThreadEntryPoint;
|
||||
DWORD dwExitCode;
|
||||
} THREAD_ITEM, *PTHREAD_ITEM;
|
||||
|
||||
DWORD (WINAPI *w32_GetModuleBaseName)(HANDLE, HMODULE, LPTSTR, DWORD);
|
||||
BOOL (WINAPI *w32_GetModuleInformation)(HANDLE, HMODULE, LPMODULEINFO, DWORD);
|
||||
BOOL (WINAPI *w32_DebugActiveProcessStop)(DWORD);
|
||||
HANDLE (WINAPI *w32_OpenThread)(DWORD, BOOL, DWORD);
|
||||
BOOL (WINAPI *w32_DebugBreakProcess)(HANDLE);
|
||||
DWORD (WINAPI *w32_GetThreadId)(HANDLE); // Vista
|
||||
DWORD (WINAPI *w32_GetProcessId)(HANDLE); // XP
|
||||
HANDLE (WINAPI *w32_OpenProcess)(DWORD, BOOL, DWORD);
|
||||
BOOL (WINAPI *w32_QueryFullProcessImageName)(HANDLE, DWORD, LPTSTR, PDWORD);
|
||||
DWORD (WINAPI *w32_GetMappedFileName)(HANDLE, LPVOID, LPTSTR, DWORD);
|
||||
NTSTATUS (WINAPI *w32_NtQuerySystemInformation)(ULONG, PVOID, ULONG, PULONG);
|
||||
NTSTATUS (WINAPI *w32_NtQueryInformationThread)(HANDLE, ULONG, PVOID, ULONG, PULONG);
|
||||
NTSTATUS (WINAPI *w32_NtDuplicateObject)(HANDLE, HANDLE, HANDLE, PHANDLE, ACCESS_MASK, ULONG, ULONG);
|
||||
NTSTATUS (WINAPI *w32_NtQueryObject)(HANDLE, ULONG, PVOID, ULONG, PULONG);
|
||||
// fpu access API
|
||||
ut64 (WINAPI *w32_GetEnabledXStateFeatures)();
|
||||
BOOL (WINAPI *w32_InitializeContext)(PVOID, DWORD, PCONTEXT*, PDWORD);
|
||||
BOOL (WINAPI *w32_GetXStateFeaturesMask)(PCONTEXT Context, PDWORD64);
|
||||
PVOID (WINAPI *w32_LocateXStateFeature)(PCONTEXT Context, DWORD, PDWORD);
|
||||
BOOL (WINAPI *w32_SetXStateFeaturesMask)(PCONTEXT Context, DWORD64);
|
||||
DWORD (WINAPI *w32_GetModuleFileNameEx)(HANDLE, HMODULE, LPTSTR, DWORD);
|
||||
HANDLE (WINAPI *w32_CreateToolhelp32Snapshot)(DWORD, DWORD);
|
||||
|
||||
#ifndef XSTATE_GSSE
|
||||
#define XSTATE_GSSE 2
|
||||
#endif
|
||||
|
||||
#ifndef XSTATE_LEGACY_SSE
|
||||
#define XSTATE_LEGACY_SSE 1
|
||||
#endif
|
||||
|
||||
#if !defined(XSTATE_MASK_GSSE)
|
||||
#define XSTATE_MASK_GSSE (1LLU << (XSTATE_GSSE))
|
||||
#endif
|
||||
|
||||
#undef CONTEXT_XSTATE
|
||||
#if defined(_M_X64)
|
||||
#define CONTEXT_XSTATE (0x00100040)
|
||||
#else
|
||||
#define CONTEXT_XSTATE (0x00010040)
|
||||
#endif
|
||||
#define XSTATE_AVX (XSTATE_GSSE)
|
||||
#define XSTATE_MASK_AVX (XSTATE_MASK_GSSE)
|
||||
#ifndef CONTEXT_ALL
|
||||
#define CONTEXT_ALL 1048607
|
||||
#endif
|
||||
void w32_break_process(void *d);
|
||||
bool w32_terminate_process(RDebug *dbg, int pid);
|
||||
int w32_first_thread(int pid);
|
||||
int w32_dbg_wait(RDebug *dbg, int pid);
|
||||
RDebugInfo *w32_info(RDebug *dbg, const char *arg);
|
||||
RList *w32_pids(int pid, RList *list);
|
||||
RList *w32_thread_list(int pid, RList *list);
|
||||
bool is_pe_hdr(unsigned char *pe_hdr);
|
||||
#include "maps/windows_maps.h"
|
||||
#endif
|
@ -21,8 +21,17 @@ enum {
|
||||
R_SYS_BITS_64 = 8,
|
||||
};
|
||||
|
||||
typedef struct _os_info {
|
||||
char name[32];
|
||||
int major;
|
||||
int minor;
|
||||
int patch;
|
||||
int compilation;
|
||||
} os_info;
|
||||
|
||||
R_API char **r_sys_get_environ(void);
|
||||
R_API void r_sys_set_environ(char **e);
|
||||
R_API os_info *r_sys_get_osinfo();
|
||||
R_API ut64 r_sys_now(void);
|
||||
R_API const char *r_time_to_string (ut64 ts);
|
||||
R_API int r_sys_fork(void);
|
||||
@ -72,6 +81,7 @@ R_API int r_sys_cmd_str_full(const char *cmd, const char *input, char **output,
|
||||
#define r_sys_conv_win_to_utf8(buf) r_acp_to_utf8 (buf)
|
||||
#define r_sys_conv_win_to_utf8_l(buf, len) r_acp_to_utf8_l (buf, len)
|
||||
#endif
|
||||
R_API os_info *r_sys_get_winver();
|
||||
R_API int r_sys_get_src_dir_w32(char *buf);
|
||||
R_API bool r_sys_cmd_str_full_w32(const char *cmd, const char *input, char **output, int *outlen, char **sterr);
|
||||
R_API bool r_sys_create_child_proc_w32(const char *cmdline, HANDLE in, HANDLE out, HANDLE err);
|
||||
|
@ -163,6 +163,40 @@ R_API int r_sys_truncate(const char *file, int sz) {
|
||||
#endif
|
||||
}
|
||||
|
||||
R_API os_info *r_sys_get_osinfo() {
|
||||
#if __WINDOWS__
|
||||
return r_sys_get_winver();
|
||||
#endif
|
||||
os_info *info = calloc (1, sizeof (os_info));
|
||||
if (!info) {
|
||||
return NULL;
|
||||
}
|
||||
int len = 0;
|
||||
char *output = r_sys_cmd_str ("uname -s", NULL, &len);
|
||||
if (len) {
|
||||
strncpy (info->name, output, sizeof (info->name));
|
||||
info->name[31] = '\0';
|
||||
}
|
||||
free (output);
|
||||
output = r_sys_cmd_str ("uname -r", NULL, &len);
|
||||
if (len) {
|
||||
char *dot = strtok (output, ".");
|
||||
if (dot) {
|
||||
info->major = atoi (dot);
|
||||
}
|
||||
dot = strtok (NULL, ".");
|
||||
if (dot) {
|
||||
info->minor = atoi (dot);
|
||||
}
|
||||
dot = strtok (NULL, ".");
|
||||
if (dot) {
|
||||
info->patch = atoi (dot);
|
||||
}
|
||||
}
|
||||
free (output);
|
||||
return info;
|
||||
}
|
||||
|
||||
R_API RList *r_sys_dir(const char *path) {
|
||||
RList *list = NULL;
|
||||
#if __WINDOWS__
|
||||
|
@ -27,6 +27,48 @@ static char *getexe(const char *str) {
|
||||
return argv0;
|
||||
}
|
||||
|
||||
R_API os_info *r_sys_get_winver() {
|
||||
HKEY key;
|
||||
DWORD type;
|
||||
DWORD size;
|
||||
DWORD major;
|
||||
DWORD minor;
|
||||
char release[25];
|
||||
os_info *info = calloc (1, sizeof (os_info));
|
||||
if (!info) {
|
||||
return NULL;
|
||||
}
|
||||
if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0,
|
||||
KEY_QUERY_VALUE, &key) != ERROR_SUCCESS) {
|
||||
r_sys_perror ("r_sys_get_winver/RegOpenKeyExA");
|
||||
return 0;
|
||||
}
|
||||
size = sizeof (major);
|
||||
if (RegQueryValueExA (key, "CurrentMajorVersionNumber", NULL, &type,
|
||||
(LPBYTE)&major, &size) != ERROR_SUCCESS
|
||||
|| type != REG_DWORD) {
|
||||
goto beach;
|
||||
}
|
||||
info->major = major;
|
||||
size = sizeof (minor);
|
||||
if (RegQueryValueExA (key, "CurrentMinorVersionNumber", NULL, &type,
|
||||
(LPBYTE)&minor, &size) != ERROR_SUCCESS
|
||||
|| type != REG_DWORD) {
|
||||
goto beach;
|
||||
}
|
||||
info->minor = minor;
|
||||
size = sizeof (release);
|
||||
if (RegQueryValueExA (key, "ReleaseId", NULL, &type,
|
||||
(LPBYTE)release, &size) != ERROR_SUCCESS
|
||||
|| type != REG_SZ) {
|
||||
goto beach;
|
||||
}
|
||||
info->compilation = atoi (release);
|
||||
beach:
|
||||
RegCloseKey (key);
|
||||
return info;
|
||||
}
|
||||
|
||||
R_API int r_sys_get_src_dir_w32(char *buf) {
|
||||
int i = 0;
|
||||
TCHAR fullpath[MAX_PATH + 1];
|
||||
|
1004
shlr/heap/include/r_windows/windows_heap.h
Normal file
1004
shlr/heap/include/r_windows/windows_heap.h
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user