天道酬勤 发表于 2024-12-30 21:15:29

纯驱动恢复SSDT

*SDT恢复是一个古老的技术了,这些天复习以前的知识,发现东西都忘了.正好找到了这份源码.
*于是开始边注释边复习起来. 现在终于又把RING3下恢复SSDT的东西温习了一下,感觉很充实.
*偶很菜,一开始看代码的时候,不知道如何下手,因为原作者的代码风格很乱,看得糊涂.于是偶
*按照 M$ 的代码风格重新注释了一遍,方便以后温习. 如果你还木有看过这份源码的话,在偶注释
*的基础上应该会理解的更快些了.
*
*搜索了下坛子,发现没有关于这份源码的相关细节.于是偶来补充下.
*存档的主要目的是方便以后学习这些知识的同学更快的找到资源,少走弯路. 老鸟飘过~
*
* Description:
*
*在RING3获得 \device\physicalmemory 的读写权限(前提必须是Administrator). 从磁盘读取
*ntoskrnl.exe,对齐后映射进内存(这需要我们自己来做),在其EAT中得到KeServiceDescriptorTable
*相对于ntoskrnl.exe的偏移.把系统高端加载的ntoskrnl.exe 的SSDT地址映射到进程的虚拟地址空间里
*推算出ServiseTable的地址后,把其所有内容映射到进程的虚拟地址空间里. 得到了ServiseTable的地址
*但它里面的函数是否被HOOK过,并不知道.所以需要参照我们自己映射的ntoskrnl.exe中的ServiseTable
*里函数的偏移来作比较.不相同,则被HOOK了,然后恢复之.
头文件


#ifndef GLOBAL_NATIVE_API_DEF_SUDAMI
#define GLOBAL_NATIVE_API_DEF_SUDAMI

