2015年3月17日星期二

获取32位系统上内核没有导出的SSDT函数的地址‏

#include <ntifs.h>
#include <windef.h>
#include <Aux_klib.h> //source里面要有TARGETLIBS=$(DDK_LIB_PATH)\Aux_klib.lib


/*
功能:获取内核中没有导出的SSDT函数的地址/ID。
思路:因为在NTDLL.DLL里必定有,而且必定是对应的关系。
      而且NTDLL.DLL的地址在内核中是可以查询到的。
      导出的函数是肯定能查询到的。
      ID是根据一个宏获取的。
      相应的套入SSDT即可获取那个函数的地址。注意是NT开头的。

注意:试验证明ZwCreateMutant,ZwOpenMutant,NtCreateMutant,NtOpenMutant在内核中都没有导出,用MmGetSystemRoutineAddress是获取不到地址的。

本文测试环境是32位系统,64位系统没有测试,可能需要稍微修改代码。

made by correy
made at 2015.03.16
*/


#define TAG  'tset' //test


#pragma pack(1)
typedef struct ServiceDescriptorEntry{
    unsigned int *ServiceTableBase;
    unsigned int *ServiceCounterTableBase; //Used only in checked build
    unsigned int NumberOfServices;
    unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
#pragma pack()

extern __declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable;

#define SYSCALL_INDEX(_Function) (*(PULONG)((PUCHAR)_Function+1)) //获取SSDT函数的ID,X64没有测试和观察。

#define SYSTEMSERVICE(index) KeServiceDescriptorTable.ServiceTableBase[index]



DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{

}


PVOID RtlImageDirectoryEntryToData (IN PVOID Base, IN BOOLEAN MappedAsImage, IN USHORT DirectoryEntry, OUT PULONG Size);
/*++
Routine Description:
    This function locates a Directory Entry within the image header and returns either the virtual address or seek address of the data the Directory describes.
Arguments:
    Base - Supplies the base of the image or data file.
    MappedAsImage - FALSE if the file is mapped as a data file.
                  - TRUE if the file is mapped as an image.
    DirectoryEntry - Supplies the directory entry to locate.
    Size - Return the size of the directory.
Return Value:
    NULL - The file does not contain data for the specified directory entry.
    NON-NULL - Returns the address of the raw data the directory describes.
--*/
//{
//    PIMAGE_NT_HEADERS NtHeaders;
//
//    if (LDR_IS_DATAFILE(Base)) {
//        Base = LDR_DATAFILE_TO_VIEW(Base);
//        MappedAsImage = FALSE;
//    }
//
//    NtHeaders = RtlImageNtHeader(Base);
//
//    if (!NtHeaders)
//        return NULL;
//
//    if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//        return (RtlpImageDirectoryEntryToData32(Base, MappedAsImage, DirectoryEntry, Size, (PIMAGE_NT_HEADERS32)NtHeaders));
//    } else if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
//        return (RtlpImageDirectoryEntryToData64(Base, MappedAsImage, DirectoryEntry, Size, (PIMAGE_NT_HEADERS64)NtHeaders));
//    } else {
//        return (NULL);
//    }
//}


PVOID MiFindExportedRoutineByName (IN PVOID DllBase, IN PANSI_STRING AnsiImageRoutineName)
/*++
Routine Description:
    This function searches the argument module looking for the requested exported function name.
Arguments:
    DllBase - Supplies the base address of the requested module.
    AnsiImageRoutineName - Supplies the ANSI routine name being searched for.
Return Value:
    The virtual address of the requested routine or NULL if not found.
--*/
{
    USHORT OrdinalNumber;
    PULONG NameTableBase;
    PUSHORT NameOrdinalTableBase;
    PULONG Addr;
    LONG High;
    LONG Low;
    LONG Middle;
    LONG Result;
    ULONG ExportSize;
    PVOID FunctionAddress = 0;
    PIMAGE_EXPORT_DIRECTORY ExportDirectory;

    PAGED_CODE();

    __try
    {
        FunctionAddress = *(PVOID *)DllBase;
        FunctionAddress = 0;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        return FunctionAddress;
    }

    //确保DllBase可以访问。否则蓝屏。
    ExportDirectory = (PIMAGE_EXPORT_DIRECTORY) RtlImageDirectoryEntryToData (DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ExportSize);
    if (ExportDirectory == NULL) {
        return NULL;
    }
   
    NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);// Initialize the pointer to the array of RVA-based ansi export strings.  
    NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);// Initialize the pointer to the array of USHORT ordinal numbers.  
    Low = 0;
    Middle = 0;
    High = ExportDirectory->NumberOfNames - 1;

    while (High >= Low) // Lookup the desired name in the name table using a binary search.
    {      
        Middle = (Low + High) >> 1;// Compute the next probe index and compare the import name with the export name entry.
        Result = strcmp (AnsiImageRoutineName->Buffer, (PCHAR)DllBase + NameTableBase[Middle]);
        if (Result < 0) {
            High = Middle - 1;
        } else if (Result > 0) {
            Low = Middle + 1;
        } else {
            break;
        }
    }

    // If the high index is less than the low index, then a matching table entry was not found.
    // Otherwise, get the ordinal number from the ordinal table.
    if (High < Low) {
        return NULL;
    }

    OrdinalNumber = NameOrdinalTableBase[Middle];

    // If the OrdinalNumber is not within the Export Address Table,then this image does not implement the function.
    // Return not found.
    if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions) {
        return NULL;
    }

    // Index into the array of RVA export addresses by ordinal number.
    Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
    FunctionAddress = (PVOID)((PCHAR)DllBase + Addr[OrdinalNumber]);

    // Forwarders are not used by the kernel and HAL to each other.
    ASSERT ((FunctionAddress <= (PVOID)ExportDirectory) || (FunctionAddress >= (PVOID)((PCHAR)ExportDirectory + ExportSize)));

    return FunctionAddress;
}


