480 lines
15 KiB
C++
480 lines
15 KiB
C++
#include "byakugan.h"
|
|
#include "heapStructs.h"
|
|
|
|
// Get offset into the hash map
|
|
ULONG hash(PVOID addr, struct HPool *heap) {
|
|
ULONG offset;
|
|
|
|
// assume 8 bit alignment, and use the golden ratio of 2^32
|
|
offset = (((ULONG) addr >> 3) * (2654435761)) % (0x007fffff/8);
|
|
//dprintf("[XXX] Hashed to bucket %d\n", offset);
|
|
|
|
return (offset);
|
|
}
|
|
|
|
/*
|
|
* added by jc. I need to be able to find the offset of a chunk inide the chunks array quickly.
|
|
* returns < 0 on error.
|
|
*/
|
|
int FindOffsetForChunk(struct HPool *heap, PVOID memoryPointer)
|
|
{
|
|
ULONG i;
|
|
|
|
i = hash(memoryPointer, heap);
|
|
i = heap->map[i];
|
|
while (i != NULLNODE) {
|
|
if (CHUNK(i).addr == memoryPointer)
|
|
return (i);
|
|
i = CHUNK(i).nextBucket;
|
|
}
|
|
return (NULLNODE); // error
|
|
}
|
|
|
|
ULONG insertBucket(ULONG newC, struct HPool *heap, ULONG inAfter) {
|
|
ULONG offset, lastNum, checkNum;
|
|
|
|
heap->numChunks++;
|
|
CHUNK(newC).inUse = TRUE;
|
|
|
|
// Set up nextInUse list
|
|
if (inAfter == NULLNODE) {
|
|
if (heap->inUseHead == NULLNODE) heap->inUseHead = newC;
|
|
else CHUNK(heap->lastInUse).nextInUse = newC;
|
|
heap->lastInUse = newC;
|
|
CHUNK(newC).nextInUse = NULLNODE;
|
|
//dprintf("[T] Inserting new chunk %d at the end\n", newC);
|
|
} else {
|
|
//dprintf("[T] Inserting new chunk %d after chunk %d\n", newC, inAfter);
|
|
if (heap->lastInUse == inAfter) heap->lastInUse = newC;
|
|
CHUNK(newC).nextInUse = CHUNK(inAfter).nextInUse;
|
|
CHUNK(inAfter).nextInUse = newC;
|
|
}
|
|
|
|
offset = hash(CHUNK(newC).addr, heap);
|
|
|
|
if (heap->map[offset] == NULLNODE) {
|
|
heap->map[offset] = newC;
|
|
CHUNK(newC).nextBucket = NULLNODE;
|
|
} else {
|
|
offset = heap->map[offset];
|
|
while (CHUNK(offset).nextBucket != NULLNODE)
|
|
offset = CHUNK(offset).nextBucket;
|
|
CHUNK(newC).nextBucket = NULLNODE;
|
|
CHUNK(offset).nextBucket = newC;
|
|
}
|
|
return (newC);
|
|
}
|
|
|
|
void removeBucket(PVOID addr, struct HPool *heap) {
|
|
ULONG offset, chunkNum, last = NULLNODE;
|
|
|
|
if (heap == NULL)
|
|
return;
|
|
|
|
|
|
offset = hash(addr, heap);
|
|
chunkNum = heap->map[offset];
|
|
|
|
// Find our bucket and the bucket before it
|
|
while (chunkNum != NULLNODE && CHUNK(chunkNum).addr != addr) {
|
|
last = chunkNum;
|
|
chunkNum = CHUNK(chunkNum).nextBucket;
|
|
}
|
|
|
|
// remove our bucket from the list if we found it
|
|
if (chunkNum != NULLNODE) {
|
|
//dprintf("[T] Deleting node %d\n", chunkNum);
|
|
CHUNK(chunkNum).inUse = FALSE;
|
|
|
|
if (last == NULLNODE)
|
|
heap->map[offset] = NULLNODE;
|
|
else
|
|
CHUNK(last).nextBucket = CHUNK(chunkNum).nextBucket;
|
|
|
|
heap->numChunks--;
|
|
} else
|
|
dprintf("[T] Couldnt find chunk 0x%08x to delete :(\n", addr);
|
|
|
|
|
|
// Head of the list case
|
|
chunkNum = heap->inUseHead;
|
|
if (CHUNK(chunkNum).addr == addr) {
|
|
heap->inUseHead = CHUNK(chunkNum).nextInUse;
|
|
return;
|
|
}
|
|
|
|
// search the list for the previous chunk
|
|
while (chunkNum != NULLNODE && CHUNK(chunkNum).addr != addr) {
|
|
last = chunkNum;
|
|
chunkNum = CHUNK(chunkNum).nextInUse;
|
|
}
|
|
if (chunkNum != NULLNODE) {
|
|
CHUNK(last).nextInUse = CHUNK(chunkNum).nextInUse;
|
|
if (heap->lastInUse == chunkNum) heap->lastInUse = last;
|
|
}
|
|
}
|
|
|
|
void initializeHeapModel(struct HeapState *heapModel) {
|
|
|
|
memset(heapModel+1, 0, sizeof (struct HeapState));
|
|
heapModel->hPoolListLen = 16;
|
|
heapModel->heaps = (struct HPool *) calloc(heapModel->hPoolListLen, sizeof (struct HPool));
|
|
}
|
|
|
|
#define ALLOCHEAD "<Allocation>\n"
|
|
#define ALLOCFOOT "</Allocation>\n"
|
|
#define REALLOCHEAD "<Reallocation>\n"
|
|
#define REALLOCFOOT "</Reallocation>\n"
|
|
#define FREEHEAD "<Free>\n"
|
|
#define FREEFOOT "</Free>\n"
|
|
#define HEAP "\t<Heap>0x%08x</Heap>\n"
|
|
#define ADDRESS "\t<Address>0x%08x</Address>\n"
|
|
#define RETURNPTR "\t<NewAddress>0x%08x</NewAddress>\n"
|
|
#define SIZE "\t<Size>0x%08x</Size>\n"
|
|
#define TRACEHEAD "\t<StackTrace>\n"
|
|
#define TRACEFOOT "\t</StackTrace>\n"
|
|
#define CALLADDR "\t\t<Caller>0x%08x</Caller>\n"
|
|
|
|
void hprintf(HANDLE fHandle, char *format, ...) {
|
|
char ptr[1024];
|
|
DWORD out;
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
|
|
|
|
StringCbVPrintf(ptr, 1024, format, args);
|
|
WriteFile(fHandle, ptr, strlen(ptr), &out, NULL);
|
|
}
|
|
|
|
void logAllocate(struct HeapState *heapModel, struct AllocateStruct *aStruct) {
|
|
ULONG infoUsed;
|
|
|
|
WriteFile(heapModel->hLogFile, ALLOCHEAD, strlen(ALLOCHEAD), &infoUsed, NULL);
|
|
hprintf(heapModel->hLogFile, HEAP, aStruct->heapHandle);
|
|
hprintf(heapModel->hLogFile, ADDRESS, aStruct->ret);
|
|
hprintf(heapModel->hLogFile, SIZE, aStruct->size);
|
|
WriteFile(heapModel->hLogFile, TRACEHEAD, strlen(TRACEHEAD), &infoUsed, NULL);
|
|
|
|
hprintf(heapModel->hLogFile, CALLADDR, aStruct->caller);
|
|
|
|
WriteFile(heapModel->hLogFile, TRACEFOOT, strlen(TRACEFOOT), &infoUsed, NULL);
|
|
WriteFile(heapModel->hLogFile, ALLOCFOOT, strlen(ALLOCFOOT), &infoUsed, NULL);
|
|
}
|
|
|
|
void logReallocate(struct HeapState *heapModel, struct ReallocateStruct *rStruct) {
|
|
ULONG infoUsed;
|
|
|
|
WriteFile(heapModel->hLogFile, REALLOCHEAD, strlen(REALLOCHEAD), &infoUsed, NULL);
|
|
hprintf(heapModel->hLogFile, HEAP, rStruct->heapHandle);
|
|
hprintf(heapModel->hLogFile, ADDRESS, rStruct->memoryPointer);
|
|
hprintf(heapModel->hLogFile, RETURNPTR, rStruct->ret);
|
|
hprintf(heapModel->hLogFile, SIZE, rStruct->size);
|
|
WriteFile(heapModel->hLogFile, TRACEHEAD, strlen(TRACEHEAD), &infoUsed, NULL);
|
|
|
|
hprintf(heapModel->hLogFile, CALLADDR, rStruct->caller);
|
|
|
|
WriteFile(heapModel->hLogFile, TRACEFOOT, strlen(TRACEFOOT), &infoUsed, NULL);
|
|
WriteFile(heapModel->hLogFile, ALLOCFOOT, strlen(REALLOCFOOT), &infoUsed, NULL);
|
|
}
|
|
|
|
void logFree(struct HeapState *heapModel, struct FreeStruct *fStruct) {
|
|
ULONG infoUsed;
|
|
|
|
WriteFile(heapModel->hLogFile, FREEHEAD, strlen(FREEHEAD), &infoUsed, NULL);
|
|
hprintf(heapModel->hLogFile, HEAP, fStruct->heapHandle);
|
|
hprintf(heapModel->hLogFile, ADDRESS, fStruct->memoryPointer);
|
|
WriteFile(heapModel->hLogFile, TRACEHEAD, strlen(TRACEHEAD), &infoUsed, NULL);
|
|
|
|
hprintf(heapModel->hLogFile, CALLADDR, fStruct->caller);
|
|
|
|
WriteFile(heapModel->hLogFile, TRACEFOOT, strlen(TRACEFOOT), &infoUsed, NULL);
|
|
WriteFile(heapModel->hLogFile, FREEFOOT, strlen(FREEFOOT), &infoUsed, NULL);
|
|
}
|
|
|
|
void heapAllocate(struct HeapState *heapModel, struct AllocateStruct *aStruct) {
|
|
struct HPool *myHeap;
|
|
struct HeapChunk *myChunk, *remainder;
|
|
|
|
if (aStruct->heapHandle == 0 || aStruct->ret == 0)
|
|
return;
|
|
|
|
myHeap = getHeap(heapModel, aStruct->heapHandle);
|
|
myChunk = getChunk(myHeap, aStruct->ret, NULLNODE);
|
|
|
|
if (myChunk == NULL)
|
|
return;
|
|
|
|
if (myChunk->size > aStruct->size + SPACEBETWEEN) {
|
|
//dprintf("[T] Splitting chunk at 0x%08x:\n", aStruct->ret);
|
|
//dprintf("\tNew Chunk 0x%08x, size %d bytes, after chunk %u\n",
|
|
// ((ULONG) aStruct->ret + aStruct->size + SPACEBETWEEN),
|
|
// (myChunk->size - aStruct->size - SPACEBETWEEN),
|
|
// FindOffsetForChunk(myHeap, myChunk->addr));
|
|
remainder = getChunk(myHeap,
|
|
(PVOID) ((ULONG) aStruct->ret + aStruct->size + SPACEBETWEEN),
|
|
FindOffsetForChunk(myHeap, myChunk->addr));
|
|
remainder->size = (myChunk->size - aStruct->size) - SPACEBETWEEN;
|
|
remainder->free = TRUE;
|
|
}
|
|
|
|
myChunk->size = aStruct->size;
|
|
myChunk->flags = aStruct->flags;
|
|
myChunk->free = FALSE;
|
|
}
|
|
|
|
void heapReallocate(struct HeapState *heapModel, struct ReallocateStruct *rStruct) {
|
|
struct HPool *myHeap;
|
|
struct HeapChunk *oChunk, *nChunk, *remainder;
|
|
|
|
if (rStruct->heapHandle == 0 || rStruct->ret == 0)
|
|
return;
|
|
|
|
myHeap = getHeap(heapModel, rStruct->heapHandle);
|
|
nChunk = getChunk(myHeap, rStruct->ret, NULLNODE);
|
|
if (nChunk == NULL)
|
|
return;
|
|
|
|
if (rStruct->ret != rStruct->memoryPointer && rStruct->memoryPointer != 0) {
|
|
oChunk = getChunk(myHeap, rStruct->memoryPointer, NULLNODE);
|
|
if (oChunk != NULL)
|
|
oChunk->free = TRUE;
|
|
}
|
|
|
|
// Split new chunk on realloc move if necessary
|
|
if (nChunk->size > rStruct->size + SPACEBETWEEN) {
|
|
remainder = getChunk(myHeap,
|
|
(PVOID) ((ULONG) rStruct->ret + rStruct->size + SPACEBETWEEN),
|
|
FindOffsetForChunk(myHeap, nChunk->addr));
|
|
remainder->size = (nChunk->size - rStruct->size) - SPACEBETWEEN;
|
|
remainder->free = TRUE;
|
|
}
|
|
|
|
nChunk->size = rStruct->size;
|
|
nChunk->flags = rStruct->flags;
|
|
nChunk->free = FALSE;
|
|
}
|
|
|
|
void heapFree(struct HeapState *heapModel, struct FreeStruct *fStruct) {
|
|
struct HPool *myHeap;
|
|
struct HeapChunk *myChunk;
|
|
|
|
//dprintf("[XXX] Freeing 0x%08x\n", fStruct->memoryPointer);
|
|
|
|
if (fStruct->heapHandle == 0 || fStruct->memoryPointer == 0x00000000) {
|
|
// So many of these that it slows us down :(
|
|
//dprintf("[T] Program attempted to free a NULL pointer.\n\n");
|
|
return;
|
|
}
|
|
myHeap = getHeap(heapModel, fStruct->heapHandle);
|
|
myChunk = getChunk(myHeap, fStruct->memoryPointer, NULLNODE);
|
|
if (myChunk == NULL)
|
|
return; // dupe free :(
|
|
|
|
#if 0
|
|
if (myChunk->free == 2)
|
|
dprintf("[T] Possible 'double free' of chunk @ 0x%08x:0x%x\n\n",
|
|
myChunk->addr, myChunk->free);
|
|
#endif
|
|
|
|
myChunk->flags = fStruct->flags;
|
|
myChunk->free += 1;
|
|
}
|
|
|
|
void heapCreate(struct HeapState *heapModel, struct CreateStruct *cStruct) {
|
|
struct HPool *newHeap;
|
|
ULONG i;
|
|
|
|
if (cStruct->base == 0)
|
|
return;
|
|
|
|
newHeap = getHeap(heapModel, cStruct->base);
|
|
newHeap->commit = cStruct->commit;
|
|
newHeap->reserve = cStruct->reserve;
|
|
newHeap->lock = cStruct->lock;
|
|
newHeap->flags = cStruct->flags;
|
|
}
|
|
|
|
void heapDestroy(struct HeapState *heapModel, struct DestroyStruct *dStruct) {
|
|
// Only place we mark heaps as not in use
|
|
struct HPool *deadHeap;
|
|
|
|
if (dStruct->heapHandle == 0)
|
|
return;
|
|
|
|
deadHeap = getHeap(heapModel, dStruct->heapHandle);
|
|
free(deadHeap->chunks);
|
|
memset(deadHeap, 0, sizeof(struct HPool));
|
|
heapModel->numHeaps--;
|
|
}
|
|
|
|
void heapCoalesce(struct HeapState *heapModel, struct CoalesceStruct *cfbStruct) {
|
|
// Only place we mark chunks as not in use
|
|
ULONG i, j, latest, last, offset;
|
|
struct HPool *heap;
|
|
|
|
if (heapModel->numHeaps == 0)
|
|
return;
|
|
|
|
// Get the heap handle from the CoalesceStruct
|
|
heap = getHeap(heapModel, cfbStruct->heapHandle);
|
|
|
|
if (heap->numChunks < 2)
|
|
return;
|
|
|
|
//dprintf("[T] Attempting to coalese heap 0x%08x...\n", cfbStruct->heapHandle);
|
|
|
|
i = heap->inUseHead;
|
|
|
|
//dprintf("[T] Starting with chunk %d at 0x%08x\n", i, CHUNK(i).addr);
|
|
|
|
// Walk the list Coalescing consecutive free chunks
|
|
// This assumes that two chunks in a row are actually consecutive...
|
|
// This is wrong. If I alloc two large blocks, free the first, then alloc
|
|
// a small one, then free the small one and the second one, then coalesce
|
|
// the result will ignore the space in between, so we need to take addresses
|
|
// into account, and/or we need to create free spaceholder chunks when creating
|
|
// chunks in a smaller space than is currently existing.
|
|
while (i != NULLNODE) {
|
|
if (CHUNK(i).free) {
|
|
//dprintf("[T] Found free chunk %d at 0x%08x\n", i, CHUNK(i).addr);
|
|
j = FindOffsetForChunk(heap, NEXTADDR(i));
|
|
while (j != NULLNODE && CHUNK(j).free) {
|
|
|
|
//dprintf("[T] Found free chunk %d at 0x%08x\n", j, CHUNK(j).addr);
|
|
|
|
// XXX Handle this by address for unknown chunks
|
|
CHUNK(i).size += CHUNK(j).size + SPACEBETWEEN; // This is only right on Vista... and only
|
|
// most of the time - win2k is insane and
|
|
// goes backwards :(
|
|
|
|
// fix up hash bucket list
|
|
removeBucket(CHUNK(j).addr, heap);
|
|
|
|
//dprintf("[T] Coalescing 0x%08x and 0x%08x. New size: %d bytes\n",
|
|
//CHUNK(i).addr, CHUNK(j).addr, CHUNK(i).size);
|
|
|
|
j = FindOffsetForChunk(heap, NEXTADDR(i));
|
|
}
|
|
}
|
|
i = CHUNK(i).nextInUse;
|
|
// Check Address ordering
|
|
// If out of order, quicksort then start this loop again
|
|
// from first in use
|
|
}
|
|
|
|
}
|
|
|
|
struct HPool *getHeap(struct HeapState *heapModel, PVOID heapHandle) {
|
|
ULONG i;
|
|
struct HPool *newHeap = NULL;
|
|
|
|
for (i = 0; i < heapModel->hPoolListLen; i++) {
|
|
if ((heapModel->heaps[i]).base == heapHandle)
|
|
return (&(heapModel->heaps[i]));
|
|
if (heapModel->numHeaps == heapModel->hPoolListLen) // Add some short circuits here
|
|
continue;
|
|
if (newHeap != NULL)
|
|
continue;
|
|
if ((heapModel->heaps[i]).inUse == FALSE)
|
|
newHeap = &(heapModel->heaps[i]);
|
|
}
|
|
|
|
// If we get here we haven't found a heap, so lets make one
|
|
//dprintf("[T] Creating entry for heap: 0x%08x\n", heapHandle);
|
|
|
|
if (newHeap == NULL) {
|
|
heapModel->hPoolListLen = heapModel->hPoolListLen * 2;
|
|
newHeap = heapModel->heaps;
|
|
heapModel->heaps = (struct HPool *) realloc(heapModel->heaps,
|
|
heapModel->hPoolListLen * sizeof(struct HPool));
|
|
if (heapModel->heaps == NULL) {
|
|
heapModel->heaps = newHeap;
|
|
//crushModel(heapModel); ADD ME XXX
|
|
#ifndef THREEDHEAPFU_ENABLED
|
|
dprintf("[T] OOM getting new heaps!\n\n");
|
|
#endif
|
|
return (NULL);
|
|
}
|
|
newHeap = &(heapModel->heaps[heapModel->numHeaps]);
|
|
// Clean the newly allocated memory
|
|
memset(newHeap, 0, (sizeof (struct HPool) * (heapModel->hPoolListLen - heapModel->numHeaps)));
|
|
}
|
|
|
|
heapModel->numHeaps++;
|
|
newHeap->base = heapHandle;
|
|
newHeap->inUse = TRUE;
|
|
newHeap->chunkListLen = 32;
|
|
newHeap->chunks = (struct HeapChunk *) calloc(newHeap->chunkListLen,
|
|
sizeof (struct HeapChunk));
|
|
newHeap->map = (ULONG *) malloc((0x007fffff/8) * sizeof (ULONG));
|
|
memset(newHeap->map, 0xff, (0x007fffff/8) * sizeof (ULONG));
|
|
newHeap->inUseHead = newHeap->lastInUse = NULLNODE;
|
|
|
|
return (newHeap);
|
|
}
|
|
|
|
struct HeapChunk *getChunk(struct HPool *heap, PVOID memoryPointer, ULONG inAfter) {
|
|
ULONG i, *oldMap;
|
|
struct HeapChunk *newChunk, *niuChunk = NULL;
|
|
|
|
i = hash(memoryPointer, heap);
|
|
i = heap->map[i];
|
|
while (i != NULLNODE) {
|
|
if (CHUNK(i).addr == memoryPointer)
|
|
return (&CHUNK(i));
|
|
i = CHUNK(i).nextBucket;
|
|
}
|
|
|
|
if (niuChunk == NULL) {
|
|
if (heap->numChunks < heap->chunkListLen) {
|
|
if ((heap->chunks[heap->numChunks]).inUse == FALSE) {
|
|
//dprintf("[XXX] FOUND CHUNK WHERE EXPECTED! :)\n\n");
|
|
niuChunk = &(heap->chunks[heap->numChunks]);
|
|
niuChunk->addr = memoryPointer;
|
|
niuChunk->heapHandle = heap->base;
|
|
insertBucket(heap->numChunks, heap, inAfter);
|
|
return (niuChunk);
|
|
}
|
|
// Heap has been coalesced and there are gaps
|
|
// Work backwards in this case - should be MUCH faster
|
|
// If this is noticably slow, we'll switch to a stack of unused chunks
|
|
// (realloc stack with heap)
|
|
for (i = 0; i < heap->chunkListLen; i++) {
|
|
if ((heap->chunks[i]).inUse == FALSE) {
|
|
niuChunk = &(heap->chunks[i]);
|
|
niuChunk->addr = memoryPointer;
|
|
niuChunk->heapHandle = heap->base;
|
|
insertBucket(i, heap, inAfter);
|
|
return (niuChunk);
|
|
}
|
|
}
|
|
dprintf("[XXX] Totally fucked up - chunk info doesnt jive with heap\n\n");
|
|
}
|
|
|
|
heap->chunkListLen = heap->chunkListLen * 2;
|
|
niuChunk = heap->chunks;
|
|
heap->chunks = (struct HeapChunk *) realloc(heap->chunks,
|
|
heap->chunkListLen * sizeof (struct HeapChunk));
|
|
|
|
if (heap->chunks == NULL) {
|
|
heap->chunks = niuChunk;
|
|
//crushHeap(heap);
|
|
heap->chunkListLen = heap->chunkListLen/2;
|
|
#ifndef THREEDHEAPFU_ENABLED
|
|
dprintf("[T] OOM getting new chunks! %d -> %d\n\n",
|
|
heap->chunkListLen, heap->chunkListLen*2);
|
|
#endif
|
|
return (NULL);
|
|
}
|
|
|
|
niuChunk = &(heap->chunks[heap->numChunks]);
|
|
memset(niuChunk, 0, (sizeof (struct HeapChunk) * (heap->chunkListLen - heap->numChunks)));
|
|
}
|
|
|
|
niuChunk->addr = memoryPointer;
|
|
niuChunk->heapHandle = heap->base;
|
|
insertBucket(heap->numChunks, heap, inAfter);
|
|
return (niuChunk);
|
|
}
|