├── heapEnc.exe └── heapEnc.nim /heapEnc.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbaertsch/nimHeapEnc/HEAD/heapEnc.exe -------------------------------------------------------------------------------- /heapEnc.nim: -------------------------------------------------------------------------------- 1 | #[ 2 | Basic heap encryption example in Nim. Compile like `nim c -d:mingw -o:./bin/ --threads:on --mm:orc -d:release .\heapEnc.nim` 3 | UPDATE: as of nim 2.0; 4 | - Using the `rollXor` proc nested in the heapsleep proc resulted in the compiler creating a clousure which uses the heap. 5 | - We also now need to use -mm:refc or -mm:markAndSweep since the Arc/orc models both now use shared heaps across all nim-threads 6 | -nbaertsch 7 | ]# 8 | import winim/lean 9 | import winim/inc/tlhelp32 10 | import strformat 11 | 12 | let keyBuf: array[16, byte] = [byte 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF ] 13 | 14 | proc rollXor*(pkey: array[16, byte], p: ptr UncheckedArray[byte], cb: int) = 15 | for i in 0..cb-1: 16 | p[i] = p[i] xor pkey[(i mod (16))] 17 | 18 | # Encrypts all non busy heap blocks and calls SmartEkko() 19 | proc heapEncSleep(ms: DWORD, keyBuf: ptr array[16, byte]) {.stdcall.} = 20 | var numHeaps = GetProcessHeaps(0,NULL) 21 | var heapHandlesOnHeap = newSeq[HANDLE](numHeaps) 22 | GetProcessHeaps(numHeaps, (PHANDLE)(addr heapHandlesOnHeap[0])) 23 | 24 | 25 | var hHeap: HANDLE = HeapCreate(0,0,0) # 'Safe' heap needs to be created after calling GetProcessHeaps so we don't xor our own data 26 | defer: HeapDestroy(hHeap) 27 | 28 | var pHeaps: ptr UncheckedArray[HANDLE] = cast[ptr UncheckedArray[HANDLE]](HeapAlloc(hHeap, 0, (sizeof(HANDLE) * numHeaps).SIZE_T)) 29 | copyMem(pHeaps, addr heapHandlesOnHeap[0], sizeof(HANDLE) * numHeaps) 30 | zeroMem(addr heapHandlesOnHeap, sizeof(heapHandlesOnHeap)) 31 | 32 | var pHeapEntry: ptr PROCESS_HEAP_ENTRY = cast[ptr PROCESS_HEAP_ENTRY](HeapAlloc(hHeap, 0, (sizeof(PROCESS_HEAP_ENTRY)).SIZE_T)) 33 | 34 | # Heap xor 35 | for i in DWORD(0) .. numHeaps-1: 36 | #if (pHeaps[i] == GetProcessHeap()): continue # Skip main process heap 37 | #echo "xoring heap ", i, " of ", numHeaps-1 38 | SecureZeroMemory(pHeapEntry, sizeof(PROCESS_HEAP_ENTRY)) 39 | while HeapWalk(pHeaps[i], pHeapEntry).bool: # walking heap entries 40 | if (pHeapEntry[].wFlags and PROCESS_HEAP_ENTRY_BUSY) != 0: # only allocated blocks 41 | for i in 0..pHeapEntry[].cbData.int-1: 42 | cast[ptr UncheckedArray[byte]](pHeapEntry[].lpData)[i] = cast[ptr UncheckedArray[byte]](pHeapEntry[].lpData)[i] xor keyBuf[][(i mod (16))] 43 | 44 | Sleep(ms) 45 | 46 | # Heap xor 47 | for i in DWORD(0) .. numHeaps-1: 48 | #if (pHeaps[i] == GetProcessHeap()): continue # Skip main process heap 49 | SecureZeroMemory(pHeapEntry, sizeof(PROCESS_HEAP_ENTRY)) 50 | while HeapWalk(pHeaps[i], pHeapEntry).bool: # walking heap entries 51 | if (pHeapEntry[].wFlags and PROCESS_HEAP_ENTRY_BUSY) != 0: # only allocated blocks 52 | for i in 0..pHeapEntry[].cbData.int-1: 53 | cast[ptr UncheckedArray[byte]](pHeapEntry[].lpData)[i] = cast[ptr UncheckedArray[byte]](pHeapEntry[].lpData)[i] xor keyBuf[][(i mod (16))] 54 | 55 | proc DoSuspendThreads*(targetProcessId: DWORD, targetThreadId: DWORD) = 56 | # Take a module snapshot and start walking through it 57 | var hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0) 58 | var threadEntry: THREADENTRY32 59 | threadEntry.dwSize = sizeof(THREADENTRY32).DWORD 60 | var contThreadSnapWalk = Thread32First(hThreadSnap, &threadEntry) 61 | 62 | # ThreadSnapWalk 63 | while contThreadSnapWalk: 64 | if (threadEntry.dwSize >= (4*sizeof(DWORD))): 65 | #if defined DEBUG: echo &"Found thread\n\tPID: {threadEntry.th32OwnerProcessID.int.toHex()}\tTID: {threadEntry.th32ThreadID.int.toHex()}" 66 | if (threadEntry.th32OwnerProcessID == targetProcessId) and (threadEntry.th32ThreadID != targetThreadId): 67 | var hThread: HANDLE = OpenThread(THREAD_SUSPEND_RESUME, false, threadEntry.th32ThreadID) 68 | if hThread != 0: 69 | if defined DEBUG: echo &"Suspending thread {(threadEntry.th32ThreadID).int}" 70 | SuspendThread(hThread) 71 | CloseHandle(hThread) 72 | 73 | # increment thread entry 74 | threadEntry.dwSize = DWORD(sizeof(THREADENTRY32)) 75 | contThreadSnapWalk = Thread32Next(hThreadSnap, &threadEntry) 76 | 77 | proc DoResumeThreads*(targetProcessId: DWORD, targetThreadId: DWORD) = 78 | # Take a module snapshot and start walking through it 79 | var hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0) 80 | var threadEntry: THREADENTRY32 81 | threadEntry.dwSize = sizeof(THREADENTRY32).DWORD 82 | var contThreadSnapWalk = Thread32First(hThreadSnap, &threadEntry) 83 | 84 | # ThreadSnapWalk 85 | while contThreadSnapWalk: 86 | if (threadEntry.dwSize >= (4*sizeof(DWORD))): 87 | #if defined DEBUG: echo &"Found thread\n\tPID: {threadEntry.th32OwnerProcessID.int.toHex()}\tTID: {threadEntry.th32ThreadID.int.toHex()}" 88 | if (threadEntry.th32OwnerProcessID == targetProcessId) and (threadEntry.th32ThreadID != targetThreadId): 89 | var hThread: HANDLE = OpenThread(THREAD_SUSPEND_RESUME, false, threadEntry.th32ThreadID) 90 | if hThread != 0: 91 | if defined DEBUG: echo &"Resuming thread {(threadEntry.th32ThreadID).int}" 92 | ResumeThread(hThread) 93 | CloseHandle(hThread) 94 | 95 | # increment thread entry 96 | threadEntry.dwSize = DWORD(sizeof(THREADENTRY32)) 97 | contThreadSnapWalk = Thread32Next(hThreadSnap, &threadEntry) 98 | 99 | when isMainModule: 100 | #enableHook(heapEncSleep) 101 | while true: 102 | echo "sleeping for 3 on key ->" 103 | discard stdin.readline 104 | DoSuspendThreads(GetCurrentProcessId(), GetCurrentThreadId()) 105 | heapEncSleep(3 * 1000, keyBuf.addr) 106 | DoResumeThreads(GetCurrentProcessId(), GetCurrentThreadId()) 107 | --------------------------------------------------------------------------------