#include "drvCommon.h"
#include "PLUtils.h"
#include "ServiceTableDef.h"
extern "C"
{
#include "ntddk_module.h"
};
#define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
typedef struct _IMAGE_EXPORT_DIRECTORY {
unsigned long Characteristics;
unsigned long TimeDateStamp;
unsigned short MajorVersion;
unsigned short MinorVersion;
unsigned long Name;
unsigned long Base;
unsigned long NumberOfFunctions;
unsigned long NumberOfNames;
unsigned long AddressOfFunctions; // RVA from base of image
unsigned long AddressOfNames; // RVA from base of image
unsigned long AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
static PZwProtectVirtualMemoryType g_pZwProtectVirtualMemory=0;
PVOID
GetProcAddrEx(PVOID DllBase, IN PCHAR FunctionName, ULONG lMaxSize )
{
PVOID Func = NULL;
PULONG NameTableBase;
PUSHORT NameOrdinalTableBase;
PIMAGE_EXPORT_DIRECTORY ExportDirectory;
PULONG Addr;
ULONG ExportSize;
ULONG Low;
ULONG Middle;
ULONG High;
LONG Result;
USHORT OrdinalNumber;
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(
DllBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&ExportSize
);
if (ExportDirectory)
{
if (((char*)ExportDirectory<(char*)DllBase || (char*)ExportDirectory > (char*)DllBase+lMaxSize))
return 0;
NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);
if (((char*)NameTableBase<(char*)DllBase || (char*)NameTableBase > (char*)DllBase+lMaxSize))
return 0;
NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);
if (((char*)NameOrdinalTableBase<(char*)DllBase ||
(char*)NameOrdinalTableBase > (char*)DllBase+lMaxSize))
return 0;
Low = 0;
High = ExportDirectory->NumberOfNames - 1;
// test High
if (((char*)(NameTableBase+High)<(char*)DllBase ||
(char*)(NameTableBase+High)> (char*)DllBase+lMaxSize))
return 0;
while (High >= Low && (LONG)High >= 0) {
Middle = (Low + High) >> 1;
// test NameOrdinalTableBase[Middle]
if ((ULONG)(NameTableBase[Middle]) > lMaxSize)
return 0;
Result = strcmp(FunctionName,
(PCHAR)((PCHAR)DllBase + NameTableBase[Middle]));
if (Result < 0) {
High = Middle - 1;
} else if (Result > 0) {
Low = Middle + 1;
} else {
break;
}
}
if ((LONG)High >= (LONG)Low) {
// test NameOrdinalTableBase[Middle]
if (((char*)(NameOrdinalTableBase+Middle)<(char*)DllBase ||
(char*)(NameOrdinalTableBase+Middle)> (char*)DllBase+lMaxSize))
return 0;
OrdinalNumber = NameOrdinalTableBase[Middle];
Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
Func = (PVOID)((PCHAR)DllBase + Addr[OrdinalNumber]);
if ((ULONG_PTR)Func > (ULONG_PTR)ExportDirectory &&
(ULONG_PTR)Func < ((ULONG_PTR)ExportDirectory + ExportSize)) {
Func = NULL;
}
}
}
return Func;
}
PVOID
GetProcAddr(PVOID DllBase, IN PCHAR FunctionName )
{
return GetProcAddrEx(DllBase, FunctionName, 0xFFFFFFF);
}
PVOID GetProcAddr(ULONG hModule, PCHAR lpProcName)
{
return GetProcAddr((PVOID)hModule, lpProcName);
}
PVOID GetKernelAnalogAddress(ULONG hModule,PCHAR FuncName)
{
PVOID function;
ULONG index;
function=GetProcAddr(hModule,FuncName);
if (!function)
return 0;
index=SYSCALL_INDEX(function);
return (PVOID)KeServiceDescriptorTable->ntoskrnl.ServiceTable[index];
}
NTSTATUS GetAllModulesInfo(PULONG * pRes)
{
ULONG i;
NTSTATUS status = STATUS_NOT_FOUND;
unsigned long Base = 0;
status = ZwQuerySystemInformation( SystemModuleInformation, &i, 0, &i ); // system module info
if (status != STATUS_INFO_LENGTH_MISMATCH)
return status;
*pRes = (PULONG)ExAllocatePool(NonPagedPool, sizeof(ULONG)*i);
if (!*pRes)
return STATUS_NO_MEMORY;
status = ZwQuerySystemInformation( SystemModuleInformation, *pRes, i * sizeof(ULONG), 0);
if (status != STATUS_SUCCESS)
{
ExFreePool(*pRes);
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
}
NTSTATUS GetModuleInfo(PCHAR Mod, SYSTEM_MODULE * pInfo)
{
ULONG i;
NTSTATUS status = STATUS_SUCCESS;
PULONG pBuf = 0;
PSYSTEM_MODULE_INFORMATION pModInfo;
status = GetAllModulesInfo(&pBuf);
if(status != STATUS_SUCCESS)
return status;
pModInfo = (PSYSTEM_MODULE_INFORMATION)pBuf;
status = STATUS_NOT_FOUND;
for (i=0; i<*pBuf; i++)
{
if (!_stricmp((CHAR*)pModInfo->aSM[i].abName + pModInfo->aSM[i].wNameOffset, Mod))
{
*pInfo=pModInfo->aSM[i];
status = STATUS_SUCCESS;
break;
}
}
ExFreePool(pBuf);
return status;
}
ULONG GetModuleBase(PCHAR Mod)
{
NTSTATUS status = 0;
SYSTEM_MODULE sysModuleInfo;
status = GetModuleInfo(Mod, &sysModuleInfo);
if(status != STATUS_SUCCESS)
return 0;
return (ULONG)sysModuleInfo.pAddress;
}
int __stdcall WriteMemoryWithWriter(MemoryWriter* pMemoryWriter,
char* pPlace,
char* pBuffer,
unsigned long ulSize,
unsigned long* pulSizeOut )
{
return pMemoryWriter->pWrite(pMemoryWriter, pPlace, pBuffer, ulSize, pulSizeOut );
}
int __stdcall AllocateHookEx(MemoryRWManager * pRwManager,
Allocator * pAllocator,
void ** ppHook)
{
int status = 0;
int iHookSize = MAX_PATCH_SIZE+5;
char hookBuffer[MAX_PATCH_SIZE+5];
char * pHook = (char*)AllocateMemory(pAllocator, iHookSize);
if (!pHook)
return STATUS_BAD_ALLOC;
memset(hookBuffer, 0x90, iHookSize);
status = WriteMemoryWithWriter(pRwManager->pWriter, pHook, hookBuffer, iHookSize, 0);
if (status)
return STATUS_UNSUCCESSFUL;
*ppHook = pHook;
return 0;
}
void RevertTransactions(TransactionInfo* pTransactionInfo,
int iFunctionsCount,
Allocator * pHookAllocator,
MemoryRWManager* pRwManager)
{
int i = 0;
for( i =0; i < iFunctionsCount; ++i)
{
// revert patch
if (pTransactionInfo[i].result.pChangedCode && pTransactionInfo[i].result.iPatchedSize)
{
WriteMemoryWithWriter(pRwManager->pWriter,
(char*)pTransactionInfo[i].pAddress,
(char*)pTransactionInfo[i].result.pChangedCode,
pTransactionInfo[i].result.iPatchedSize,
0);
}
// delete hook
if (pTransactionInfo[i].pHook)
{
FreeMemory(pHookAllocator, pTransactionInfo[i].pHook);
pTransactionInfo[i].pHook = 0;
}
}
}
#define CALL_SIZE 5
int __stdcall InitializeHookEx(MemoryRWManager * pRwManager, unsigned char * pHook, unsigned char * pHandler)
{
int status = 0;
char buffer[CALL_SIZE];
// Write CALL instruction into buffer
buffer[0] = 0xE8; // 1 byte for CALL opcode
*(size_t*)(buffer + 1) = (unsigned char *)pHandler - (pHook + CALL_SIZE); // 4 bytes for relative address
// Copy CALL instruction from buffer to hook memory
status = WriteMemoryWithWriter(pRwManager->pWriter, (char*)pHook, buffer, CALL_SIZE, 0);
if (status)
return STATUS_UNSUCCESSFUL;
return STATUS_SUCCESS;
}
int PatchFunctions2Ex(MemoryManager * pMemoryManager,
MemoryRWManager * pRwManager,
Allocator * pAllocator,
Allocator * pHookAllocator,
struct Target ** ppTargetInfoVec,
TransactionInfo * pTransactionInfo,
int iFunctionsCount)
{
int i = 0;
void * pHook=0;
int iStatus = 0;
for( i =0; i < iFunctionsCount; ++i)
{
// allocate and initialize hook
{
iStatus = AllocateHookEx(pRwManager, pHookAllocator, &pHook);
if (iStatus != STATUS_SUCCESS)
break;
pTransactionInfo[i].pHook = pHook;
iStatus = InitializeHookEx(pRwManager, (unsigned char*)pHook, (unsigned char*)pTransactionInfo->pHookHandler);
if (iStatus != STATUS_SUCCESS)
break;
}
// try to patch code
iStatus = TryPatchCodeEx(pRwManager,
(unsigned char*)pTransactionInfo[i].pAddress,
pMemoryManager,
ppTargetInfoVec,
pHook);
if (iStatus != STATUS_SUCCESS)
break;
}
if (iStatus == STATUS_SUCCESS)
{
// patch!
int i = 0;
for( i =0; i < iFunctionsCount; ++i)
{
// prepare result structure
pTransactionInfo[i].result.pChangedCode = pTransactionInfo[i].resultBuffer;
// patch code
iStatus = PatchCodeEx(pRwManager,
(unsigned char*)pTransactionInfo[i].pAddress,
pMemoryManager,
ppTargetInfoVec,
pTransactionInfo[i].pHook,
1,
&pTransactionInfo[i].result);
if (iStatus != STATUS_SUCCESS)
break;
}
}
// revert snapshot and clean hooks if failed
if (iStatus != STATUS_SUCCESS)
{
RevertTransactions(pTransactionInfo, iFunctionsCount, pHookAllocator, pRwManager);
}
return iStatus;
}
MemoryRWManager * GetInternalMemoryRwManager()
{
return &g_defaultRwManager;
}
int PatchFunctions2(MemoryManager * pMemoryManager,
Allocator * pAllocator,
Allocator * pHookAllocator,
struct Target ** ppTargetInfoVec,
TransactionInfo * pTransactionInfo,
int iFunctionsCount)
{
return PatchFunctions2Ex(pMemoryManager,
GetInternalMemoryRwManager(),
pAllocator,
pHookAllocator,
ppTargetInfoVec,
pTransactionInfo,
iFunctionsCount);
}
int PatchSomething(SimplePatchContext * pContext, //IN
void * pPlaceToPatch, // IN
void * pHook) // IN
{
int iStatus = 0;
TransactionInfo info[1];
memset(&info, 0, sizeof(info));
info[0].pAddress = pPlaceToPatch;
info[0].pHookHandler = pHook;
iStatus = PatchFunctions2(pContext->pMemoryManager,
pContext->pAllocator,
pContext->pHookAllocator,
g_knownTargets,
info,
1);
return iStatus;
}
NTSTATUS PatchNtosFunction_npaged(wchar_t * pFunctionName,
ULONG nameSizeInBytes,
void * pHook,
int * pRes)
{
UNICODE_STRING setProcessInfo;
SimplePatchContext context;
void * pTarget = 0;
context.pAllocator = &g_kernelNonPagedAllocator;
context.pHookAllocator = &g_kernelNonPagedAllocator;
context.pMemoryManager = &g_kernelMemoryManager;
*pRes = 0;
setProcessInfo.Buffer = pFunctionName;
setProcessInfo.Length = nameSizeInBytes;
setProcessInfo.MaximumLength = nameSizeInBytes;
pTarget = MmGetSystemRoutineAddress(&setProcessInfo);
if (!pTarget)
return STATUS_NOT_FOUND;
{
*pRes = PatchSomething(&context, pTarget, pHook);
if (*pRes)
{
return STATUS_UNSUCCESSFUL;
}
}
return STATUS_SUCCESS;
}
void __stdcall FreeMemory(Allocator* pAllocator, void* pBuffer)
{
pAllocator->pFreeBuffer(pAllocator, pBuffer);
}
void* __stdcall AllocateMemory(Allocator* pAllocator, int iSize)
{
return pAllocator->pAllocateBuffer(pAllocator, iSize);
}
int __stdcall AllocatePostParams(Allocator * pAllocator,
int size,
void * pPostHandler,
void ** ppParamsData,
void ** ppStub)
{
unsigned char * pParamsData = (unsigned char*)AllocateMemory(pAllocator, GET_STUB_SIZE(size));
unsigned char * pCaller = pParamsData+size;
if (!pParamsData)
return STATUS_BAD_ALLOC;
FORM_POST_PARAMS(pParamsData, size, pPostHandler, pCaller)
*ppParamsData = pParamsData;
*ppStub = pCaller;
return 0;
}
int TryPatchCodeEx(MemoryRWManager *pRwManager,
unsigned char * pAddress,
MemoryManager * pMemoryManager,
struct Target ** targets,
void * pHook)
{
return PatchCodeEx(pRwManager,
pAddress,
pMemoryManager,
targets,
pHook,
0,
0);
}
int __stdcall ReadMemoryWithReader( MemoryReader* pMemoryReader,
char* pPlace,
char* pBuffer,
unsigned long ulSize,
unsigned long* pulSizeOut )
{
return pMemoryReader->pRead ( pMemoryReader, pPlace, pBuffer, ulSize, pulSizeOut );
}
int IsCallInstruction(unsigned char * pAddress, int * pSize)
{
if (*pAddress == 0xE8 || *pAddress == 0xE9)
{
*pSize = 5;
return 1;
}
return 0;
}
int __stdcall UnprotectMemory(MemoryManager* pMemoryManager,
void* pBuffer)
{
return pMemoryManager->pUnProtect(pMemoryManager, pBuffer);
}
#pragma pack(push, 1)
struct CCall
{
public:
unsigned char bCode;
unsigned char * pAddress;
};
#pragma pack(pop)
int IsJMP_short(unsigned char * pAddress,
int * pSize)
{
if (*pAddress == 0xEB)
{
*pSize = 2;
return 1;
}
return 0;
}
int __stdcall PatchMemory(MemoryManager* pMemoryManager,
void * pBuffer,
const void * pInfo,
size_t size2Patch)
{
return pMemoryManager->pPatcher(pMemoryManager, pBuffer, pInfo, size2Patch);
}
int InstallHook(MemoryManager * pMemoryManager,
unsigned char * pReadAddress,
unsigned char * pWriteAddress,
int iSize,
void * pHook,
PatchResult * pResult,
int iSizeOfRels,
unsigned char * pRels)
{
unsigned char buff_nops[MAX_PATCH_SIZE];
struct CCall * pCall = (struct CCall*)pWriteAddress;
unsigned char * pStubStart = 0;
int i=0;
int status = 0;
memset(buff_nops, 0x90, sizeof(buff_nops));
// create stub
{
int lostI = 5, iInstuctionSize = 0;
unsigned long * pCorrectionPtr = 0;
unsigned long dwCorrectionValue = 0;
unsigned long currShift = 0;
pStubStart = (unsigned char * )pHook + 5;
pResult->iPatchedSize = 0;
currShift = (unsigned long)(pWriteAddress-pStubStart);
// if starts with call
if (IsJMP_short(pReadAddress, &iInstuctionSize))
{
unsigned char stubBuffer[5] = {0};
unsigned char* bufferPtr = stubBuffer;
// copy changed code for backup
int u = 0;
for(u=0; u < 5;++u)
{
pResult->pChangedCode[u] = pReadAddress[u];
++pResult->iPatchedSize;
}
pCorrectionPtr = (unsigned long * )(pStubStart+1);
dwCorrectionValue = (unsigned long)(pWriteAddress-pStubStart)-3;
// create stub
*bufferPtr++ = 0xE9;
*(unsigned long *)bufferPtr = (unsigned long)(signed char)pReadAddress[1];
bufferPtr+=4;
for(i=2; i < 5;++i)
{
*bufferPtr = pReadAddress[i];
++pStubStart;
}
status = PatchMemory(pMemoryManager, pStubStart, stubBuffer, 5);
if (status)
return status;
pStubStart += 5;
}
else
{
unsigned char stubBuffer[5] = {0};
int bufferIx = 0;
int maxFor = 5;
if (IsCallInstruction(pReadAddress, &iInstuctionSize))
{
pCorrectionPtr = (unsigned long * )(pStubStart+1);
dwCorrectionValue = (unsigned long)(pWriteAddress-pStubStart);
}
if ((iSizeOfRels) && (maxFor > *pRels))
{
maxFor = *pRels;
}
// move function bytes to stub
for(i=0; i < maxFor;++i)
{
pResult->pChangedCode[i] = pReadAddress[i];
++pResult->iPatchedSize;
stubBuffer[bufferIx] = pReadAddress[i];
++bufferIx;
}
status = PatchMemory(pMemoryManager, pStubStart, stubBuffer, bufferIx);
if (status)
return status;
pStubStart += bufferIx;
}
// correct
if (pCorrectionPtr)
*pCorrectionPtr += dwCorrectionValue;
if (iSizeOfRels)
{
unsigned long * pCallPtr = (unsigned long * )(pReadAddress + *pRels);
unsigned long currAdrOfCall = (unsigned long )*pCallPtr + currShift;
status = PatchMemory(pMemoryManager, pStubStart, &currAdrOfCall, sizeof(currAdrOfCall));
if (status)
return status;
status = PatchMemory(pMemoryManager, &(pResult->pChangedCode[i]), &currAdrOfCall, sizeof(currAdrOfCall));
if (status)
return status;
pStubStart += 4;
i += 4;
}
{
unsigned char stubBuffer[MAX_PATCH_SIZE] = {0};
int bufferIx = 0;
// move other data - not corrected!!!
//lostI = i;
for(; i < iSize;++i)
{
pResult->pChangedCode[i] = pReadAddress[i];
++pResult->iPatchedSize;
stubBuffer[bufferIx] = pReadAddress[i];
++bufferIx;
}
status = PatchMemory(pMemoryManager, pStubStart, stubBuffer, bufferIx);
if (status)
return status;
pStubStart += bufferIx;
}
{ // patch nops
status = PatchMemory(pMemoryManager, pWriteAddress + lostI, buff_nops, iSize - lostI);
if (status)
return status;
}
}
{
unsigned char stubBuffer[5] = {0};
unsigned char* bufferPtr = stubBuffer;
*bufferPtr = 0xE9;
*(size_t*)(bufferPtr+1) = (unsigned char * )pWriteAddress - pStubStart +iSize-5;
status = PatchMemory(pMemoryManager, pStubStart, stubBuffer, 5);
if (status)
return status;
// install hook
{
struct CCall source;
source.bCode = 0xE9;
source.pAddress = (unsigned char *)((unsigned char *)pHook - pWriteAddress - 5);
status = PatchMemory(pMemoryManager, pCall, &source, sizeof(source));
if (status)
return status;
}
return status;
}
}
int PatchCodeEx(MemoryRWManager *pRwManager,
unsigned char * pAddress,
MemoryManager * pMemoryManager,
struct Target ** targets,
void * pHook,
int bNeedPatch,
PatchResult * pResult)
{
int i = 0, u = 0;
struct Target * used_target = 0;
int iCallInstructionSize = 0;
// check pre-defined targets
unsigned char patchBuffer[MAX_PATCH_SIZE];
int status = ReadMemoryWithReader(pRwManager->pReader, (char*)pAddress, (char*)patchBuffer, MAX_PATCH_SIZE, 0);
if (status)
return STATUS_UNSUCCESSFUL;
if (IsCallInstruction(patchBuffer, &iCallInstructionSize))
{
status = UnprotectMemory(pMemoryManager, pAddress);
status = UnprotectMemory(pMemoryManager, pHook);
if (bNeedPatch)
{
return InstallHook(pMemoryManager,
patchBuffer,
pAddress,
iCallInstructionSize,
pHook,
pResult,
0,
0);
}
else
return STATUS_SUCCESS;
}
// check targets
for(i=0; targets[i] ;++i)
{
used_target = targets[i];
if (used_target->iSize > MAX_PATCH_SIZE)
return STATUS_INCORRECT_SIZE;
for(u =0; u < used_target->iSize; ++u)
{
if ((patchBuffer[u]&used_target->pFlags[u]) != used_target->pData[u])
goto cont1;
}
// target recognized
status = UnprotectMemory(pMemoryManager, pAddress);
status = UnprotectMemory(pMemoryManager, pHook);
if (bNeedPatch)
return InstallHook(pMemoryManager,
patchBuffer,
pAddress,
used_target->iSize,
pHook,
pResult,
used_target->iSizeOfRels,
used_target->pRels);
else
return STATUS_SUCCESS;
cont1:;
}
return STATUS_PATCH_NOT_FOUND;
}
void __stdcall InitKernelAllocators(PZwProtectVirtualMemoryType pZwProtectVirtualMemory,
Patcher_type pPatcher)
{
g_pZwProtectVirtualMemory = pZwProtectVirtualMemory;
g_kernelMemoryManager.pPatcher = pPatcher;
}
ULONG DisableKernelDefence(KIRQL * OldIrql)
{
ULONG OldCr0=0;
__asm
{
cli
mov eax, cr0
mov OldCr0, eax
and eax, 0xFFFEFFFF
mov cr0, eax
}
return OldCr0;
}
VOID EnableKernelDefence(ULONG OldCr0, KIRQL OldIrql)
{
__asm
{
mov eax, OldCr0
mov cr0, eax
sti
}
}
int Defence_Patcher(MemoryManager* pMemoryManager,
void * pBuffer,
const void * pInfo,
size_t size2Patch)
{
KIRQL OldIrqL;
ULONG OldCr0;
OldCr0 = DisableKernelDefence(&OldIrqL);
memcpy(pBuffer, pInfo, size2Patch);
EnableKernelDefence(OldCr0, OldIrqL);
return 0;
}