A forum for reverse engineering, OS internals and malware analysis 

Forum for discussion about kernel-mode development.
 #8636  by _Lynn
 Mon Sep 19, 2011 3:38 pm
Hi there, I am a little new to kernel mode and to further my learning I would like to locate and read the SSDT. I have no interest in writing to it, just reading and parsing the function pointers. Mostly to locate functions that are not exported by ntoskrnl, such as ZwReadVirtualMemory.

First off, I am using Windows 7 x64. I noticed the KeServiceDescriptor table is not exported by my ntoskrnl build. I have read in a few places that I can obtain the linear address to this structure within the KTHREAD struct (TEB) which is defined here http://www.nirsoft.net/kernel_struct/vista/KTHREAD.html as PVOID ServiceTable.

Is that true? or is there another way I need to locate this structure?

Thanks :roll:
 #8641  by Vrtule
 Mon Sep 19, 2011 6:54 pm
Hello,

64bit versions of Windows do not store pointer to SSDT in ETHREAD structures. AFAIK the only way how to find the table is to disassemble the main kernel image (ntoskrnl.exe), locate references to the KeServiceDescriptorTableShadow symbol and perform pattern-matching.

Additionally, SSDT does not store direct function pointers on x64. They're offsets relative to the start of nt!KiServiceTable array (this is the first "pointer" table stored in SSDT). So, if you want to compute address of a function with index X, you must do something like this:
Code: Select all
Address = &KiServiceTable + (LONG32)KiServiceTable[X] / 16
(the offset also caches argument count in its four lowest bits).
 #8642  by _Lynn
 Mon Sep 19, 2011 8:32 pm
Alright thanks, ill look into that. Also, what is a good way to get the kernel base address? Currently I am just playing in a virtual machine, and I use EnumDeviceDrivers from user mode. What is a feasible method I can use from my driver?

Thanks again
 #8645  by _Lynn
 Tue Sep 20, 2011 2:39 am
Alright I spent some time studying, for future reference, here is the information I dug up.

I used ZwQuerySystemInformation to get the kernel base address, I had a problem with NTSTATUS telling me the buffer was not large enough but after looking over some memory dumps it looks like they tacked on 2 more 8 byte ULONG's at the beginning of the structure, I just labeled them as unknowns.

As for the shadowtable, that is not referenced at all in my ntoskrnl build, but I have been able to locate the beginning of the structure by doing a linear search on this signature..

8B F8 C1 EF 07 83 E7 20 25 FF 0F 00 00

now to find out how the call numbers from ntdll line up with the actual system calls :)

thanks for the help :D
 #8660  by Cr4sh
 Tue Sep 20, 2011 4:54 pm
