作者:ZwelL
工作需要,想控制进程的创建,于是HOOK了ZwCreateProcess,后来发现xp和2003中创建进程的都用NtCreateProcessEx(参见[1])。
但是ZwCreateProcessEx未被ntoskrnl.exe导出,用softice的ntcall命令也没有看到,网上也没有找到相关代码。没办法,跟踪ntoskrnl!ZwCreateProcess
>u ntoskrnl!ZwCreateProcessEx
_ZwCreateProcess
0008:804e7ae2 bb32000000 mov eax, 00000032
但是ZwCreateProcessEx有9个参数,最后一个未知,4字节,猜成HANDLE型。
原型如下:
typedef NTSTATUS (*NTCREATEPROCESSEX)(
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ParentProcess,
IN BOOLEAN InheritObjectTable,
IN HANDLE SectionHandle OPTIONAL,
IN HANDLE DebugPort OPTIONAL,
IN HANDLE ExceptionPort OPTIONAL,
IN HANDLE Unknown );
最终用硬编码HOOK 成功,代码如下:
#include "ntddk.h"
#include "stdarg.h"
#include "stdio.h"
#include "ntiologc.h"
#define DWORD unsigned long
#define WORD unsigned short
#define BOOL unsigned long
typedef struct ServiceDescriptorEntry {
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase; //Used only in checked build
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry, *PServiceDescriptorTableEntry;
extern PServiceDescriptorTableEntry KeServiceDescriptorTable;
typedef NTSTATUS (*NTCREATEPROCESSEX)(
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ParentProcess,
IN BOOLEAN InheritObjectTable,
IN HANDLE SectionHandle OPTIONAL,
IN HANDLE DebugPort OPTIONAL,
IN HANDLE ExceptionPort OPTIONAL,
IN HANDLE Unknown );
NTCREATEPROCESSEX OldNtCreateProcessEx;
// Length of process name (rounded up to next DWORD)
#define PROCNAMELEN 20
// Maximum length of NT process name
#define NT_PROCNAMELEN 16
ULONG gProcessNameOffset;
void GetProcessNameOffset()
{
PEPROCESS curproc;
int i;
curproc = PsGetCurrentProcess();
for( i = 0; i < 3*PAGE_SIZE; i++ )
{
if( !strncmp( "System", (PCHAR) curproc + i, strlen("System") ))
{
gProcessNameOffset = i;
}
}
}
BOOL GetProcessName( PCHAR theName )
{
PEPROCESS curproc;
char *nameptr;
ULONG i;
KIRQL oldirql;
if( gProcessNameOffset )
{
curproc = PsGetCurrentProcess();
nameptr = (PCHAR) curproc + gProcessNameOffset;
strncpy( theName, nameptr, NT_PROCNAMELEN );
theName[NT_PROCNAMELEN] = 0; /* NULL at end */
return TRUE;
}
return FALSE;
}
NTSTATUS NewNtCreateProcessEx(
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ParentProcess,
IN BOOLEAN InheritObjectTable,
IN HANDLE SectionHandle OPTIONAL,
IN HANDLE DebugPort OPTIONAL,
IN HANDLE ExceptionPort OPTIONAL,
IN HANDLE Unknown OPTIONAL)
{
CHAR aProcessName[PROCNAMELEN];
GetProcessName( aProcessName );
DbgPrint("rootkit: NewNtCreateProcessEx() from %s\n", aProcessName);
//DbgPrint("ok");
return OldNtCreateProcessEx(ProcessHandle,DesiredAccess,
ObjectAttributes,ParentProcess,InheritObjectTable,SectionHandle,DebugPort,ExceptionPort,Unknown);
}
NTSTATUS
OnStubDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest (Irp,
IO_NO_INCREMENT
);
return Irp->IoStatus.Status;
}
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
DbgPrint("ROOTKIT: OnUnload called\n");
_asm
{
CLI //dissable interrupt
MOV EAX, CR0 //move CR0 register into EAX
AND EAX, NOT 10000H //disable WP bit
MOV CR0, EAX //write register back
}
(NTCREATEPROCESSEX)(*(((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase + 0x32))=OldNtCreateProcessEx;
_asm
{
MOV EAX, CR0 //move CR0 register into EAX
OR EAX, 10000H //enable WP bit
MOV CR0, EAX //write register back
STI //enable interrupt
}
}
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )
{
int i;
DbgPrint("My Driver Loaded!");
GetProcessNameOffset();
// Register a dispatch function
for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
theDriverObject->MajorFunction[i] = OnStubDispatch;
}
theDriverObject->DriverUnload = OnUnload;
// save old system call locations
//OldNtCreateProcessEx=(NTCREATEPROCESSEX)(SYSTEMSERVICE(0x32));
OldNtCreateProcessEx=(NTCREATEPROCESSEX)(*(((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase + 0x32));
_asm
{
CLI //dissable interrupt
MOV EAX, CR0 //move CR0 register into EAX
AND EAX, NOT 10000H //disable WP bit
MOV CR0, EAX //write register back
}
(NTCREATEPROCESSEX)(*(((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase + 0x32))= NewNtCreateProcessEx;
_asm
{
MOV EAX, CR0 //move CR0 register into EAX
OR EAX, 10000H //enable WP bit
MOV CR0, EAX //write register back
STI //enable interrupt
}
return STATUS_SUCCESS;
}
这样很不爽,每次都要这样看索引号,问了SOBEIT,可以通过从NTDLL中这样获取服务索引号:
来自rookkit:
#include <windows.h>
#include <stdio.h>
BOOL GetId( char *FuncName, ULONG *FunctionID )
{
//get the function's address
PBYTE Function = (PBYTE)GetProcAddress( GetModuleHandle( "ntdll.dll" ), FuncName );
/*
do some sanity checks,
make sure this function
has a corresponding kernel
level function
*/
*FunctionID = 0;
//func not found...
if ( Function == NULL )
{
return FALSE;
}
/*
77F5B438 B8 00000000 MOV EAX, _FUNCTION_ID_
77F5B43D BA 0003FE7F MOV EDX,7FFE0300
77F5B442 FFD2 CALL EDX
77F5B444 C2 1800 RETN XX
*/
//mov eax
if ( *Function != 0xB8 )
{
return FALSE;
}
/*
since the address of
the function which
actually makes the call
(SYSCALL) may change, we just
check for mov edx
*/
if ( *(Function + 5) != 0xBA )
{
return FALSE;
}
//call edx
/*if ( *(PWORD)(Function + 10) != 0xD2FF )
{
return FALSE;
}
//retn
if ( *(Function + 12) != 0xC2 )
{
return FALSE;
}*/
*FunctionID = *(PDWORD)(Function + 1);
return TRUE;
}
int main(int argc, char* argv[])
{
ULONG Id;
printf( "function name: NtCreateProcessEx\n" );
GetId( "NtCreateProcessEx", &Id );
printf( "function id: %08X\n", Id );
return 0;
}
///////////////////////////////////////////////////////////////////////
这样也不爽,要从用户态传到驱动层不方便,最后,用这个代码:
#include "ntddk.h"
#include "stdarg.h"
#include "stdio.h"
#include "ntiologc.h"
#include "ntimage.h"
#define DWORD unsigned long
#define WORD unsigned short
#define BOOL unsigned long
#define BYTE unsigned char
#define SEC_IMAGE 0x01000000
typedef struct _SECTION_IMAGE_INFORMATION {
PVOID EntryPoint;
ULONG StackZeroBits;
ULONG StackReserved;
ULONG StackCommit;
ULONG ImageSubsystem;
WORD SubsystemVersionLow;
WORD SubsystemVersionHigh;
ULONG Unknown1;
ULONG ImageCharacteristics;
ULONG ImageMachineType;
ULONG Unknown2[3];
} SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION;
DWORD GetDllFunctionAddress(char* lpFunctionName, PUNICODE_STRING pDllName)
{
HANDLE hThread, hSection, hFile, hMod;
SECTION_IMAGE_INFORMATION sii;
IMAGE_DOS_HEADER* dosheader;
IMAGE_OPTIONAL_HEADER* opthdr;
IMAGE_EXPORT_DIRECTORY* pExportTable;
DWORD* arrayOfFunctionAddresses;
DWORD* arrayOfFunctionNames;
WORD* arrayOfFunctionOrdinals;
DWORD functionOrdinal;
DWORD Base, x, functionAddress;
char* functionName;
STRING ntFunctionName, ntFunctionNameSearch;
PVOID BaseAddress = NULL;
SIZE_T size=0;
OBJECT_ATTRIBUTES oa = {sizeof oa, 0, pDllName, OBJ_CASE_INSENSITIVE};
IO_STATUS_BLOCK iosb;
//_asm int 3;
ZwOpenFile(&hFile, FILE_EXECUTE | SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
oa.ObjectName = 0;
ZwCreateSection(&hSection, SECTION_ALL_ACCESS, &oa, 0,PAGE_EXECUTE, SEC_IMAGE, hFile);
ZwMapViewOfSection(hSection, NtCurrentProcess(), &BaseAddress, 0, 1000, 0, &size, (SECTION_INHERIT)1, MEM_TOP_DOWN, PAGE_READWRITE);
ZwClose(hFile);
hMod = BaseAddress;
dosheader = (IMAGE_DOS_HEADER *)hMod;
opthdr =(IMAGE_OPTIONAL_HEADER *) ((BYTE*)hMod+dosheader->e_lfanew+24);
pExportTable =(IMAGE_EXPORT_DIRECTORY*)((BYTE*) hMod + opthdr->DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT]. VirtualAddress);
// now we can get the exported functions, but note we convert from RVA to address
arrayOfFunctionAddresses = (DWORD*)( (BYTE*)hMod + pExportTable->AddressOfFunctions);
arrayOfFunctionNames = (DWORD*)( (BYTE*)hMod + pExportTable->AddressOfNames);
arrayOfFunctionOrdinals = (WORD*)( (BYTE*)hMod + pExportTable->AddressOfNameOrdinals);
Base = pExportTable->Base;
RtlInitString(&ntFunctionNameSearch, lpFunctionName);
for(x = 0; x < pExportTable->NumberOfFunctions; x++)
{
functionName = (char*)( (BYTE*)hMod + arrayOfFunctionNames[x]);
RtlInitString(&ntFunctionName, functionName);
functionOrdinal = arrayOfFunctionOrdinals[x] + Base - 1; // always need to add base, -1 as array counts from 0
// this is the funny bit. you would expect the function pointer to simply be arrayOfFunctionAddresses[x]...
// oh no... thats too simple. it is actually arrayOfFunctionAddresses[functionOrdinal]!!
functionAddress = (DWORD)( (BYTE*)hMod + arrayOfFunctionAddresses[functionOrdinal]);
if (RtlCompareString(&ntFunctionName, &ntFunctionNameSearch, TRUE) == 0)
{
ZwClose(hSection);
return functionAddress;
}
}
ZwClose(hSection);
return 0;
}
NTSTATUS
OnStubDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest (Irp,
IO_NO_INCREMENT
);
return Irp->IoStatus.Status;
}
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
DbgPrint("ROOTKIT: OnUnload called\n");
}
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )
{
int i;
UNICODE_STRING dllName;
DWORD functionAddress;
int position;
DbgPrint("My Driver Loaded!");
theDriverObject->DriverUnload = OnUnload;
RtlInitUnicodeString(&dllName, L"\\Device\\HarddiskVolume1\\Windows\\System32\\ntdll.dll");
functionAddress = GetDllFunctionAddress("ZwCreateProcessEx", &dllName);
position = *((WORD*)(functionAddress+1));
DbgPrint("Id:%d\n", position);
return STATUS_SUCCESS;
}
上面的代码从驱动层加载NTDLL,再从输出表中找出函数地址,mov eax,[ID]对应的b8后面的字就是索引号,其实跟前一个代码作用是相似的,
只是驱动层没有LoadLibrary,只能这样解决了。