Awesome
kli
Simple header only, kernel-mode alternative to lazy_importer.
Example
// Same as KeBugCheck(XBOX_360_SYSTEM_CRASH);
KLI_FN(KeBugCheck)(XBOX_360_SYSTEM_CRASH);
// Same as ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'enoN');
KLI_FN(ExAllocatePoolWithTag)(NonPagedPool, PAGE_SIZE, 'enoN');
How it works
The macro KLI_FN
hashes the name of desired function in compiletime (using fnv1a64), and in runtime it will enumerate the Export Address Table (EAT) of ntoskrnl.exe
to compare against this hash.
To get the kernel base, it uses the SIDT instruction to find the nt!KiDivideErrorFault
Interrupt Service Routine (ISR), and abuses the fact that ntoskrnl.exe is mapped using 2MiB pages to walk downwards until
a valid PE image is found. To avoid issues with discardable sections, checks are ran on the PE image to make sure it's ntoskrnl (otherwise you risk finding random drivers).
Output
NTSTATUS __stdcall DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
unsigned __int64 i; // rdx
__int64 v3; // rax
__int64 v4; // rax
unsigned __int64 v5; // r9
__int128 v6; // xmm0
__int64 v7; // xmm1_8
char v8; // al
__int128 *v9; // r9
__int64 v10; // rcx
_DWORD *v11; // rcx
unsigned __int64 v12; // r11
unsigned int *v13; // r9
unsigned int v14; // edi
unsigned __int64 v15; // rbx
__int64 v16; // rdx
__int64 v17; // r10
char *v18; // rcx
char v19; // al
void (__fastcall *v21)(__int64, __int64, _QWORD, unsigned int *); // r8
_WORD v22[8]; // [rsp+20h] [rbp-30h] BYREF
__int128 v23; // [rsp+30h] [rbp-20h] BYREF
__int64 v24; // [rsp+40h] [rbp-10h]
int v25; // [rsp+48h] [rbp-8h]
__int16 v26; // [rsp+4Ch] [rbp-4h]
char v27; // [rsp+4Eh] [rbp-2h]
char v28; // [rsp+4Fh] [rbp-1h]
i = qword_140003000;
if ( !qword_140003000 )
{
__sidt(v22);
if ( !*(_QWORD *)&v22[1] )
__debugbreak();
for ( i = ((*(unsigned __int16 *)(*(_QWORD *)&v22[1] + 6i64) | ((unsigned __int64)*(unsigned int *)(*(_QWORD *)&v22[1] + 8i64) << 16)) & 0xFFFFFFFFFFFFFFE0ui64) << 16;
;
i -= 0x200000i64 )
{
if ( *(_WORD *)i == 23117 )
{
v3 = *(int *)(i + 60);
if ( *(_DWORD *)(v3 + i) == 17744 && *(_WORD *)(v3 + i + 4) == 0x8664 )
{
v4 = *(unsigned int *)(v3 + i + 136);
v28 = 0;
v5 = i + *(unsigned int *)(v4 + i + 12);
v6 = *(_OWORD *)v5;
v25 = *(_DWORD *)(v5 + 24);
v7 = *(_QWORD *)(v5 + 16);
v26 = *(_WORD *)(v5 + 28);
v27 = *(_BYTE *)(v5 + 30);
v23 = v6;
v24 = v7;
v8 = v6;
v9 = &v23;
v10 = 0xCBF29CE484222325ui64;
if ( (_BYTE)v6 )
{
do
{
v9 = (__int128 *)((char *)v9 + 1);
v10 = 0x100000001B3i64 * (v8 ^ (unsigned __int64)v10);
v8 = *(_BYTE *)v9;
}
while ( *(_BYTE *)v9 );
if ( v10 == 0x9BE7F70164F3DBF0ui64 )
break;
}
}
}
}
qword_140003000 = i;
}
v11 = (_DWORD *)(i + *(unsigned int *)(*(int *)(i + 60) + i + 136));
v12 = i + (unsigned int)v11[7];
v13 = (unsigned int *)(i + (unsigned int)v11[8]);
v14 = v11[6];
v15 = i + (unsigned int)v11[9];
v16 = 0i64;
if ( v14 )
{
while ( 1 )
{
v17 = 0xCBF29CE484222325ui64;
v18 = (char *)(qword_140003000 + *v13);
v19 = *v18;
if ( *v18 )
{
do
{
++v18;
v17 = 0x100000001B3i64 * (v19 ^ (unsigned __int64)v17);
v19 = *v18;
}
while ( *v18 );
if ( v17 == 0xDA68D5AF5F40988Fui64 )
break;
}
v16 = (unsigned int)(v16 + 1);
++v13;
if ( (unsigned int)v16 >= v14 )
goto LABEL_18;
}
v21 = (void (__fastcall *)(__int64, __int64, _QWORD, unsigned int *))(qword_140003000
+ *(unsigned int *)(v12
+ 4i64
* *(unsigned __int16 *)(v15 + 2i64 * (unsigned int)v16)));
v21(864i64, v16, v21, v13);
}
else
{
LABEL_18:
__debugbreak();
MEMORY[0](864i64, v16, 0i64, v13);
}
return 0;
}
Credits
- https://twitter.com/JustasMasiulis for making lazy_importer
- https://twitter.com/Ch40zz_ codereview/debugging
- https://twitter.com/duk_37 codereview