mimikatz/modules/kull_m_memory.c

448 lines
15 KiB
C

/* Benjamin DELPY `gentilkiwi`
https://blog.gentilkiwi.com
benjamin@gentilkiwi.com
Licence : https://creativecommons.org/licenses/by/4.0/
*/
#include "kull_m_memory.h"
KULL_M_MEMORY_HANDLE KULL_M_MEMORY_GLOBAL_OWN_HANDLE = {KULL_M_MEMORY_TYPE_OWN, NULL};
BOOL kull_m_memory_open(IN KULL_M_MEMORY_TYPE Type, IN HANDLE hAny, OUT PKULL_M_MEMORY_HANDLE *hMemory)
{
BOOL status = FALSE;
*hMemory = (PKULL_M_MEMORY_HANDLE) LocalAlloc(LPTR, sizeof(KULL_M_MEMORY_HANDLE));
if(*hMemory)
{
(*hMemory)->type = Type;
switch (Type)
{
case KULL_M_MEMORY_TYPE_OWN:
status = TRUE;
break;
case KULL_M_MEMORY_TYPE_PROCESS:
if((*hMemory)->pHandleProcess = (PKULL_M_MEMORY_HANDLE_PROCESS) LocalAlloc(LPTR, sizeof(KULL_M_MEMORY_HANDLE_PROCESS)))
{
(*hMemory)->pHandleProcess->hProcess = hAny;
status = TRUE;
}
break;
case KULL_M_MEMORY_TYPE_FILE:
if((*hMemory)->pHandleFile = (PKULL_M_MEMORY_HANDLE_FILE) LocalAlloc(LPTR, sizeof(KULL_M_MEMORY_HANDLE_FILE)))
{
(*hMemory)->pHandleFile->hFile = hAny;
status = TRUE;
}
break;
case KULL_M_MEMORY_TYPE_PROCESS_DMP:
if((*hMemory)->pHandleProcessDmp = (PKULL_M_MEMORY_HANDLE_PROCESS_DMP) LocalAlloc(LPTR, sizeof(KULL_M_MEMORY_HANDLE_PROCESS_DMP)))
status = kull_m_minidump_open(hAny, &(*hMemory)->pHandleProcessDmp->hMinidump);
break;
case KULL_M_MEMORY_TYPE_KERNEL:
if((*hMemory)->pHandleDriver = (PKULL_M_MEMORY_HANDLE_KERNEL) LocalAlloc(LPTR, sizeof(KULL_M_MEMORY_HANDLE_KERNEL)))
{
(*hMemory)->pHandleDriver->hDriver = hAny;
status = TRUE;
}
break;
default:
break;
}
if(!status)
LocalFree(*hMemory);
}
return status;
}
PKULL_M_MEMORY_HANDLE kull_m_memory_close(IN PKULL_M_MEMORY_HANDLE hMemory)
{
if(hMemory)
{
switch (hMemory->type)
{
case KULL_M_MEMORY_TYPE_PROCESS:
LocalFree(hMemory->pHandleProcess);
break;
case KULL_M_MEMORY_TYPE_FILE:
LocalFree(hMemory->pHandleFile);
break;
case KULL_M_MEMORY_TYPE_PROCESS_DMP:
if(hMemory->pHandleProcessDmp)
{
kull_m_minidump_close(hMemory->pHandleProcessDmp->hMinidump);
LocalFree(hMemory->pHandleProcessDmp);
}
break;
case KULL_M_MEMORY_TYPE_KERNEL:
LocalFree(hMemory->pHandleDriver);
break;
default:
break;
}
return (PKULL_M_MEMORY_HANDLE) LocalFree(hMemory);
}
else return NULL;
}
BOOL kull_m_memory_copy(OUT PKULL_M_MEMORY_ADDRESS Destination, IN PKULL_M_MEMORY_ADDRESS Source, IN SIZE_T Length)
{
BOOL status = FALSE;
BOOL bufferMeFirst = FALSE;
KULL_M_MEMORY_ADDRESS aBuffer = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE};
DWORD nbReadWrite;
switch(Destination->hMemory->type)
{
case KULL_M_MEMORY_TYPE_OWN:
switch(Source->hMemory->type)
{
case KULL_M_MEMORY_TYPE_OWN:
RtlCopyMemory(Destination->address, Source->address, Length);
status = TRUE;
break;
case KULL_M_MEMORY_TYPE_PROCESS:
status = ReadProcessMemory(Source->hMemory->pHandleProcess->hProcess, Source->address, Destination->address, Length, NULL);
break;
case KULL_M_MEMORY_TYPE_PROCESS_DMP:
status = kull_m_minidump_copy(Source->hMemory->pHandleProcessDmp->hMinidump, Destination->address, Source->address, Length);
break;
case KULL_M_MEMORY_TYPE_FILE:
if(SetFilePointer(Source->hMemory->pHandleFile->hFile, PtrToLong(Source->address), NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER)
status = ReadFile(Source->hMemory->pHandleFile->hFile, Destination->address, (DWORD) Length, &nbReadWrite, NULL);
break;
case KULL_M_MEMORY_TYPE_KERNEL:
status = kull_m_kernel_ioctl_handle(Source->hMemory->pHandleDriver->hDriver, IOCTL_MIMIDRV_VM_READ, Source->address, 0, &Destination->address, (PDWORD) &Length, FALSE);
break;
default:
break;
}
break;
case KULL_M_MEMORY_TYPE_PROCESS:
switch(Source->hMemory->type)
{
case KULL_M_MEMORY_TYPE_OWN:
status = WriteProcessMemory(Destination->hMemory->pHandleProcess->hProcess, Destination->address, Source->address, Length, NULL);
break;
default:
bufferMeFirst = TRUE;
break;
}
break;
case KULL_M_MEMORY_TYPE_FILE:
switch(Source->hMemory->type)
{
case KULL_M_MEMORY_TYPE_OWN:
if(!Destination->address || SetFilePointer(Destination->hMemory->pHandleFile->hFile, PtrToLong(Destination->address), NULL, FILE_BEGIN))
status = WriteFile(Destination->hMemory->pHandleFile->hFile, Source->address, (DWORD) Length, &nbReadWrite, NULL);
break;
default:
bufferMeFirst = TRUE;
break;
}
break;
case KULL_M_MEMORY_TYPE_KERNEL:
switch(Source->hMemory->type)
{
case KULL_M_MEMORY_TYPE_OWN:
status = kull_m_kernel_ioctl_handle(Destination->hMemory->pHandleDriver->hDriver, IOCTL_MIMIDRV_VM_WRITE, Source->address, (DWORD) Length, &Destination->address, NULL, FALSE);
break;
default:
bufferMeFirst = TRUE;
break;
}
break;
default:
break;
}
if(bufferMeFirst)
{
if(aBuffer.address = LocalAlloc(LPTR, Length))
{
if(kull_m_memory_copy(&aBuffer, Source, Length))
status = kull_m_memory_copy(Destination, &aBuffer, Length);
LocalFree(aBuffer.address);
}
}
return status;
}
BOOL kull_m_memory_search(IN PKULL_M_MEMORY_ADDRESS Pattern, IN SIZE_T Length, IN PKULL_M_MEMORY_SEARCH Search, IN BOOL bufferMeFirst)
{
BOOL status = FALSE;
KULL_M_MEMORY_SEARCH sBuffer = {{{NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}, Search->kull_m_memoryRange.size}, NULL};
PBYTE CurrentPtr;
PBYTE limite = (PBYTE) Search->kull_m_memoryRange.kull_m_memoryAdress.address + Search->kull_m_memoryRange.size;
switch(Pattern->hMemory->type)
{
case KULL_M_MEMORY_TYPE_OWN:
switch(Search->kull_m_memoryRange.kull_m_memoryAdress.hMemory->type)
{
case KULL_M_MEMORY_TYPE_OWN:
for(CurrentPtr = (PBYTE) Search->kull_m_memoryRange.kull_m_memoryAdress.address; !status && (CurrentPtr + Length <= limite); CurrentPtr++)
status = RtlEqualMemory(Pattern->address, CurrentPtr, Length);
CurrentPtr--;
break;
case KULL_M_MEMORY_TYPE_PROCESS:
case KULL_M_MEMORY_TYPE_FILE:
case KULL_M_MEMORY_TYPE_KERNEL:
if(sBuffer.kull_m_memoryRange.kull_m_memoryAdress.address = LocalAlloc(LPTR, Search->kull_m_memoryRange.size))
{
if(kull_m_memory_copy(&sBuffer.kull_m_memoryRange.kull_m_memoryAdress, &Search->kull_m_memoryRange.kull_m_memoryAdress, Search->kull_m_memoryRange.size))
if(status = kull_m_memory_search(Pattern, Length, &sBuffer, FALSE))
CurrentPtr = (PBYTE) Search->kull_m_memoryRange.kull_m_memoryAdress.address + (((PBYTE) sBuffer.result) - (PBYTE) sBuffer.kull_m_memoryRange.kull_m_memoryAdress.address);
LocalFree(sBuffer.kull_m_memoryRange.kull_m_memoryAdress.address);
}
break;
case KULL_M_MEMORY_TYPE_PROCESS_DMP:
if(sBuffer.kull_m_memoryRange.kull_m_memoryAdress.address = kull_m_minidump_remapVirtualMemory64(Search->kull_m_memoryRange.kull_m_memoryAdress.hMemory->pHandleProcessDmp->hMinidump, Search->kull_m_memoryRange.kull_m_memoryAdress.address, Search->kull_m_memoryRange.size))
if(status = kull_m_memory_search(Pattern, Length, &sBuffer, FALSE))
CurrentPtr = (PBYTE) Search->kull_m_memoryRange.kull_m_memoryAdress.address + (((PBYTE) sBuffer.result) - (PBYTE) sBuffer.kull_m_memoryRange.kull_m_memoryAdress.address);
break;
default:
break;
}
break;
default:
break;
}
Search->result = status ? CurrentPtr : NULL;
return status;
}
BOOL kull_m_memory_alloc(IN PKULL_M_MEMORY_ADDRESS Address, IN SIZE_T Lenght, IN DWORD Protection)
{
PVOID ptrAddress = &Address->address;
DWORD lenPtr = sizeof(PVOID);
Address->address = NULL;
switch(Address->hMemory->type)
{
case KULL_M_MEMORY_TYPE_OWN:
Address->address = VirtualAlloc(NULL, Lenght, MEM_COMMIT, Protection);
break;
case KULL_M_MEMORY_TYPE_PROCESS:
Address->address = VirtualAllocEx(Address->hMemory->pHandleProcess->hProcess, NULL, Lenght, MEM_COMMIT, Protection);
break;
case KULL_M_MEMORY_TYPE_KERNEL:
kull_m_kernel_ioctl_handle(Address->hMemory->pHandleDriver->hDriver, IOCTL_MIMIDRV_VM_ALLOC, NULL, (DWORD) Lenght, &ptrAddress, &lenPtr, FALSE);
break;
default:
break;
}
return (Address->address) != NULL;
}
BOOL kull_m_memory_free(IN PKULL_M_MEMORY_ADDRESS Address)
{
BOOL status = FALSE;
switch(Address->hMemory->type)
{
case KULL_M_MEMORY_TYPE_OWN:
status = VirtualFree(Address->address, 0, MEM_RELEASE);
break;
case KULL_M_MEMORY_TYPE_PROCESS:
status = VirtualFreeEx(Address->hMemory->pHandleProcess->hProcess, Address->address, 0, MEM_RELEASE);
break;
case KULL_M_MEMORY_TYPE_KERNEL:
kull_m_kernel_ioctl_handle(Address->hMemory->pHandleDriver->hDriver, IOCTL_MIMIDRV_VM_FREE, Address->address, 0, NULL, NULL, FALSE);
break;
default:
break;
}
return status;
}
BOOL kull_m_memory_query(IN PKULL_M_MEMORY_ADDRESS Address, OUT PMEMORY_BASIC_INFORMATION MemoryInfo)
{
BOOL status = FALSE;
//PMINIDUMP_MEMORY_INFO_LIST maListeInfo = NULL;
//PMINIDUMP_MEMORY_INFO mesInfos = NULL;
//ULONG i;
switch(Address->hMemory->type)
{
case KULL_M_MEMORY_TYPE_OWN:
status = VirtualQuery(Address->address, MemoryInfo, sizeof(MEMORY_BASIC_INFORMATION)) == sizeof(MEMORY_BASIC_INFORMATION);
break;
case KULL_M_MEMORY_TYPE_PROCESS:
status = VirtualQueryEx(Address->hMemory->pHandleProcess->hProcess, Address->address, MemoryInfo, sizeof(MEMORY_BASIC_INFORMATION)) == sizeof(MEMORY_BASIC_INFORMATION);
break;
//case KULL_M_MEMORY_TYPE_PROCESS_DMP:
// if(maListeInfo = (PMINIDUMP_MEMORY_INFO_LIST) kull_m_minidump_stream(Address->hMemory->pHandleProcessDmp->hMinidump, MemoryInfoListStream))
// {
// for(i = 0; (i < maListeInfo->NumberOfEntries) && !status; i++)
// {
// if(status = ((PBYTE) Address->address >= (PBYTE) mesInfos->BaseAddress) && ((PBYTE) Address->address <= (PBYTE) mesInfos->BaseAddress + (SIZE_T) mesInfos->RegionSize))
// {
// MemoryInfo->AllocationBase = (PVOID) mesInfos->AllocationBase;
// MemoryInfo->AllocationProtect = mesInfos->AllocationProtect;
// MemoryInfo->BaseAddress = (PVOID) mesInfos->BaseAddress;
// MemoryInfo->Protect = mesInfos->Protect;
// MemoryInfo->RegionSize = (SIZE_T) mesInfos->RegionSize;
// MemoryInfo->State = mesInfos->State;
// MemoryInfo->Type = mesInfos->Type;
// }
// }
// }
// break;
default:
break;
}
return status;
}
BOOL kull_m_memory_protect(IN PKULL_M_MEMORY_ADDRESS Address, IN SIZE_T dwSize, IN DWORD flNewProtect, OUT OPTIONAL PDWORD lpflOldProtect)
{
BOOL status = FALSE;
DWORD OldProtect;
switch(Address->hMemory->type)
{
case KULL_M_MEMORY_TYPE_OWN:
status = VirtualProtect(Address->address, dwSize, flNewProtect, &OldProtect);
break;
case KULL_M_MEMORY_TYPE_PROCESS:
status = VirtualProtectEx(Address->hMemory->pHandleProcess->hProcess, Address->address, dwSize, flNewProtect, &OldProtect);
break;
default:
break;
}
if(status && lpflOldProtect)
*lpflOldProtect = OldProtect;
return status;
}
BOOL kull_m_memory_equal(IN PKULL_M_MEMORY_ADDRESS Address1, IN PKULL_M_MEMORY_ADDRESS Address2, IN SIZE_T Lenght)
{
BOOL status = FALSE;
KULL_M_MEMORY_ADDRESS aBuffer = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE};
switch(Address1->hMemory->type)
{
case KULL_M_MEMORY_TYPE_OWN:
switch(Address2->hMemory->type)
{
case KULL_M_MEMORY_TYPE_OWN:
status = RtlEqualMemory(Address1->address, Address2->address, Lenght);
break;
default:
status = kull_m_memory_equal(Address2, Address1, Lenght);
break;
}
break;
default:
if(aBuffer.address = LocalAlloc(LPTR, Lenght))
{
if(kull_m_memory_copy(&aBuffer, Address1, Lenght))
status = kull_m_memory_equal(&aBuffer, Address2, Lenght);
LocalFree(aBuffer.address);
}
break;
}
return status;
}
BOOL kull_m_memory_quick_compress(IN PVOID data, IN DWORD size, IN OUT PVOID *compressedData, IN OUT PDWORD compressedSize)
{
BOOL status = FALSE;
DWORD CompressBufferWorkSpaceSize, CompressFragmentWorkSpaceSize;
PVOID WorkSpace;
if(NT_SUCCESS(RtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, &CompressBufferWorkSpaceSize, &CompressFragmentWorkSpaceSize)))
{
if(WorkSpace = LocalAlloc(LPTR, CompressBufferWorkSpaceSize))
{
if((*compressedData) = LocalAlloc(LPTR, size))
{
status = NT_SUCCESS(RtlCompressBuffer(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, (PUCHAR) data, size, (PUCHAR) (*compressedData), size, 4096, compressedSize, WorkSpace));
if(!status)
LocalFree(*compressedData);
}
LocalFree(WorkSpace);
}
}
return status;
}
BOOL kull_m_memory_quick_decompress(IN PVOID data, IN DWORD size, IN OPTIONAL DWORD originalSize, IN OUT PVOID *decompressedData, IN OUT PDWORD decompressedSize)
{
BOOL status = FALSE;
NTSTATUS ntStatus = STATUS_BAD_COMPRESSION_BUFFER;
DWORD UncompressedBufferSize;
for(UncompressedBufferSize = (originalSize ? originalSize : (size << 2)); ntStatus == STATUS_BAD_COMPRESSION_BUFFER; UncompressedBufferSize <<= 2)
{
if((*decompressedData) = LocalAlloc(LPTR, UncompressedBufferSize))
{
ntStatus = RtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, (PUCHAR) (*decompressedData), UncompressedBufferSize, (PUCHAR) data, size, decompressedSize);
status = NT_SUCCESS(ntStatus);
if(!status)
LocalFree(*decompressedData);
}
else break;
}
return status;
}
void kull_m_memory_reverseBytes(PVOID start, SIZE_T size)
{
PBYTE lo = (PBYTE) start, hi = lo + size - 1;
BYTE swap;
while (lo < hi)
{
swap = *lo;
*lo++ = *hi;
*hi-- = swap;
}
}
#if defined(_M_ARM64)
PVOID kull_m_memory_arm64_AddrFromInstr(PVOID cur, ULONG i1, ULONG i2)
{
PVOID addr = NULL;
ULONG_PTR curAddr = (ULONG_PTR)cur, page;
LONG offset;
//kprintf(L"Cur @: %p (%p)\n", curAddr, (curAddr & ~((ULONG_PTR) 0xfff)));
page = (curAddr & ~((ULONG_PTR)0xfff)) + (LONGLONG)(((i1 << 9) & 0x1ffffc000) | ((i1 >> 17) & 0x3000));
//kprintf(L"Page @: %p\n", page);
if ((i2 & 0xb9400000) == 0xb9400000)
{
//kprintf(L"{LDR (immediate -- unsigned offset)}\n");
offset = (i2 >> 10 & 0xfff) << ((i2 >> 30) & 0x3);
}
else if ((i2 & 0x91000000) == 0x91000000)
{
//kprintf(L"{ADD (immediate -- 64 bit variant, 0 shift)}\n");
offset = i2 >> 10 & 0xfff;
}
else
{
PRINT_ERROR(L"i2: %08x\n", i2);
return NULL;
}
//kprintf(L"Offset: 0x%08x\n", offset);
addr = (PVOID)(page + offset);
//kprintf(L"Addr @: %p\n", addr);
return addr;
}
PVOID kull_m_memory_arm64_getRealAddress(PKULL_M_MEMORY_ADDRESS Address, LONG off)
{
PVOID ret = NULL;
ULONG data0, data1;
KULL_M_MEMORY_ADDRESS aBuffer = *Address, aLocalMemory = {&data0, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE};
if (kull_m_memory_copy(&aLocalMemory, &aBuffer, sizeof(data0)))
{
aBuffer.address = (PBYTE) Address->address + off;
aLocalMemory.address = &data1;
if(kull_m_memory_copy(&aLocalMemory, &aBuffer, sizeof(data1)))
ret = kull_m_memory_arm64_AddrFromInstr(Address->address, data0, data1);
}
return ret;
}
#endif