纯驱动恢复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]