My procedure, that locates nt!KeServiceDescriptorTable on x64, by disassembling a code of exported nt!KeAddSystemServiceTable().
Code: Select all
PVOID GetKeSDT(void)
{
    PVOID Ret = NULL;

#ifdef _X86_

    PVOID KernelBase = KernelGetModuleBase("ntoskrnl.exe");
    if (KernelBase)
    {
        ULONG KeSDT_RVA = KernelGetExportAddress(KernelBase, "KeServiceDescriptorTable");
        if (KeSDT_RVA > 0)
        {
            Ret = RVATOVA(KernelBase, KeSDT_RVA);
        }
        else
        {
            DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: Symbol nt!KeServiceDescriptorTable is not found\n");
        }
    }
    else
    {
        DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: Unable to locate kernel base\n");
    }

#elif _AMD64_

    #define MAX_INST_LEN 24

    PVOID KernelBase = KernelGetModuleBase("ntoskrnl.exe");
    if (KernelBase)
    {
        ULONG Func_RVA = KernelGetExportAddress(KernelBase, "KeAddSystemServiceTable");
        if (Func_RVA > 0)
        {
            // initialize disassembler engine
            ud_t ud_obj;
            ud_init(&ud_obj);

            UCHAR ud_mode = 64;

            // set mode, syntax and vendor
            ud_set_mode(&ud_obj, ud_mode);
            ud_set_syntax(&ud_obj, UD_SYN_INTEL);
            ud_set_vendor(&ud_obj, UD_VENDOR_INTEL);

            for (ULONG i = 0; i < 0x40;)
            {
                PUCHAR Inst = (PUCHAR)RVATOVA(KernelBase, Func_RVA + i);
                if (!MmIsAddressValid(Inst))
                {
                    DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: Invalid memory at "IFMT"\n", Inst);
                    break;
                }
                            
                ud_set_input_buffer(&ud_obj, Inst, MAX_INST_LEN);

                // get length of the instruction
                ULONG InstLen = ud_disassemble(&ud_obj);
                if (InstLen == 0)
                {
                    // error while disassembling instruction
                    DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: Can't disassemble instruction at "IFMT"\n", Inst);
                    break;
                }

                /*
                    Check for the following code

                    nt!KeAddSystemServiceTable:
                    fffff800`012471c0 448b542428         mov     r10d,dword ptr [rsp+28h]
                    fffff800`012471c5 4183fa01           cmp     r10d,1
                    fffff800`012471c9 0f871ab70c00       ja      nt!KeAddSystemServiceTable+0x78
                    fffff800`012471cf 498bc2             mov     rax,r10
                    fffff800`012471d2 4c8d1d278edbff     lea     r11,0xfffff800`01000000
                    fffff800`012471d9 48c1e005           shl     rax,5
                    fffff800`012471dd 4a83bc1880bb170000 cmp     qword ptr [rax+r11+17BB80h],0
                    fffff800`012471e6 0f85fdb60c00       jne     nt!KeAddSystemServiceTable+0x78
                */

                if ((*(PULONG)Inst & 0x00ffffff) == 0x1d8d4c &&
                    (*(PUSHORT)(Inst + 0x0b) == 0x834b || *(PUSHORT)(Inst + 0x0b) == 0x834a))
                {
                    // clculate nt!KeServiceDescriptorTableAddress
                    LARGE_INTEGER Addr;
                    Addr.QuadPart = (ULONGLONG)Inst + InstLen;
                    Addr.LowPart += *(PULONG)(Inst + 0x03) + *(PULONG)(Inst + 0x0f);

                    Ret = (PVOID)Addr.QuadPart;

                    break;
                }

                i += InstLen;
            }
        }
        else
        {
            DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: Symbol nt!KeServiceDescriptorTable is not found\n");
        }
    }
    else
    {
        DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: Unable to locate kernel base\n");
    }

#endif

    if (Ret)
    {
        DbgMsg(__FILE__, __LINE__, __FUNCTION__"(): nt!KeServiceDescriptorTable is at "IFMT"\n", Ret);
    }

    return Ret;
}
 #8672  by _Lynn
 Wed Sep 21, 2011 2:54 pm
Got another random question here, Ill try to keep it to one thread.

In my driver I have defined a few typedef'd function pointers. If I take for example an exported function from ntoskrnl, subtract it by the base to get an offset, then add that offset to the dynamic base that I obtain with ZwQuerySystemInformation, is there some reason that calling it through a pointer like that would cause me a bsod? Is there something I am missing or is this not allowed?
 #8673  by r2nwcnydc
 Wed Sep 21, 2011 3:14 pm
Which functions are you trying to call? What are the bug check codes? Can you post some of your source?

If you are calling a function that takes a pointer as one of its parameters and you are passing in a kernel address, it may have to do with the previous mode not being set properly.

http://www.kernelmode.info/forum/viewto ... =14&t=1147
 #8683  by _Lynn
 Wed Sep 21, 2011 6:23 pm
r2nwcnydc wrote:Which functions are you trying to call? What are the bug check codes? Can you post some of your source?

If you are calling a function that takes a pointer as one of its parameters and you are passing in a kernel address, it may have to do with the previous mode not being set properly.

http://www.kernelmode.info/forum/viewto ... =14&t=1147

Hey thanks for the response. I was just attempting to call ZwQuerySystemInformation through a pointer, I know I can call it with the definition in ntddk, but I was just checking to see how it works out with a function pointer, for when I manage to sort out the SSDT. I can't post my code till I get home but when I do, ill post it here.

I also tried with ZwClose but also got a bsod, for my typedef I just did this -

typedef NTSTATUS(__stdcall *ZwClosePtr)(HANDLE h);

ZwClosePtr ZwClose=(ZwClosePtr) ComputedOffset;

Not sure if the standard calling convention applies in kernel mode.. ;p Im just a nub.
 #8685  by EP_X0FF
 Wed Sep 21, 2011 6:32 pm
And what is the address value you trying to call ComputedOffset?
Attach BSOD minidump.
 #8686  by _Lynn
 Wed Sep 21, 2011 6:44 pm
Computed offset is the address I get from dumping the ntoskrnl EAT subtracted by default image base. I then add that offset to the kernel base I obtain in my driver, which I check to make sure is correct with DbgPrint.

Ill post the minidump when I get home. Thanks for the help.