PVOID get_module_image_base(__in UCHAR * name)
{
    NTSTATUS status = 0;
    ULONG  modulesSize;
    AUX_MODULE_EXTENDED_INFO * modules;
    ULONG  numberOfModules;
    ULONG i;
    PIMAGE_EXPORT_DIRECTORY pied = 0;
    PVOID ImageBase = 0;

    status = AuxKlibInitialize();
    if (!NT_SUCCESS( status ))
    {
        KdPrint(( "AuxKlibInitialize fail %d\n", status));
        return ImageBase;
    }  
   
    status = AuxKlibQueryModuleInformation(&modulesSize, sizeof(AUX_MODULE_EXTENDED_INFO), NULL);// Get the required array size.
    if (!NT_SUCCESS(status) || modulesSize == 0) {
        return ImageBase;
    }
   
    numberOfModules = modulesSize / sizeof(AUX_MODULE_EXTENDED_INFO);// Calculate the number of modules.
       
    modules = (AUX_MODULE_EXTENDED_INFO*) ExAllocatePoolWithTag(PagedPool, modulesSize, TAG);// Allocate memory to receive data.
    if (modules == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        return ImageBase;
    }
    RtlZeroMemory(modules, modulesSize);
   
    status = AuxKlibQueryModuleInformation(&modulesSize, sizeof(AUX_MODULE_EXTENDED_INFO), modules);// Obtain the module information.
    if (!NT_SUCCESS(status)) {
        ExFreePoolWithTag(modules,TAG);
        return ImageBase;
    }

    for (i = 0;i<numberOfModules;i++)
    {
        UCHAR * pfilename = modules[i].FullPathName + modules[i].FileNameOffset;

        if (_stricmp(pfilename, name) == 0)
        {
            ImageBase = modules[i].BasicInfo.ImageBase;
            break;
        }
    }

    ExFreePoolWithTag(modules,TAG);

    return ImageBase;
}


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    PVOID ImageBase = 0;
    PVOID FunctionAddress;
    //ANSI_STRING EngCreateBitmap  = RTL_CONSTANT_STRING("EngCreateBitmap");//NT开头的函数竟然一个也没有导出。
    ANSI_STRING test  = RTL_CONSTANT_STRING("NtCreateMutant");
    int index = 0;
    PVOID p_NtCreateMutant = 0;

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;  

    //ImageBase = get_module_image_base("WIN32k.sys");//注意这个的环境:有的线程不可以访问。不然地址不可访问。
    ImageBase = get_module_image_base("ntdll.dll");
    if (ImageBase == 0)
    {
        return status;
    }

    FunctionAddress = MiFindExportedRoutineByName (ImageBase, &test);
    if (FunctionAddress == 0)
    {
        return status;
    }

    index = SYSCALL_INDEX(FunctionAddress);
    KdPrint(( "NtCreateMutant ID: %d\n", index));

    p_NtCreateMutant = (PVOID)SYSTEMSERVICE(index);
    KdPrint(( "NtCreateMutant: %p\n", p_NtCreateMutant));

    return status;  
}


