在Windows 2003中HOOK ZwCreateProcessEx

作者: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,只能这样解决了。
原文地址:https://www.cnblogs.com/Safe3/p/1334828.html