#ifdef __cplusplus
extern "C" {
#endif

////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////

typedef long NTSTATUS, *PNTSTATUS;

typedef unsigned long DWORD;
typedef DWORD * PDWORD;
typedef unsigned long ULONG;
typedef unsigned long ULONG_PTR;
typedef ULONG *PULONG;
typedef unsigned short WORD;
typedef unsigned char BYTE;
typedef unsigned char UCHAR;
typedef unsigned short USHORT;
typedef void *PVOID;
typedef int BOOL;
typedef BYTE BOOLEAN;


#define NT_SUCCESS(Status)                          ((NTSTATUS)(Status) >= 0)
#define STATUS_SUCCESS                                        0x00000000
#define STATUS_UNSUCCESSFUL                                0xC0000001
#define STATUS_NOT_IMPLEMENTED          0xC0000002
#define STATUS_INFO_LENGTH_MISMATCH   0xC0000004
#define STATUS_INVALID_PARAMETER                0xC000000D
#define STATUS_ACCESS_DENIED                        0xC0000022
#define STATUS_BUFFER_TOO_SMALL                        0xC0000023
#define STATUS_IO_DEVICE_ERROR          ((NTSTATUS) 0xC0000185)
#define OBJ_KERNEL_HANDLE                                0x00000200

#ifndefLOWORD
#define LOWORD(l)         ((unsigned short)(unsigned int)(l))
#endif

#ifndef HIWORD
#define HIWORD(l)         ((unsigned short)((((unsigned int)(l)) >> 16) & 0xFFFF))
#endif

// 定义ioctl相关的,用于R3和R0间的通信
#ifndef MAKELONG
#define MAKELONG(a, b) ((LONG) (((WORD) (a)) | ((DWORD) ((WORD) (b))) << 16))
#endif

#define MY_DEVICE_TYPE               0x0000AA71   // 这地方可以自己改
#define DRIVER_IO(code)       CTL_CODE (MY_DEVICE_TYPE, code, METHOD_BUFFERED, FILE_ANY_ACCESS)

typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;


/////////////////////////////////////////////////////////////////         --          --   
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//   --   -      -   --
//+                                                         +//   --      -   -       --
//+                      结构体声明                           +//      --       -      --
//+                                                         +//       -   sudami   -   
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//      --            --   
/////////////////////////////////////////////////////////////////          --      --      
//                                                                           --    --      
//                                                                              --   

#ifndefANSI_STRING
typedef struct _STRING {
USHORTLength;
USHORTMaximumLength;
PCHARBuffer;
} ANSI_STRING, *PANSI_STRING;
#endif

#ifndefUNICODE_STRING
typedef struct _UNICODE_STRING {
USHORTLength;
USHORTMaximumLength;
PWSTRBuffer;
} UNICODE_STRING, *PUNICODE_STRING;
#endif

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

typedef struct ServiceDescriptorShadowEntry {
        unsigned int        *Win32kTableBase;
        unsigned int        *Win32kCounterTableBase;
        unsigned int        NumberofWin32kServices;
        unsigned char   *Win32kParamTableBase;
} ServiceDescriptorTableShadowEntry_t, *PServiceDescriptorTableShadowEntry_t;
#pragma pack()

__declspec(dllimport)ServiceDescriptorTableEntry_t    KeServiceDescriptorTable;
PServiceDescriptorTableShadowEntry_t                                        KeServiceDescriptorTableShadow;

// OBJECT_ATTRIBUTES
typedef struct _OBJECT_ATTRIBUTES {
        DWORD                         Length;
        HANDLE                        RootDirectory;
        PUNICODE_STRING               ObjectName;
        DWORD                         Attributes;
        PSECURITY_DESCRIPTOR          SecurityDescriptor;
        PSECURITY_QUALITY_OF_SERVICESecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;



// CLIENT_ID
#ifndefCLIENT_ID
typedef struct _CLIENT_ID {
        HANDLE   UniqueProcess;
        HANDLE   UniqueThread;
} CLIENT_ID, *PCLIENT_ID;
#endif

/*
struct _SYSTEM_THREADS
{
        LARGE_INTEGER                KernelTime;
        LARGE_INTEGER                UserTime;
        LARGE_INTEGER                CreateTime;
        ULONG                                WaitTime;
        PVOID                                StartAddress;
        CLIENT_ID                        ClientIs;
        KPRIORITY                        Priority;
        KPRIORITY                        BasePriority;
        ULONG                                ContextSwitchCount;
        ULONG                                ThreadState;
        KWAIT_REASON                WaitReason;
};

struct _SYSTEM_PROCESSES
{
        ULONG                                NextEntryDelta;
        ULONG                                ThreadCount;
        ULONG                                Reserved;
        LARGE_INTEGER                CreateTime;
        LARGE_INTEGER                UserTime;
        LARGE_INTEGER                KernelTime;
        UNICODE_STRING                ProcessName;
        KPRIORITY                        BasePriority;
        ULONG                                ProcessId;
        ULONG                                InheritedFromProcessId;
        ULONG                                HandleCount;
        ULONG                                Reserved2;
        VM_COUNTERS                        VmCounters;
        IO_COUNTERS                        IoCounters; //windows 2000 only
        struct _SYSTEM_THREADS                Threads;
};


// PROCESS_BASIC_INFORMATION
#ifdefPROCESS_BASIC_INFORMATION
#undefPROCESS_BASIC_INFORMATION
typedef struct _PROCESS_BASIC_INFORMATION {
        NTSTATUS                ExitStatus;
        ULONG                        PebBaseAddress;
        ULONG_PTR                AffinityMask;
        LONG                        BasePriority;
        ULONG_PTR                UniqueProcessId;
        ULONG_PTR                InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;
#endif


// SYSTEM_HANDLE_INFORMATION
typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO {
   USHORT UniqueProcessId;
   USHORT CreatorBackTraceIndex;
   UCHAR ObjectTypeIndex;
   UCHAR HandleAttributes;
   USHORT HandleValue;   // 句柄
   PVOID Object;         // 若HANDLE类型为线程,则它是ETHREAD结构
   ULONG GrantedAccess;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO;

typedef struct _SYSTEM_HANDLE_INFORMATION {
   ULONG NumberOfHandles;
   SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
*/

// SYSTEM_MODULE_INFORMATION
typedef struct _SYSTEM_MODULE_INFORMATION {
        ULONG        Reserved;
        PVOID        Base;
        ULONG        Size;
        ULONG        Flags;
        USHORTIndex;
        USHORTUnknown;
        USHORTLoadCount;
        USHORTModuleNameOffset;
        CHAR    ImageName;
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;


typedef struct {
    ULONG   dwNumberOfModules;
    SYSTEM_MODULE_INFORMATION   smi;
} MODULES, *PMODULES;


// SYSTEM_BASIC_INFORMATION
typedef struct _SYSTEM_BASIC_INFORMATION {
        ULONG Unknown;                  //Always contains zero
        ULONG MaximumIncrement;         //一个时钟的计量单位
        ULONG PhysicalPageSize;         //一个内存页的大小
        ULONG NumberOfPhysicalPages;    //系统管理着多少个页
        ULONG LowestPhysicalPage;       //低端内存页
        ULONG HighestPhysicalPage;      //高端内存页
        ULONG AllocationGranularity;
        ULONG LowestUserAddress;      //地端用户地址
        ULONG HighestUserAddress;       //高端用户地址
        ULONG ActiveProcessors;         //激活的处理器
        UCHAR NumberProcessors;         //有多少个处理器
} SYSTEM_BASIC_INFORMATION, *PSYSTEM_BASIC_INFORMATION;


// SYSTEM_INFORMATION_CLASS
typedef enum _SYSTEM_INFORMATION_CLASS {
    SystemBasicInformation,
    SystemProcessorInformation,
    SystemPerformanceInformation,
    SystemTimeOfDayInformation,
    SystemPathInformation,
    SystemProcessInformation,
    SystemCallCountInformation,
    SystemDeviceInformation,
    SystemProcessorPerformanceInformation,
    SystemFlagsInformation,
    SystemCallTimeInformation,
    SystemModuleInformation,// 11
    SystemLocksInformation,
    SystemStackTraceInformation,
    SystemPagedPoolInformation,
    SystemNonPagedPoolInformation,
    SystemHandleInformation,// 0x10 -- 16
    SystemObjectInformation,
    SystemPageFileInformation,
    SystemVdmInstemulInformation,
    SystemVdmBopInformation,
    SystemFileCacheInformation,
    SystemPoolTagInformation,
    SystemInterruptInformation,
    SystemDpcBehaviorInformation,
    SystemFullMemoryInformation,
    SystemLoadGdiDriverInformation,
    SystemUnloadGdiDriverInformation,
    SystemTimeAdjustmentInformation,
    SystemSummaryMemoryInformation,
    SystemUnused1,
    SystemPerformanceTraceInformation,
    SystemCrashDumpInformation,
    SystemExceptionInformation,
    SystemCrashDumpStateInformation,
    SystemKernelDebuggerInformation,
    SystemContextSwitchInformation,
    SystemRegistryQuotaInformation,
    SystemExtendServiceTableInformation,
    SystemPrioritySeperation,
    SystemUnused3,
    SystemUnused4,
    SystemUnused5,
    SystemUnused6,
    SystemCurrentTimeZoneInformation,
    SystemLookasideInformation,
    SystemTimeSlipNotification,
    SystemSessionCreate,
    SystemSessionDetach,
    SystemSessionInformation
} SYSTEM_INFORMATION_CLASS;


#ifndefSECTION_INHERIT
typedef enum _SECTION_INHERIT {
        ViewShare = 1,
        ViewUnmap = 2
} SECTION_INHERIT;
#endif



/******************************************************
*                                                   *
*                   部分 PE 结构                      *
*                                                   *
******************************************************/

#ifndef IMAGE_DOS_SIGNATURE
#define IMAGE_DOS_SIGNATURE 0x5a4d      // MZ
#endif

#ifndef IMAGE_NT_SIGNATURE
#define IMAGE_NT_SIGNATURE0x00004550// PE00
#endif

// _IMAGE_DOS_HEADER
struct MZHeader
{
        unsigned short signature; // "MZ"
        unsigned short partPag;
        unsigned short pageCnt;
        unsigned short reloCnt;
        unsigned short hdrSize;
        unsigned short minMem;
        unsigned short maxMem;
        unsigned short reloSS;
        unsigned short exeSP;
        unsigned short chksum;
        unsigned short exeIP;
        unsigned short reloCS;
        unsigned short tablOff;
        unsigned short overlay;
        unsigned char reserved;
        unsigned long offsetToPE;
};

// DWORD signature + _IMAGE_FILE_HEADER. 不是完全的IMAGE_FILE_HEADER.
struct PE_Header
{
        unsigned long signature; // "PE\0\0"
        unsigned short machine;
        unsigned short numSections;
        unsigned long timeDateStamp;
        unsigned long pointerToSymbolTable;
        unsigned long numOfSymbols;
        unsigned short sizeOfOptionHeader;
        unsigned short characteristics;
};

// _IMAGE_OPTIONAL_HEADER
struct PE_ExtHeader
{
        unsigned short magic;
        unsigned char majorLinkerVersion;
        unsigned char minorLinkerVersion;
        unsigned long sizeOfCode;
        unsigned long sizeOfInitializedData;
        unsigned long sizeOfUninitializedData;
        unsigned long addressOfEntryPoint;
        unsigned long baseOfCode;
        unsigned long baseOfData;
        unsigned long imageBase;
        unsigned long sectionAlignment;
        unsigned long fileAlignment;
        unsigned short majorOSVersion;
        unsigned short minorOSVersion;
        unsigned short majorImageVersion;
        unsigned short minorImageVersion;
        unsigned short majorSubsystemVersion;
        unsigned short minorSubsystemVersion;
        unsigned long reserved1;
        unsigned long sizeOfImage;
        unsigned long sizeOfHeaders;
        unsigned long checksum;
        unsigned short subsystem;
        unsigned short DLLCharacteristics;
        unsigned long sizeOfStackReserve;
        unsigned long sizeOfStackCommit;
        unsigned long sizeOfHeapReserve;
        unsigned long sizeOfHeapCommit;
        unsigned long loaderFlags;
        unsigned long numberOfRVAAndSizes;
        unsigned long exportTableAddress;
        unsigned long exportTableSize;
        unsigned long importTableAddress;
        unsigned long importTableSize;
        unsigned long resourceTableAddress;
        unsigned long resourceTableSize;
        unsigned long exceptionTableAddress;
        unsigned long exceptionTableSize;
        unsigned long certFilePointer;
        unsigned long certTableSize;
        unsigned long relocationTableAddress;
        unsigned long relocationTableSize;
        unsigned long debugDataAddress;
        unsigned long debugDataSize;
        unsigned long archDataAddress;
        unsigned long archDataSize;
        unsigned long globalPtrAddress;
        unsigned long globalPtrSize;
        unsigned long TLSTableAddress;
        unsigned long TLSTableSize;
        unsigned long loadConfigTableAddress;
        unsigned long loadConfigTableSize;
        unsigned long boundImportTableAddress;
        unsigned long boundImportTableSize;
        unsigned long importAddressTableAddress;
        unsigned long importAddressTableSize;
        unsigned long delayImportDescAddress;
        unsigned long delayImportDescSize;
        unsigned long COMHeaderAddress;
        unsigned long COMHeaderSize;
        unsigned long reserved2;
        unsigned long reserved3;
};

// _IMAGE_SECTION_HEADER
struct SectionHeader
{
        unsigned char sectionName;
        unsigned long virtualSize;
        unsigned long virtualAddress;
        unsigned long sizeOfRawData;
        unsigned long pointerToRawData;
        unsigned long pointerToRelocations;
        unsigned long pointerToLineNumbers;
        unsigned short numberOfRelocations;
        unsigned short numberOfLineNumbers;
        unsigned long characteristics;
};

struct ImportDirEntry
{
        DWORD importLookupTable;
        DWORD timeDateStamp;
        DWORD fowarderChain;
        DWORD nameRVA;
        DWORD importAddressTable;
};


/******************************************************
*                                                   *
*               结构体自定义区域                  *
*                                                   *
******************************************************/


typedef struct _MY_PROCESS_INFO {
        ULONG   PID;
        ULONG          KPEB;
        ULONG   CR3;
        CHAR      Name;
        ULONG   Reserved;
} MY_PROCESS_INFO, *PMY_PROCESS_INFO;



/////////////////////////////////////////////////////////////////         --          --   
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//   --   -      -   --
//+                                                         +//   --      -   -       --
//+                      函数原型声明                         +//      --       -      --
//+                                                         +//       -   sudami   -   
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//      --            --   
/////////////////////////////////////////////////////////////////          --      --      
//                                                                           --    --      
//                                                                              --

NTSYSAPI
NTSTATUS
NTAPI
NtCreateFile(
OUT PHANDLE             FileHandle,
IN ACCESS_MASK          DesiredAccess,
IN POBJECT_ATTRIBUTES   ObjectAttributes,
//OUT PIO_STATUS_BLOCK    IoStatusBlock,
IN PLARGE_INTEGER       AllocationSize OPTIONAL,
IN ULONG                FileAttributes,
IN ULONG                ShareAccess,
IN ULONG                CreateDisposition,
IN ULONG                CreateOptions,
IN PVOID                EaBuffer OPTIONAL,
IN ULONG                EaLength
);


NTSYSAPI
VOID
NTAPI
RtlInitUnicodeString(
   PUNICODE_STRING DestinationString,
   PCWSTR SourceString
   );

NTSYSAPI
NTSTATUS
NTAPI
ZwOpenSection(
   OUT PHANDLE SectionHandle,
   IN ACCESS_MASK DesiredAccess,
   IN POBJECT_ATTRIBUTES objectAttributes
   );


NTSYSAPI
NTSTATUS
NTAPI
ZwMapViewOfSection(
   IN HANDLE SectionHandle,
   IN HANDLE ProcessHandle,
   IN OUT PVOID *BaseAddress,
   IN ULONG ZeroBits,
   IN ULONG CommitSize,
   IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
   IN OUT PULONG ViewSize,
   IN SECTION_INHERIT InheritDisposition,
   IN ULONG AllocationType,
   IN ULONG Protect
   );


NTSYSAPI
NTSTATUS
NTAPI
ZwUnmapViewOfSection(
   IN HANDLE ProcessHandle,
   IN PVOID BaseAddress
   );

/*
NTSYSAPI
NTSTATUS
NTAPI
NtVdmControl(
   IN ULONG ControlCode,
   IN PVOID ControlData
   );

NTSYSAPI
NTSTATUS
PsLookupProcessByProcessId (
    IN HANDLE ProcessId,
    OUT PEPROCESS *Process
    );

NTSYSAPI
NTSTATUS
PsLookupThreadByThreadId (
    IN HANDLE ThreadId,
    OUT PETHREAD *Thread
    );
*/


NTSYSAPI
NTSTATUS
NTAPI
NtQuerySystemInformation(

IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
OUT PVOID               SystemInformation,
IN ULONG                SystemInformationLength,
OUT PULONG            ReturnLength OPTIONAL
);




//
//
//
//

VOID
RtlFreeUnicodeString(
    IN PUNICODE_STRINGUnicodeString
    );

NTSTATUS (WINAPI * _RtlAnsiStringToUnicodeString)
        (PUNICODE_STRINGDestinationString,
       IN PANSI_STRINGSourceString,
       IN BOOLEAN);

VOID (WINAPI *_RtlInitAnsiString)
        (IN OUT PANSI_STRINGDestinationString,
       IN PCHARSourceString);

/////////////////////////////////////////////////////////////////         --          --   
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//   --   -      -   --
//+                                                         +//   --      -   -       --
//+                  一些自定义必备函数                     +//      --       -      --
//+                                                         +//       -   sudami   -   
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//      --            --   
/////////////////////////////////////////////////////////////////          --      --      
//                                                                           --    --      
//                                                                              --


// 写保护的开&关
void WPOFF()
{
        __asm {   //去掉内存保护
                cli
                moveax,cr0
                andeax,not 10000h
                movcr0,eax
        }
}

void WPON()
{
        __asm {   //恢复内存保护
                moveax,cr0
                or   eax,10000h
                movcr0,eax
                sti
        }
}

//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////

#ifdef __cplusplus
}
#endif

#endif
.c文件



#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <aclapi.h>
#include "rootkit.h"

////////////////////////////////////////////////////////////////////

#pragma comment (lib, "F:\\WINDDK\\lib\\wxp\\i386\\ntdll.lib")

////////////////////////////////////////////////////////////////////

#define OBJ_CASE_INSENSITIVE                        0x00000040L
#define PAGE_READONLY                                        0x02
#define PAGE_READWRITE                                        0x04
#define DEF_KERNEL_BASE                                        0x80400000L
#define PROT_MEMBASE                                        0x80000000


DWORD gWinVersion;

struct FixupBlock
{
        ULONG pageRVA;
        ULONG blockSize;
};

/*++

3个函数,就不用注解了

--*/
BOOL GetNativeAPIs ()
{
        HMODULE hntdll;

        hntdll = GetModuleHandle("ntdll.dll");
                       
        *(FARPROC *)&_RtlAnsiStringToUnicodeString =
                        GetProcAddress(hntdll, "RtlAnsiStringToUnicodeString");

        *(FARPROC *)&_RtlInitAnsiString =
                        GetProcAddress(hntdll, "RtlInitAnsiString");

        if (_RtlAnsiStringToUnicodeString && _RtlInitAnsiString) {
                return TRUE;
        }

        return FALSE;
}

DWORD myStrlenA (char *ptr)
{
        DWORD len = 0;
        while(*ptr) {
                len++;
                ptr++;
        }

        return len;
}

BOOL myStrcmpA (char *str1, char *str2)
{
        while(*str1 && *str2) {
                if(*str1 == *str2) {
                        str1++;
                        str2++;
                } else {
                        return FALSE;
                }
        }

        if (*str1 && !*str2) {
                return FALSE;
        } else if (*str2 && !*str1){
                return FALSE;
        }

        return TRUE;       
}


//----------------------                         ----------------------                  
//                      -------------------------                      ---------------------
BOOL
ReadPEInfo (
char *modulePos,
MZHeader *outMZ,
PE_Header *outPE,
PE_ExtHeader *outpeXH,
SectionHeader **outSecHdr
)

/*++

学习者 : sudami
时间   : 08/01/11

功能   :
得到映射到内存的PE结构中各部分的地址

参数   :

modulePos - 将磁盘上的文件读出来后,映射到进程的虚拟地址空间中,对齐后得到其起始地址.
outMZ - 保存MZ头 IMAGE_DOS_HEADER
outPE - 保存PE头 IMAGE_FILE_HEADER
outpeXH - 保存扩展PE头 IMAGE_OPTIONAL_HEADER
outSecHdr - 2级指针,保存Section头 IMAGE_SECTION_HEADER

返回 :

TRUE -读取成功
FALSE - 不是有效的PE文件,读取失败

--*/
{
        MZHeader *mzH = (MZHeader *)modulePos;

        if (mzH->signature != IMAGE_DOS_SIGNATURE) {
                printf("File does not have MZ header\n");
                return false;
        }

        PE_Header *peH = (PE_Header *)(modulePos + mzH->offsetToPE);

        // 通过 sizeOfOptionHeader 验证PE文件的合法性(它是 _IMAGE_OPTIONAL_HEADER 的尺寸)
        if (peH->sizeOfOptionHeader != sizeof (PE_ExtHeader)) {
                printf("Unexpected option header size.\n");
                return false;
        }

        PE_ExtHeader *peXH = (PE_ExtHeader *)((char *)peH + sizeof(PE_Header));
        SectionHeader *secHdr = (SectionHeader *)((char *)peXH + sizeof(PE_ExtHeader));

        *outMZ      =*mzH;
        *outPE      =*peH;
        *outpeXH    =*peXH;
        *outSecHdr=secHdr;

        return true;
}

//----------------------                         ----------------------                  
//                      -------------------------                      ---------------------
int
CalcTotalImageSize (
MZHeader *inMZ,
PE_Header *inPE,
PE_ExtHeader *inpeXH,
SectionHeader *inSecHdr
)

/*++

学习者 : sudami
时间   : 08/01/11

功能 :
计算PE文件加载到内存所需要占用的空间: 所有的文件头长度 + 所有的节长度

返回 :
PE在内存中对齐后需要的大小

--*/
{
        int result = 0;
        int val    = 0;

        /* (1) 统计所有文件头在内存中对齐后所需空间 */
        // sectionAlignment 为段加载后在内存中的对齐方式 4KB
        int alignment = inpeXH->sectionAlignment;

        // 如果所有的文件头长度之和 是4KB整数倍。则留给它的空间就是sizeOfHeaders
        if (inpeXH->sizeOfHeaders % alignment == 0) {

                result += inpeXH->sizeOfHeaders;
        } else { // 如果所有的文件头长度之和 非4KB整数倍。则需要对齐后申请空间

          val = inpeXH->sizeOfHeaders / alignment;
                val++;
                result += (val * alignment);
        }

        /* (2) 统计所有节在内存中对齐后所需空间 */
        for (int i = 0; i < inPE->numSections; i++) {

                // virtualSize 是 块对齐前的真实长度
                if (inSecHdr.virtualSize) {
                        if (inSecHdr.virtualSize % alignment == 0) {
                                result += inSecHdr.virtualSize;
                        } else {
                                val = inSecHdr.virtualSize / alignment;
                                val++;
                                result += (val * alignment);
                        }
                }
        }

        return result;
}

//----------------------                         ----------------------                  
//                      -------------------------                      ---------------------
ULONG
GetAlignedSize (
ULONG curSize,
ULONG alignment
)

/*++

功能 :
得到对齐后的文件大小

参数 :
curSize - 未对齐前的大小
alignment - 段加载后在内存中的对齐方式,一般为4KB

返回 :
对齐后的大小

--*/
{       
        if (curSize % alignment == 0) { // 如果不存在对齐方式,则不对齐,直接返回原大小
                return curSize;
        } else {
                int val = curSize / alignment;
                val++;
                return (val * alignment);
        }
}

//----------------------                         ----------------------                  
//                      -------------------------                      ---------------------
BOOL
LoadPE (
char *exePtr,
MZHeader *inMZ,
PE_Header *inPE,
PE_ExtHeader *inpeXH,
SectionHeader *inSecHdr,
LPVOID ptrLoc
)

/*++

学习者 : sudami
时间   : 08/01/11

功能 :
加载从磁盘读取的文件到内存,需要自己对齐

         <PE加载到内存对齐后的示意图>
          所有文件头      节1      节2   ...   节N
       ------------------------------------------------
        |aa...aa######| a...a##| a...a###| ... | a...a##|
       ------------------------------------------------
注: a 表示实际数据; # 表示空隙,是内存中对齐后留出的空隙

参数 :
exePtr - PE文件在磁盘中时起始的地址.还没有经过对齐.所以需要把它映射到内存
...
ptrLoc - 根据内存中PE的大小调用HeapAlloc而分配的一块空闲内存区域.

返回 : TRUE

--*/
{
        char *outPtr = (char *)ptrLoc;

        // 把所有文件头的数据全部拷贝到ptrLoc指向的地址处
        // 然后地址按照PE在内存中的对齐方式往后挪
        memcpy (outPtr, exePtr, inpeXH->sizeOfHeaders);
        outPtr += GetAlignedSize (inpeXH->sizeOfHeaders, inpeXH->sectionAlignment);

        // 拷贝所有的节数据到新的地址处. 复制的时候要知道每个节在文件中的实际大小.而不是在内存在对齐后的大小
        for (int i = 0; i < inPE->numSections; i++) {

                if (inSecHdr.sizeOfRawData > 0) { // sizeOfRawData 是节对齐后的长度
                        ULONG toRead = inSecHdr.sizeOfRawData;

                        if (toRead > inSecHdr.virtualSize) {
                                toRead = inSecHdr.virtualSize;
                        }

                        // 拷贝没个节的实际数据,然后把指针挪到每个节在内存中对齐后的位置处
                        memcpy (outPtr, exePtr + inSecHdr.pointerToRawData, toRead);

                        outPtr += GetAlignedSize (inSecHdr.virtualSize, inpeXH->sectionAlignment);
                }
        }

        return true;
}

//----------------------                         ----------------------                  
//                      -------------------------                      ---------------------
LPVOID
LoadDLL (
char *dllName
)

/*++

学习者 : sudami
时间   : 08/01/11

功能 :
将指定模块从磁盘读取,然后映射进内存,按照对齐的方式映射.这需要我们自己来做.
这个过程是调用 自定义函数 LoadPE

流程 :
CreateFile 打开指定的文件 --> GetFileInformationByHandle 获得文件的大小信息a -->
HeapAlloc 分配大小为a的内存 --> ReadFile 将文件读入此内存区域中 --> ReadPEInfo 获得
PE文件各结构地址 --> CalcTotalImageSize 计算此文件映射到内存对齐后的总大小b-->
HeapAlloc 分配大小为b的内存 --> LoadPE加载之

参数 :
exePtr - 指定的模块名称,不包含路径.所以一般是系统目录下的文件


返回 : 完成加载后,对齐后的PE在进程空间中一段内存的起始地址

--*/
{
        MZHeader mzH2;
        PE_Header peH2;
        PE_ExtHeader peXH2;
        SectionHeader *secHdr2;
        char moduleFilename;
        LPVOID ptrLoc = NULL;

        GetSystemDirectory (moduleFilename, MAX_PATH);
        if (moduleFilename != '\\') {
                strcat (moduleFilename, "\\");
        }

        if ((myStrlenA (moduleFilename) + myStrlenA (dllName)) >= MAX_PATH)
                return NULL;

        strcat (moduleFilename, dllName);

    // 调用CreateFile打开要映射的文件
        HANDLE fp = CreateFile (moduleFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
       
        if (fp != INVALID_HANDLE_VALUE) {

                // 通过打开的文件句柄得到文件的信息: 主要是文件的大小
                BY_HANDLE_FILE_INFORMATIONfileInfo;
                GetFileInformationByHandle (fp, &fileInfo);
                DWORD fileSize = fileInfo.nFileSizeLow;
               
                // 得到了磁盘文件的大小后,为其分配内存
                if (fileSize) {
                        // 在当前进程的Heap中为其分配一段空间
                        LPVOID exePtr = HeapAlloc (GetProcessHeap(), 0, fileSize);

                        if (exePtr) {
                                DWORD read;

                                // read = 保存的是实际读入的大小. 如果完全读完了.就计算PE在内存中对齐后的大小.
                                if ( ReadFile (fp, exePtr, fileSize, &read, NULL) && read == fileSize) {                                       
                                        if ( ReadPEInfo ((char *)exePtr, &mzH2, &peH2, &peXH2, &secHdr2)) {
                                                int imageSize = CalcTotalImageSize(&mzH2, &peH2, &peXH2, secHdr2);                                       
                                                ptrLoc = HeapAlloc(GetProcessHeap(), 0, imageSize);

                                                if (ptrLoc) {       
                                                        LoadPE ((char *)exePtr, &mzH2, &peH2, &peXH2, secHdr2, ptrLoc);
                                                }
                                        }

                                }
                        }

                        HeapFree (GetProcessHeap(), 0, exePtr);
                }

                CloseHandle (fp);
        }

        return ptrLoc;
}


//----------------------                         ----------------------                  
//                      -------------------------                      ---------------------
DWORD
ProcAPIExportAddr (
DWORD hModule,
char *apiName
)

/*++

学习者 : sudami
时间   : 08/01/11

功能 :
得到指定模块EAT中的指定函数相对于ntoskrnl.exe的偏移

参数 :
hModule - 模块地址

apiName - 指定函数名

返回 :

0 - 如果传进来的参数不正确,则返回0
# - 指定模块EAT中的指定函数相对于ntoskrnl.exe的偏移

--*/
{       
        if (!hModule || !apiName) {
                return 0;
        }

        char *ptr = (char *)hModule;
        ptr += 0x3c;
       
        ptr = (char *)(*(DWORD *)ptr) + hModule + 0x78; // 此时ptr保存的是EAT的偏移
        ptr = (char *)(*(DWORD *)ptr) + hModule;                // 此时ptr指向EAT

        DWORD numEntries = *(DWORD *)(ptr + 24);      // 24-->0x18 NumberOfNames
        DWORD *ExportNamePointerTable = (DWORD *)(*(DWORD *)(ptr + 32) + hModule); // 32-->0x20AddressOfNames       
        DWORD ordinalBase = *((DWORD *)(ptr + 16));         // 16-->0x10Base
        WORD *ExportOrdinalTable = (WORD *)((*(DWORD *)(ptr + 36)) + hModule);           // 36-->0x24AddressOfNameOrdinals
        DWORD *ExportAddrTable = (DWORD *)((*(DWORD *)(ptr + 28)) + hModule);      // 28-->0x1CAddressOfFunctions

        // 遍历每个有名字的函数 (有的函数没有名字)
        for(DWORD i = 0; i < numEntries; i++)
        {
                // 得到函数的名字
                char *exportName = (char *)(ExportNamePointerTable + hModule);

                if (myStrcmpA (exportName, apiName) == TRUE) {               
                        // 得到函数在AddressOfFunctions中的索引号
                        WORD ordinal = ExportOrdinalTable;

                        return (DWORD)(ExportAddrTable);
                }               
        }

        return 0;
}


//----------------------                         ----------------------                  
//                      -------------------------                      ---------------------
BOOL
BuildNativeAPITable (
DWORD hModule,
char *nativeAPINames[],
DWORD numNames
)

/*++

学习者 : sudami
时间   : 08/01/11

功能 :
从ntdll.dll的EAT中搜索,建立一张 API 函数表

参数 :
hModule - 模块地址

nativeAPINames[] - 保存函数名的数组

numNames -

返回 : TRUE | FALSE

--*/
{
        if(!hModule) {
                return FALSE;
        }

        char *ptr = (char *)hModule;
        ptr += 0x3c;
       
        ptr = (char *)(*(DWORD *)ptr) + hModule + 0x78; // 此时ptr保存的是EAT的偏移

        ptr = (char *)(*(DWORD *)ptr) + hModule;                // 此时ptr指向EAT

        DWORD numEntries = *(DWORD *)(ptr + 24);      // 24-->0x18 NumberOfNames
        DWORD *ExportNamePointerTable = (DWORD *)(*(DWORD *)(ptr + 32) + hModule); // 32-->0x20AddressOfNames       
        DWORD ordinalBase = *((DWORD *)(ptr + 16));                                    // 16-->0x10Base
        WORD *ExportOrdinalTable = (WORD *)((*(DWORD *)(ptr + 36)) + hModule);           // 36-->0x24AddressOfNameOrdinals
        DWORD *ExportAddrTable = (DWORD *)((*(DWORD *)(ptr + 28)) + hModule);      // 28-->0x1CAddressOfFunctions


        for (DWORD i = 0; i < numEntries; i++)
        {               
                // i now contains the index of the API in the Ordinal Table
                // ptr points to Export directory table

                WORD ordinalValue = ExportOrdinalTable;               
                DWORD apiAddr = (DWORD)ExportAddrTable + hModule;
                char *exportName = (char *)(ExportNamePointerTable + hModule);
               
                // Win2K
                if (gWinVersion == 0 &&
                   *((UCHAR *)apiAddr) == 0xB8 &&
                   *((UCHAR *)apiAddr + 9) == 0xCD &&
                   *((UCHAR *)apiAddr + 10) == 0x2E) {

                        DWORD serviceNum = *(DWORD *)((char *)apiAddr + 1);

                        if(serviceNum < numNames){
                                nativeAPINames = exportName;
                        }
                }
               
                // WinXP
                else if (gWinVersion == 1 &&
                                *((UCHAR *)apiAddr) == 0xB8 &&
                                *((UCHAR *)apiAddr + 5) == 0xBA &&
                                *((UCHAR *)apiAddr + 6) == 0x00 &&
                                *((UCHAR *)apiAddr + 7) == 0x03 &&
                                *((UCHAR *)apiAddr + 8) == 0xFE &&
                                *((UCHAR *)apiAddr + 9) == 0x7F) {

                        DWORD serviceNum = *(DWORD *)((char *)apiAddr + 1);

                        if(serviceNum < numNames) {
                                nativeAPINames = exportName;
                        }
                }
        }

        return TRUE;
}




//----------------------                         ----------------------                  
//                      -------------------------                      ---------------------
HANDLE
OpenPhyMem(
                   )

/*++

学习者 : sudami
时间   : 08/01/11

功能 :
从用户态获得 \device\physicalmemory 的句柄

参数 : NULL

返回 : \device\physicalmemory 的句柄

--*/
{
        HANDLE hPhyMem;
        OBJECT_ATTRIBUTES oAttr;
        ANSI_STRING aStr;
               
        _RtlInitAnsiString (&aStr, "\\device\\physicalmemory");
                                               
        UNICODE_STRING uStr;

        if (_RtlAnsiStringToUnicodeString (&uStr, &aStr, TRUE) != STATUS_SUCCESS){               
                return INVALID_HANDLE_VALUE;       
        }

    oAttr.Length                       =sizeof(OBJECT_ATTRIBUTES);
    oAttr.RootDirectory               =NULL;
    oAttr.Attributes               =OBJ_CASE_INSENSITIVE;
    oAttr.ObjectName               =&uStr;
    oAttr.SecurityDescriptor =NULL;
    oAttr.SecurityQualityOfService = NULL;

        if (ZwOpenSection (&hPhyMem, SECTION_MAP_READ | SECTION_MAP_WRITE, &oAttr ) != STATUS_SUCCESS) {               
                return INVALID_HANDLE_VALUE;
        }

        return hPhyMem;
}

//----------------------                         ----------------------                  
//                      -------------------------                      ---------------------
BOOL
MapPhyMem (
HANDLE hPhyMem,
DWORD *phyAddr,
DWORD *length,
PVOID *virtualAddr
)

/*++

学习者 : sudami
时间   : 08/01/11

功能 :
从用户态获得 \device\physicalmemory 的句柄
映射系统地址空间内的一部分数据到用户的地址空间.前提当然是用户对这段空间有读写权限
映射结束后 phyAddr 的值发生了变化,它挪到了映射结束的地方

参数 :
hPhyMem - \device\physicalmemory 的句柄
phyAddr - 从系统地址范围内的指定位置处开始映射
length- 指定映射的大小
virtualAddr - 进程的虚拟地址空间中保存映射的起始地址

返回 :

--*/
{
        NTSTATUS                        ntStatus;
        PHYSICAL_ADDRESS        viewBase;

        *virtualAddr = 0;
        viewBase.QuadPart = (ULONGLONG) (*phyAddr);

        ntStatus = ZwMapViewOfSection (
                hPhyMem,
                (HANDLE)-1,
                virtualAddr, // 指定接收映射的起始地址
                0,
                *length,   // 指定映射的大小
                &viewBase,   // 指定从哪儿开始映射
                length,      // 保存实际的映射大小
                ViewShare,   // 指定继承性
                0,
                PAGE_READWRITE
                );

        if (ntStatus != STATUS_SUCCESS) {
                printf("Failed to map physical memory view of length %X at %X!", *length, *phyAddr);
                return FALSE;                                       
        }

        *phyAddr = viewBase.LowPart;
        return TRUE;
}


// Unmap section of physical memory
void UnMapPhyMem(DWORD virtualAddr)
{
        NTSTATUS status;

        status = ZwUnmapViewOfSection ((HANDLE)-1, (PVOID)virtualAddr);
        if (status != STATUS_SUCCESS) {
                printf("Unmapping view failed!\n");
        }
}

//----------------------                         ----------------------                  
//                      -------------------------                      ---------------------
BOOL
AssignACL (
                   )

/*++

学习者 : sudami
时间   : 08/01/11

功能 :
使当前的用户对 \device\physicalmemory 有写的权限
btw: 此部分需要参考MSDN,里面的数据结构太多.

参数 :NULL

返回 : TRUE | FALSE

--*/
{
        HANDLE             hPhyMem;
        ANSI_STRING      aStr;
        UNICODE_STRING   uStr;
        OBJECT_ATTRIBUTESoAttr;
        BOOL               result = FALSE;
               
        _RtlInitAnsiString(&aStr, "\\device\\physicalmemory");

        if (_RtlAnsiStringToUnicodeString (&uStr, &aStr, TRUE) != STATUS_SUCCESS) {               
                return FALSE;
        }

    oAttr.Length                                   = sizeof(OBJECT_ATTRIBUTES);
    oAttr.RootDirectory                           = NULL;
    oAttr.Attributes                           = OBJ_CASE_INSENSITIVE;
    oAttr.ObjectName                           = &uStr;
    oAttr.SecurityDescriptor           = NULL;
    oAttr.SecurityQualityOfService = NULL;

        if (ZwOpenSection (&hPhyMem, READ_CONTROL | WRITE_DAC, &oAttr ) != STATUS_SUCCESS) {               
                return FALSE;
        } else {
                PACL dacl;
                PSECURITY_DESCRIPTORsd;

                if ( GetSecurityInfo (hPhyMem, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL,
                                                &dacl, NULL, &sd) == ERROR_SUCCESS) {

                        EXPLICIT_ACCESS ea;

                        // 得到当前进程的用户名
                        char userName;
                        DWORD userNameSize = MAX_PATH-1;
                        GetUserName (userName, &userNameSize);

                        // 设置精确的权限
                        ea.grfAccessPermissions                = SECTION_MAP_WRITE;
                        ea.grfAccessMode                        = GRANT_ACCESS;
                        ea.grfInheritance                        = NO_INHERITANCE;
                        ea.Trustee.pMultipleTrustee = NULL;
                        ea.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
                        ea.Trustee.TrusteeForm                = TRUSTEE_IS_NAME;
                        ea.Trustee.TrusteeType                = TRUSTEE_IS_USER;
                        ea.Trustee.ptstrName                = userName;

                        // 建立新的ACL
                        PACL newDacl;
                        if ( SetEntriesInAcl(1, &ea, dacl, &newDacl) == ERROR_SUCCESS)        {
                                // 用新的DACL重新设置当前对象的安全权限
                                if( SetSecurityInfo (hPhyMem, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL,
                                                                newDacl, NULL) == ERROR_SUCCESS) {               
                                        result = TRUE;
                                }

                                LocalFree(newDacl);
                        }
                }
        }

        return result;       
}

//----------------------                         ----------------------                  
//                      -------------------------                      ---------------------
DWORD
GetKernelBase (
                           void
                           )

/*++

学习者 : sudami
时间   : 08/01/11

功能 :
为NtQuerySystemInformation传递11号参数,得到SYSTEM_MODULE_INFORMATION信息,遍历之,得到ntoskrnl.exe的基址

参数 :NULL

返回 : TRUE | FALSE

--*/
{
        HANDLE hHeap = GetProcessHeap();
       
        NTSTATUS Status;
    ULONG cbBuffer = 0x8000;
    PVOID pBuffer = NULL;
        DWORD retVal = DEF_KERNEL_BASE; // 0x80400000

        // 这样申请内存 - cbBuffer *= 2;,不错不错.学习~
    do {
                pBuffer = HeapAlloc (hHeap, 0, cbBuffer);
                if (pBuffer == NULL) {
                        return DEF_KERNEL_BASE;
                }

                Status = NtQuerySystemInformation (SystemModuleInformation,        pBuffer, cbBuffer, NULL);

                if (Status == STATUS_INFO_LENGTH_MISMATCH) {
                        HeapFree (hHeap, 0, pBuffer);
                        cbBuffer *= 2;
                } else if (Status != STATUS_SUCCESS) {
                        HeapFree(hHeap, 0, pBuffer);
                        return DEF_KERNEL_BASE;
                }
    }
    while (Status == STATUS_INFO_LENGTH_MISMATCH);

        // 其实没必要这样写哦. 得到系统模块的地址后,第一个就是ntoskrnl.exe的了
        DWORD numEntries = *((DWORD *)pBuffer);
        SYSTEM_MODULE_INFORMATION *smi = (SYSTEM_MODULE_INFORMATION *)((char *)pBuffer + sizeof(DWORD));

        for (DWORD i = 0; i < numEntries; i++) {
                if (strcmpi(smi->ImageName, "ntoskrnl.exe")) {
                        retVal = (DWORD)(smi->Base);
                        break;
                }

                smi++;
        }

        HeapFree(hHeap, 0, pBuffer);

        return retVal;
}

//----------------------                         ----------------------                  
//                      -------------------------                      ---------------------
// Thanks to 90210 for this excellent way of getting the KiServiceTable address from the disk image of
// ntoskrnl.exe
// http://www.rootkit.com/newsread.php?newsid=176
DWORD
getKiServiceTableAddr (
PVOID exeAddr,
DWORD sdtAddr,
PE_ExtHeader *peXH2
)

/*--
学习者 :sudami
时间   :08/01/11

参数   :
exeAddr - 从磁盘中读取指定的文件.在进程的heap当中分配一段空间,按照PE在内存中的对齐方式
            将文件映射到内存.得到的这个地址(这里用的是PVOID exeAddr = LoadDLL("\\ntoskrnl.exe"))

sdtAddr - sdt在ntoskrnl.exe中的偏移量

peXH2 - IMAGE_OPTIONAL_HEADER

返回:TRUE | FALSE

功能    :得到真实的SDT地址
         btw: 此方法得到的不一定准确.系统出问题,后果自负~

--*/
{
        // 如果重定位表存在
        if ( peXH2->relocationTableAddress && peXH2->relocationTableSize ) {
                //
                // fixBlk保存的是当前数据块BLOCK的起始地址
                // fixBlk+1 所指向的内容便是这个数据块BLOCK的 VirtualAddress。它指向第一个数据包
                //
                FixupBlock *fixBlk = (FixupBlock *)((char *)exeAddr + peXH2->relocationTableAddress);               

                // 其实是一个BLOCK数组。当fixBlk->blockSize不存在时,数组就遍历完了
                while(fixBlk->blockSize) {
                        // 一个BLOCK数组中数据包的个数 = (一个数据块的大小 - 8) / 2
                        int numEntries = (fixBlk->blockSize - sizeof(FixupBlock)) >> 1;
       
                        //
                        // offsetPtr指向的是这个BLOCK数组中第一个数据包开始处的地址,然后依次+1。
                        // 直到把一个BLOCK遍历完。对每个数据包,判断里面的Type是否为3。如果是,把地址
                        // 转化为重定位后的地址
                        //
                        unsigned short *offsetPtr = (unsigned short *)(fixBlk + 1);

                        for(int i = 0; i < numEntries; i++)        {               
                                // 得到每个数据包的重定位类型
                                // *offsetPtr & 0xF000 得到的正是Type,右移12位得到它的大小
                                int relocType = (*offsetPtr & 0xF000) >> 12;
                               
                                if(relocType == 3) { // 如果是 IMAGE_REL_BASED_HIGHLOW (3)       

                                        // 计算代码的重定位地址
                                        // *offsetPtr & 0x0FFF 保存的是偏移量
                                        DWORD sudami= (DWORD) (fixBlk->pageRVA + (*offsetPtr & 0x0FFF));
                                        DWORD *codeLoc = (DWORD *)((char *)exeAddr + sudami);                                       

                                        // *codeLoc 保存的是重定位后sdt在内存中的地址
                                        if (*codeLoc == sdtAddr + peXH2->imageBase &&
                                                *(WORD *)((DWORD)codeLoc - 2) == 0x05c7) {

                                                DWORD kiServiceTableAddr = *(DWORD *)((DWORD)codeLoc + 4);
                                               
                                                // checks for presence of found address in the relocation table
                                                if( sudami+ peXH2->imageBase == kiServiceTableAddr) {
                                                        return sudami ;
                                                }
                                        }                                               
                                }

                                offsetPtr++;
                        }

                        fixBlk = (FixupBlock *)offsetPtr;
                }
        }
        return 0;
}

//
// 主函数入口
//
int main (int argc, char* argv[])
{
        MZHeader mzH2;
        PE_Header peH2;
        PE_ExtHeader peXH2;
        SectionHeader *secHdr2;

        ::system ("title = SDTrestore V 0.2 08/01/12");
        printf("Author : SIG^2 G-TEC (www.security.org.sg)\n");
        printf("Learner: sudami \n\n");

        // 得到系统的版本信息
        OSVERSIONINFO ov;
        ov.dwOSVersionInfoSize = sizeof(ov);
        GetVersionEx (&ov);

        if (ov.dwMajorVersion != 5) {
                printf("Sorry, this version supports only Win2K and WinXP.\n");
                return 1;
        }

        if (ov.dwMinorVersion != 0 && ov.dwMinorVersion != 1) {
                printf("Sorry, this version supports only Win2K and WinXP.\n");
                return 1;
        }

        gWinVersion = ov.dwMinorVersion;

        if (!GetNativeAPIs()) {
                printf("Failed to get addresses of Native APIs!\n");
                return 1;
        }

        // 提升权限。使user能读写内存
        AssignACL();
        HANDLE hPhyMem = OpenPhyMem();

        if ( hPhyMem == INVALID_HANDLE_VALUE) {
                AssignACL();
        }

        hPhyMem = OpenPhyMem();
        if (hPhyMem == INVALID_HANDLE_VALUE) {
                        printf("Could not open physical memory device!\n \
                                   Make sure you are running as Administrator.\n");
                        return 1;
        }

        // 映射 ntoskrnl.exe 到用户的进程空间中。对齐
        PVOID exeAddr = LoadDLL("ntoskrnl.exe");
        if (!exeAddr){
                printf("Failed to load ntoskrnl.exe!\n");
                return 1;
        }

        // 在其的EAT中得到SDT相对于ntoskrnl.exe的偏移
        DWORD sdtAddr = ProcAPIExportAddr ((DWORD)exeAddr, "KeServiceDescriptorTable");
        if (!sdtAddr) {
                printf("Failed to get address of KeServiceDescriptorTable!\n");
                return 1;
        }
       
        // 得到其映射后各部分的地址
        if (!ReadPEInfo((char *)exeAddr, &mzH2, &peH2, &peXH2, &secHdr2)) {
                printf("Failed to get PE header of ntoskrnl.exe!\n");
                return 1;
        }

        // kernelPhyBase 为系统加载ntoskrnl.exe后,相对于0x80000000的偏移(对齐了的)
        DWORD kernelPhyBase = GetKernelBase() - PROT_MEMBASE; //#define PROT_MEMBASE 0x80000000

        // kernelOffset 即是RVA
        DWORD kernelOffset = kernelPhyBase - peXH2.imageBase;
        printf ("KeServiceDescriptorTable\t\t%X\n", sdtAddr + GetKernelBase());

        /**********************************************************************************

        此时已经得到SSDT相对于ntoskrnl.exe的偏移了,保存于sdtAddr.采用普通的方法-->

        从磁盘读ntoskrnl.exe,计算对齐后的大小.将其对齐后映射到用户进程空间.在其EAT中得到
        SSDT相对于ntoskrnl.exe的偏移

        ***********************************************************************************/
        UCHAR *ptr = NULL;
        DWORD pAddr = sdtAddr + kernelPhyBase;
        DWORD wantedAddr = pAddr;
        DWORD len = 0x2000;

        // 第一次映射
        // 把系统加载的ntoskrnl.exe的SSDT地址映射到进程的虚拟地址空间里面去
        if (MapPhyMem(hPhyMem, &pAddr, &len, (LPVOID *)&ptr)) {
               
                DWORD serviceTableAddr, sdtCount;

                // 现在start保存的是 已经映射的内容的大小
                // 其实是KeServiceDecriptorTable.base从开始到结束的这段大小
                DWORD start      = wantedAddr - pAddr;

                // 本来要映射len = 0x2000大小的,但实际上只映射了start大小
                // wantedBytes 是没有按期望映射的差值
                DWORD wantedBytes= len - start;
/*--
typedef struct _KSERVICE_TABLE_DESCRIPTOR {
    PULONG_PTR Base;
    PULONG Count;
    ULONG Limit;
    PUCHAR Number;
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;
++*/

                if (wantedBytes >= 4) {

                        serviceTableAddr = *((DWORD *)(&ptr));
                        printf("KeServiceDecriptorTable.ServiceTable\t%X\n", serviceTableAddr);

                        if (wantedBytes >= 12) {

                                // &ptr 是指针,此时指向Base结束的地方,Base中保存了很长一段的数据
                                // ((DWORD *)(&ptr)) + 2也是指针,指向KeServiceDescriptorTable.ServiceLimit
                                sdtCount = *(((DWORD *)(&ptr)) + 2);
                                printf("KeServiceDescriptorTable.ServiceLimit\t%d\n", sdtCount);
                        }
                } else {
                        printf("Sorry, an unexpected situation occurred!\n");
                        return 1;
                }

                UnMapPhyMem ((DWORD)ptr);

                printf("\n");

                if (sdtCount >= 300)        {
                        printf("Sorry, an unexpected error occurred! SDT Count > 300???\n");
                        return 1;
                }

                pAddr = serviceTableAddr - PROT_MEMBASE; // 0x80000000
                wantedAddr = pAddr;
                ptr = NULL;
                len = 0x2000;

                // 第2次映射
                // 把系统加载的ntoskrnl.exe的KeServiceDecriptorTable.base中的所有内容映射到进程的虚拟地址空间里面去
                if (MapPhyMem(hPhyMem, &pAddr, &len, (LPVOID *)&ptr)) {

                        //
                        start = wantedAddr - pAddr;
                        DWORD numEntries = (len - start) >> 2;

                        if (numEntries >= sdtCount) {

                                // 为ST表分配名字所占用的空间
                                char **nativeApiNames = NULL;
                                nativeApiNames = (char **)malloc(sizeof(char *) * sdtCount);

                                if(!nativeApiNames) {
                                        printf("Failed to allocate memory for Native API name table.\n");
                                        return 1;
                                }

                                memset (nativeApiNames, 0, sizeof(char *) * sdtCount);

                                // 映射ntdll.dll到内存,对齐
                                PVOID ntdll = LoadDLL("\\ntdll.dll");

                                if(!ntdll) {
                                        printf("Failed to load ntdll.dll!\n");
                                        return 1;
                                }

                                // 建立API名称表表
                                BuildNativeAPITable ((DWORD)ntdll, nativeApiNames, sdtCount);

                                // *serviceTable 是系统地址空间内的ST表的地址.它是否被HOOK过还不晓得.
                                // 所以要拿它和我们自己从文件中读到的偏移相比较
                                DWORD *serviceTable = (DWORD *)(&ptr);

                                /***********************************
                                *                                  *
                *    这里得到了真实的ST地址啦      *
                                *                                  *
                                ***********************************/
                                // wantedAddr = serviceTableAddr - 0x80000000;
                    // wantedAddr - kernelOffset. 即是ST表中系统范围内的地址 - RVA = ST在文件中的偏移
                                // *fileServiceTable 保存的是真实的ST表中每个函数的偏移
                                DWORD *fileServiceTable = (DWORD *)((DWORD)exeAddr + wantedAddr - kernelOffset - peXH2.imageBase);
                               
                                // calculate address based on 90210's suggestion
                                DWORD fileAddr2 = (DWORD)exeAddr + getKiServiceTableAddr(exeAddr, sdtAddr, &peXH2);                               
                               
                          // start...
                                if (!IsBadReadPtr(fileServiceTable, sizeof(DWORD)) &&
                                   !IsBadReadPtr(&fileServiceTable, sizeof(DWORD)))        {

                                        DWORD hookCount = 0;
                                        for (DWORD i = 0; i < sdtCount; i++) {       

                                                // 这里就是真正判断是否被HOOK 的地方
                                                if ( (serviceTable - PROT_MEMBASE - kernelOffset) != fileServiceTable) {
                                                        printf("%-25s %3X ----\n",
                                                                  (nativeApiNames ? nativeApiNames : "Unknown API"),
                                                                  i, serviceTable);
                                                        hookCount++;
                                                }
                                               
                                        }

                                        printf("\nNumber of Service Table entries hooked = %u\n", hookCount);
                                       
                                        if (hookCount) {
                                                printf("\nFix SDT Entries (Y/N)? : ");
       
                                                char inputReply;
                                                memset(inputReply, 0, sizeof(inputReply));
                                                fgets(inputReply, sizeof(inputReply) - 1, stdin);
                                                printf("\n");
                                                                               
                                                // 恢复被HOOK的SDT
                                                if (stricmp(inputReply, "y\n") == 0) {

                                                        ::system ("cls");
                                                        for(DWORD i = 0; i < sdtCount; i++)        {

                                                                if((serviceTable - PROT_MEMBASE - kernelOffset) != fileServiceTable) {
                                                                       
                                                                        // 就这一句就搞定啦~
                                                                        serviceTable = fileServiceTable + PROT_MEMBASE + kernelOffset;

                                                                        printf("[+] Patched SDT entry %.2X to %.8X\n", i,
                                                                                fileServiceTable + PROT_MEMBASE + kernelOffset);
                                                                }
                                                        }

                                                        printf("\n\n[+] SDT Recover is done,but if there is a KTIMER,the SDT may be re-HOOKED again\n");
                                                        ::getchar ();
                                                } else {
                                                        printf("[-] SDT Entries NOT fixed.\n");
                                                        ::getchar ();
                                                }
                                        }
                                } else {
                                        printf("It's likely that the SDT service table has been relocated.\n"
                                                   "This POC code cannot support patching of relocated SDT service table.\n");
                                        ::getchar ();
                                }

                        }

                        UnMapPhyMem((DWORD)ptr);
                }
        }

        return 0;
}

//////////////////////////////////// END OF FILE /////////////////////////////////////////////

页: [1]
查看完整版本: 纯驱动恢复SSDT