/*
结果如下:
0: kd> g
Mon Mar 16 18:16:28.216 2015 (UTC + 8:00): NtCreateMutant ID: 43
Mon Mar 16 18:16:28.216 2015 (UTC + 8:00):
Mon Mar 16 18:16:28.220 2015 (UTC + 8:00): NtCreateMutant: 80618822
分析如下:
0: kd> u nt!NtCreateMutant
nt!NtCreateMutant:
80618822 6a2c            push    2Ch
80618824 6890e44d80      push    offset nt!ExpLuidIncrement+0x108 (804de490)
80618829 e81245f2ff      call    nt!_SEH_prolog (8053cd40)
8061882e 33db            xor     ebx,ebx
80618830 895dfc          mov     dword ptr [ebp-4],ebx
80618833 64a124010000    mov     eax,dword ptr fs:[00000124h]
80618839 8945d0          mov     dword ptr [ebp-30h],eax
8061883c 8a8040010000    mov     al,byte ptr [eax+140h]
看来获取的地址是对的。
0: kd> u nt!ZwCreateMutant
nt!ZwCreateMutant:
805010e4 b82b000000      mov     eax,2Bh
805010e9 8d542404        lea     edx,[esp+4]
805010ed 9c              pushfd
805010ee 6a08            push    8
805010f0 e81c150400      call    nt!KiSystemService (80542611)
805010f5 c21000          ret     10h
nt!ZwCreateNamedPipeFile:
805010f8 b82c000000      mov     eax,2Ch
805010fd 8d542404        lea     edx,[esp+4]
0: kd> ?2b
Evaluate expression: 43 = 0000002b
看来获取的ID是正确的。
0: kd> !process 0 0 explorer.exe
PROCESS 81b49020  SessionId: 0  Cid: 0694    Peb: 7ffdb000  ParentCid: 0654
    DirBase: 02940200  ObjectTable: e1d1b3b8  HandleCount: 523.
    Image: explorer.exe

PROCESS 82054620  SessionId: 0  Cid: 059c    Peb: 7ffd6000  ParentCid: 0694
    DirBase: 029402e0  ObjectTable: e1604900  HandleCount: 312.
    Image: explorer.exe

0: kd> .process /r /p 82054620
Implicit process is now 82054620
.cache forcedecodeuser done
Loading User Symbols
................................................................
.......................
0: kd> u ntdll!ZwCreateMutant
ntdll!NtCreateMutant:
7c92d10e b82b000000      mov     eax,2Bh
7c92d113 ba0003fe7f      mov     edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c92d118 ff12            call    dword ptr [edx]
7c92d11a c21000          ret     10h
7c92d11d 90              nop
ntdll!ZwCreateNamedPipeFile:
7c92d11e b82c000000      mov     eax,2Ch
7c92d123 ba0003fe7f      mov     edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c92d128 ff12            call    dword ptr [edx]
0: kd> u ntdll!NtCreateMutant
ntdll!NtCreateMutant:
7c92d10e b82b000000      mov     eax,2Bh
7c92d113 ba0003fe7f      mov     edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c92d118 ff12            call    dword ptr [edx]
7c92d11a c21000          ret     10h
7c92d11d 90              nop
ntdll!ZwCreateNamedPipeFile:
7c92d11e b82c000000      mov     eax,2Ch
7c92d123 ba0003fe7f      mov     edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c92d128 ff12            call    dword ptr [edx]
*/


/*
1: kd> uf nt!Zwcreatefile
Flow analysis was incomplete, some code may be missing
nt!ZwCreateFile:
fffff800`018d8680 488bc4          mov     rax,rsp
fffff800`018d8683 fa              cli
fffff800`018d8684 4883ec10        sub     rsp,10h
fffff800`018d8688 50              push    rax
fffff800`018d8689 9c              pushfq
fffff800`018d868a 6a10            push    10h
fffff800`018d868c 488d05dd270000  lea     rax,[nt!KiServiceLinkage (fffff800`018dae70)]
fffff800`018d8693 50              push    rax
fffff800`018d8694 b852000000      mov     eax,52h 注意这个。
fffff800`018d8699 e9225f0000      jmp     nt!KiServiceInternal (fffff800`018de5c0)

nt!KiServiceInternal:
fffff800`018de5c0 4883ec08        sub     rsp,8
fffff800`018de5c4 55              push    rbp
fffff800`018de5c5 4881ec58010000  sub     rsp,158h
fffff800`018de5cc 488dac2480000000 lea     rbp,[rsp+80h]
fffff800`018de5d4 48899dc0000000  mov     qword ptr [rbp+0C0h],rbx
fffff800`018de5db 4889bdc8000000  mov     qword ptr [rbp+0C8h],rdi
fffff800`018de5e2 4889b5d0000000  mov     qword ptr [rbp+0D0h],rsi
fffff800`018de5e9 fb              sti
fffff800`018de5ea 65488b1c2588010000 mov   rbx,qword ptr gs:[188h]
fffff800`018de5f3 0f0d8bd8010000  prefetchw [rbx+1D8h]
fffff800`018de5fa 0fb6bbf6010000  movzx   edi,byte ptr [rbx+1F6h]
fffff800`018de601 40887da8        mov     byte ptr [rbp-58h],dil
fffff800`018de605 c683f601000000  mov     byte ptr [rbx+1F6h],0
fffff800`018de60c 4c8b93d8010000  mov     r10,qword ptr [rbx+1D8h]
fffff800`018de613 4c8995b8000000  mov     qword ptr [rbp+0B8h],r10
fffff800`018de61a 4c8d1d3d010000  lea     r11,[nt!KiSystemServiceStart (fffff800`018de75e)]
fffff800`018de621 41ffe3          jmp     r11
1: kd> !process 0 0 explorer.exe
PROCESS fffffa801ad91910
    SessionId: 1  Cid: 06a4    Peb: 7fffffdd000  ParentCid: 0680
    DirBase: 66ef5000  ObjectTable: fffff8a0011323b0  HandleCount: 755.
    Image: explorer.exe

1: kd> .process /p /r fffffa801ad91910
Implicit process is now fffffa80`1ad91910
.cache forcedecodeuser done
Loading User Symbols
................................................................
................................................................
...........
1: kd> uf ntdll!NtCreateFile
ntdll!ZwCreateFile:
00000000`77051860 4c8bd1          mov     r10,rcx
00000000`77051863 b852000000      mov     eax,52h 注意这个
00000000`77051868 0f05            syscall
00000000`7705186a c3              ret
知道64位SSDT的结构及地址,根据上面的示例不难写出X64的相应的例子。

*/

没有评论:

发表评论