检测并恢复 ssdt

//*********************************************************************************************
// SDTrestore (Proof-of-Concept)
// Version 0.2
// by SIG^2 G-TEC Lab
//
// Coded by Chew Keong TAN
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// “Software”), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, provided that the above
// copyright notice(s) and this permission notice appear in all copies of
// the Software and that both the above copyright notice(s) and this
// permission notice appear in supporting documentation.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
// OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
// INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
// FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
// NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
// WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// This program needs to access \device\physicalmemory, so you must be
// running as Administrator when using this.
//
// CHANGES
// ——-
// Version 0.2
// Thanks to 90210 for the suggestion of a more stable way to find the
// address of KiServiceTable from the disk image of ntoskrnl.exe
// http://www.rootkit.com/newsread.php?newsid=176
//
//*********************************************************************************************

#include
#include
#include
#include

#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
#define OBJ_CASE_INSENSITIVE 0x00000040L
#define PAGE_READONLY 0x02
#define PAGE_READWRITE 0x04
#define DEF_KERNEL_BASE 0x80400000L
#define SystemModuleInformation 11
#define PROT_MEMBASE 0x80000000

typedef LONG NTSTATUS;
typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;

DWORD gWinVersion;

typedef struct _STRING {
USHORT Length;
USHORT MaximumLength;
PCHAR Buffer;
} ANSI_STRING, *PANSI_STRING;

typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;

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

typedef struct _SYSTEM_MODULE_INFORMATION
{
ULONG Reserved[2];
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT Index;
USHORT Unknown;
USHORT LoadCount;
USHORT ModuleNameOffset;
CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION;

NTSTATUS (WINAPI * _RtlAnsiStringToUnicodeString)
(PUNICODE_STRING DestinationString,
IN PANSI_STRING SourceString,
IN BOOLEAN);

VOID (WINAPI *_RtlInitAnsiString)
(IN OUT PANSI_STRING DestinationString,
IN PCHAR SourceString);

VOID (WINAPI * _RtlFreeUnicodeString)
(IN PUNICODE_STRING UnicodeString);

NTSTATUS (WINAPI *_NtOpenSection)
(OUT PHANDLE SectionHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes);

NTSTATUS (WINAPI *_NtMapViewOfSection)
(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);

NTSTATUS (WINAPI *_NtUnmapViewOfSection)
(IN HANDLE ProcessHandle,
IN PVOID BaseAddress);

NTSTATUS (WINAPI * _NtQuerySystemInformation)(UINT, PVOID, ULONG, PULONG);

//*******************************************************************************************************
// PE File structure declarations
//
//*******************************************************************************************************

struct PE_Header
{
unsigned long signature;
unsigned short machine;
unsigned short numSections;
unsigned long timeDateStamp;
unsigned long pointerToSymbolTable;
unsigned long numOfSymbols;
unsigned short sizeOfOptionHeader;
unsigned short characteristics;
};

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;
};

struct SectionHeader
{
unsigned char sectionName[8];
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 MZHeader
{
unsigned short signature;
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[32];
unsigned long offsetToPE;
};

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

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;
}

//*******************************************************************************************************
// Fills the various structures with info of a PE image. The PE image is located at modulePos.
//
//*******************************************************************************************************

bool readPEInfo(char *modulePos, MZHeader *outMZ, PE_Header *outPE, PE_ExtHeader *outpeXH,
SectionHeader **outSecHdr)
{
// read MZ Header
MZHeader *mzH;
mzH = (MZHeader *)modulePos;

if(mzH->signature != 0x5a4d) // MZ
{
printf(“File does not have MZ header\n”);
return false;
}

// read PE Header
PE_Header *peH;
peH = (PE_Header *)(modulePos + mzH->offsetToPE);

if(peH->sizeOfOptionHeader != sizeof(PE_ExtHeader))
{
printf(“Unexpected option header size.\n”);

return false;
}

// read PE Ext Header
PE_ExtHeader *peXH;
peXH = (PE_ExtHeader *)((char *)peH + sizeof(PE_Header));

// read the sections
SectionHeader *secHdr = (SectionHeader *)((char *)peXH + sizeof(PE_ExtHeader));

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

return true;
}

//*******************************************************************************************************
// Returns the total size required to load a PE image into memory
//
//*******************************************************************************************************

int calcTotalImageSize(MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,
SectionHeader *inSecHdr)
{
int result = 0;
int alignment = inpeXH->sectionAlignment;

if(inpeXH->sizeOfHeaders % alignment == 0)
result += inpeXH->sizeOfHeaders;
else
{
int val = inpeXH->sizeOfHeaders / alignment;
val++;
result += (val * alignment);
}
for(int i = 0; i < inPE->numSections; i++)
{
if(inSecHdr.virtualSize)
{
if(inSecHdr.virtualSize % alignment == 0)
result += inSecHdr.virtualSize;
else
{
int val = inSecHdr.virtualSize / alignment;
val++;
result += (val * alignment);
}
}
}

return result;
}

//*******************************************************************************************************
// Returns the aligned size of a section
//
//*******************************************************************************************************

unsigned long getAlignedSize(unsigned long curSize, unsigned long alignment)
{
if(curSize % alignment == 0)
return curSize;
else
{
int val = curSize / alignment;
val++;
return (val * alignment);
}
}

//*******************************************************************************************************
// Copy a PE image from exePtr to ptrLoc with proper memory alignment of all sections
//
//*******************************************************************************************************

bool loadPE(char *exePtr, MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,
SectionHeader *inSecHdr, LPVOID ptrLoc)
{
char *outPtr = (char *)ptrLoc;

memcpy(outPtr, exePtr, inpeXH->sizeOfHeaders);
outPtr += getAlignedSize(inpeXH->sizeOfHeaders, inpeXH->sectionAlignment);

for(int i = 0; i < inPE->numSections; i++)
{
if(inSecHdr.sizeOfRawData > 0)
{
unsigned long toRead = inSecHdr.sizeOfRawData;
if(toRead > inSecHdr.virtualSize)
toRead = inSecHdr.virtualSize;

memcpy(outPtr, exePtr + inSecHdr.pointerToRawData, toRead);

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

return true;
}

//*******************************************************************************************************
// Loads the DLL into memory and align it
//
//*******************************************************************************************************

LPVOID loadDLL(char *dllName)
{
char moduleFilename[MAX_PATH + 1];
LPVOID ptrLoc = NULL;
MZHeader mzH2;
PE_Header peH2;
PE_ExtHeader peXH2;
SectionHeader *secHdr2;

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

strcat(moduleFilename, dllName);

// load this EXE into memory because we need its original Import Hint Table

HANDLE fp;
fp = createFile(moduleFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);

if(fp != INVALID_HANDLE_VALUE)
{
BY_HANDLE_FILE_INFORMATION fileInfo;
GetFileInformationByHandle(fp, &fileInfo);

DWORD fileSize = fileInfo.nFileSizeLow;
//printf(“Size = %d\n”, fileSize);
if(fileSize)
{
LPVOID exePtr = HeapAlloc(GetProcessHeap(), 0, fileSize);
if(exePtr)
{
DWORD read;

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 = VirtualAlloc(NULL, imageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
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)
{
if(!hModule || !apiName)
return 0;

char *ptr = (char *)hModule;
ptr += 0x3c; // offset 0x3c contains offset to PE header

ptr = (char *)(*(DWORD *)ptr) + hModule + 0x78; // offset 78h into PE header contains addr of export table

ptr = (char *)(*(DWORD *)ptr) + hModule; // ptr now points to export directory table

// offset 24 into the export directory table == number of entries in the Export Name Pointer Table
// table
DWORD numEntries = *(DWORD *)(ptr + 24);
//printf(“NumEntries = %d\n”, numEntries);

DWORD *ExportNamePointerTable = (DWORD *)(*(DWORD *)(ptr + 32) + hModule); // offset 32 into export directory contains offset to Export Name Pointer Table

DWORD ordinalBase = *((DWORD *)(ptr + 16));
//printf(“OrdinalBase is %d\n”, ordinalBase);

WORD *ExportOrdinalTable = (WORD *)((*(DWORD *)(ptr + 36)) + hModule); // offset 36 into export directory contains offset to Ordinal Table
DWORD *ExportAddrTable = (DWORD *)((*(DWORD *)(ptr + 28)) + hModule); // offset 28 into export directory contains offset to Export Addr Table

for(DWORD i = 0; i < numEntries; i++) { char *exportName = (char *)(ExportNamePointerTable + hModule); if(myStrcmpA(exportName, apiName) == TRUE) { WORD ordinal = ExportOrdinalTable; //printf("%s (i = %d) Ordinal = %d at %X\n", exportName, i, ordinal, ExportAddrTable[ordinal]); return (DWORD)(ExportAddrTable[ordinal]); } } return 0; } //******************************************************************************************************* // -- END PE File support functions -- // //******************************************************************************************************* //********************************************************************************************* // Builds a table of native API names using the export table of ntdll.dll // //********************************************************************************************* BOOL buildNativeAPITable(DWORD hModule, char *nativeAPINames[], DWORD numNames) { if(!hModule) return FALSE; char *ptr = (char *)hModule; ptr += 0x3c; // offset 0x3c contains offset to PE header ptr = (char *)(*(DWORD *)ptr) + hModule + 0x78; // offset 78h into PE header contains addr of export table ptr = (char *)(*(DWORD *)ptr) + hModule; // ptr now points to export directory table // offset 24 into the export directory table == number of entries in the Name Pointer Table // table DWORD numEntries = *(DWORD *)(ptr + 24); DWORD *ExportNamePointerTable = (DWORD *)(*(DWORD *)(ptr + 32) + hModule); // offset 32 into export directory contains offset to Export Name Pointer Table DWORD ordinalBase = *((DWORD *)(ptr + 16)); WORD *ExportOrdinalTable = (WORD *)((*(DWORD *)(ptr + 36)) + hModule); // offset 36 into export directory contains offset to Ordinal Table DWORD *ExportAddrTable = (DWORD *)((*(DWORD *)(ptr + 28)) + hModule); // offset 28 into export directory contains offset to Export Addr Table 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[ordinalValue] + hModule; char *exportName = (char *)(ExportNamePointerTable + hModule); // Win2K if(gWinVersion == 0 && *((unsigned char *)apiAddr) == 0xB8 && *((unsigned char *)apiAddr + 9) == 0xCD && *((unsigned char *)apiAddr + 10) == 0x2E) { DWORD serviceNum = *(DWORD *)((char *)apiAddr + 1); if(serviceNum < numNames) { nativeAPINames[serviceNum] = exportName; } //printf("%X - %s\n", serviceNum, exportName); } // WinXP else if(gWinVersion == 1 && *((unsigned char *)apiAddr) == 0xB8 && *((unsigned char *)apiAddr + 5) == 0xBA && *((unsigned char *)apiAddr + 6) == 0x00 && *((unsigned char *)apiAddr + 7) == 0x03 && *((unsigned char *)apiAddr + 8) == 0xFE && *((unsigned char *)apiAddr + 9) == 0x7F) { DWORD serviceNum = *(DWORD *)((char *)apiAddr + 1); if(serviceNum < numNames) { nativeAPINames[serviceNum] = exportName; } //printf("%X - %s\n", serviceNum, exportName); } } return TRUE; } //******************************************************************************************************* // Gets address of native API's that we'll be using // //******************************************************************************************************* BOOL getNativeAPIs(void) { HMODULE hntdll; hntdll = GetModuleHandle("ntdll.dll"); *(FARPROC *)&_RtlAnsiStringToUnicodeString = GetProcAddress(hntdll, "RtlAnsiStringToUnicodeString"); *(FARPROC *)&_RtlInitAnsiString = GetProcAddress(hntdll, "RtlInitAnsiString"); *(FARPROC *)&_RtlFreeUnicodeString = GetProcAddress(hntdll, "RtlFreeUnicodeString"); *(FARPROC *)&_NtOpenSection = GetProcAddress(hntdll, "NtOpenSection"); *(FARPROC *)&_NtMapViewOfSection = GetProcAddress(hntdll, "NtMapViewOfSection"); *(FARPROC *)&_NtUnmapViewOfSection = GetProcAddress(hntdll, "NtUnmapViewOfSection"); *(FARPROC *)&_NtQuerySystemInformation = GetProcAddress(hntdll, "ZwQuerySystemInformation"); if(_RtlAnsiStringToUnicodeString && _RtlInitAnsiString && _RtlFreeUnicodeString && _NtOpenSection && _NtMapViewOfSection && _NtUnmapViewOfSection && _NtQuerySystemInformation) { return TRUE; } return FALSE; } //******************************************************************************************************* // Obtain a handle to \device\physicalmemory // //******************************************************************************************************* HANDLE openPhyMem() { 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(_NtOpenSection(&hPhyMem, SECTION_MAP_READ | SECTION_MAP_WRITE, &oAttr ) != STATUS_SUCCESS) { return INVALID_HANDLE_VALUE; } return hPhyMem; } //******************************************************************************************************* // Map in a section of physical memory into this process's virtual address space. // //******************************************************************************************************* BOOL mapPhyMem(HANDLE hPhyMem, DWORD *phyAddr, DWORD *length, PVOID *virtualAddr) { NTSTATUS ntStatus; PHYSICAL_ADDRESS viewBase; *virtualAddr = 0; viewBase.QuadPart = (ULONGLONG) (*phyAddr); ntStatus = _NtMapViewOfSection(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 = _NtUnmapViewOfSection((HANDLE)-1, (PVOID)virtualAddr); if(status != STATUS_SUCCESS) { printf("Unmapping view failed!\n"); } } //******************************************************************************************************* // Assign SECTION_MAP_WRITE assess of \device\physicalmemory to current user. // //******************************************************************************************************* BOOL assignACL(void) { HANDLE hPhyMem; OBJECT_ATTRIBUTES oAttr; BOOL result = FALSE; ANSI_STRING aStr; _RtlInitAnsiString(&aStr, "\\device\\physicalmemory"); UNICODE_STRING uStr; 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(_NtOpenSection(&hPhyMem, READ_CONTROL | WRITE_DAC, &oAttr ) != STATUS_SUCCESS) { return FALSE; } else { PACL dacl; PSECURITY_DESCRIPTOR sd; if(GetSecurityInfo(hPhyMem, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &dacl, NULL, &sd) == ERROR_SUCCESS) { EXPLICIT_ACCESS ea; char userName[MAX_PATH]; 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; PACL newDacl; if(SetEntriesInAcl(1, &ea, dacl, &newDacl) == ERROR_SUCCESS) { if(SetSecurityInfo(hPhyMem, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, newDacl, NULL) == ERROR_SUCCESS) { result = TRUE; } LocalFree(newDacl); } } } return result; } //******************************************************************************************************* // Gets the kernel base address // //******************************************************************************************************* DWORD getKernelBase(void) { HANDLE hHeap = GetProcessHeap(); NTSTATUS Status; ULONG cbBuffer = 0x8000; PVOID pBuffer = NULL; DWORD retVal = DEF_KERNEL_BASE; 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); 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”))
{
//printf(“%.8X – %s\n”, smi->Base, smi->ImageName);
retVal = (DWORD)(smi->Base);
break;
}
smi++;
}

HeapFree(hHeap, 0, pBuffer);

return retVal;
}

struct FixupBlock
{
unsigned long pageRVA;
unsigned long blockSize;
};

BOOL checkKiServiceTableAddr(PVOID exeAddr, DWORD chkAddr, PE_ExtHeader *peXH2)
{
if(peXH2->relocationTableAddress && peXH2->relocationTableSize)
{
FixupBlock *fixBlk = (FixupBlock *)((char *)exeAddr + peXH2->relocationTableAddress);

while(fixBlk->blockSize)
{
int numEntries = (fixBlk->blockSize – sizeof(FixupBlock)) >> 1;

unsigned short *offsetPtr = (unsigned short *)(fixBlk + 1);

for(int i = 0; i < numEntries; i++) { int relocType = (*offsetPtr & 0xF000) >> 12;

if(relocType == 3)
{
DWORD *codeLoc = (DWORD *)((char *)exeAddr + fixBlk->pageRVA + (*offsetPtr & 0x0FFF));

if(fixBlk->pageRVA + (*offsetPtr & 0x0FFF) + peXH2->imageBase == chkAddr)
{
return TRUE;
}
}
offsetPtr++;
}
fixBlk = (FixupBlock *)offsetPtr;
}
}
return FALSE;
}

// 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)
{
if(peXH2->relocationTableAddress && peXH2->relocationTableSize)
{
FixupBlock *fixBlk = (FixupBlock *)((char *)exeAddr + peXH2->relocationTableAddress);

while(fixBlk->blockSize)
{
//printf(“Addr = %X\n”, fixBlk->pageRVA);
//printf(“Size = %X\n”, fixBlk->blockSize);

int numEntries = (fixBlk->blockSize – sizeof(FixupBlock)) >> 1;
//printf(“Num Entries = %d\n”, numEntries);

unsigned short *offsetPtr = (unsigned short *)(fixBlk + 1);

for(int i = 0; i < numEntries; i++) { int relocType = (*offsetPtr & 0xF000) >> 12;

//printf(“Val = %X\n”, *offsetPtr);
//printf(“Type = %X\n”, relocType);

if(relocType == 3)
{
DWORD *codeLoc = (DWORD *)((char *)exeAddr + fixBlk->pageRVA + (*offsetPtr & 0x0FFF));

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(checkKiServiceTableAddr(exeAddr, kiServiceTableAddr, peXH2))
{
return kiServiceTableAddr – peXH2->imageBase;
}
}
}

offsetPtr++;
}

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

//*******************************************************************************************************
// Program entry point
// No commandline arguments required.
//
//*******************************************************************************************************

int main(int argc, char* argv[])
{
MZHeader mzH2;
PE_Header peH2;
PE_ExtHeader peXH2;
SectionHeader *secHdr2;

printf(“SDTrestore Version 0.2 Proof-of-Concept by SIG^2 G-TEC (www.security.org.sg)\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;
}

assignACL();
HANDLE hPhyMem = openPhyMem();
if(hPhyMem == INVALID_HANDLE_VALUE)
assignACL();

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

PVOID exeAddr = loadDLL(“\\ntoskrnl.exe”);
if(!exeAddr)
{
printf(“Failed to load ntoskrnl.exe!\n”);
return 1;
}

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;
}

DWORD kernelPhyBase = getKernelBase() – PROT_MEMBASE;
DWORD kernelOffset = kernelPhyBase – peXH2.imageBase;

printf(“KeServiceDescriptorTable\t\t%X\n”, sdtAddr + kernelPhyBase + PROT_MEMBASE);

unsigned char *ptr = NULL;
DWORD pAddr = sdtAddr + kernelPhyBase;
DWORD wantedAddr = pAddr;
DWORD len = 0x2000;

// map in page containing KeServiceDecriptorTable
if(mapPhyMem(hPhyMem, &pAddr, &len, (LPVOID *)&ptr))
{
DWORD start = wantedAddr – pAddr;
DWORD serviceTableAddr, sdtCount;
DWORD wantedBytes = len – start;
if(wantedBytes >= 4)
{
serviceTableAddr = *((DWORD *)(&ptr[start]));
printf(“KeServiceDecriptorTable.ServiceTable\t%X\n”, serviceTableAddr);
if(wantedBytes >= 12)
{
sdtCount = *(((DWORD *)(&ptr[start])) + 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;
wantedAddr = pAddr;
ptr = NULL;
len = 0x2000;
if(mapPhyMem(hPhyMem, &pAddr, &len, (LPVOID *)&ptr))
{
start = wantedAddr – pAddr;
DWORD numEntries = (len – start) >> 2;
if(numEntries >= sdtCount)
{
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);

PVOID ntdll = loadDLL(“\\ntdll.dll”);
if(!ntdll)
{
printf(“Failed to load ntdll.dll!\n”);
return 1;
}

buildNativeAPITable((DWORD)ntdll, nativeApiNames, sdtCount);

DWORD *serviceTable = (DWORD *)(&ptr[start]);
DWORD *fileServiceTable = (DWORD *)((DWORD)exeAddr + wantedAddr – kernelOffset – peXH2.imageBase);

// calculate address based on 90210’s suggestion
DWORD fileAddr2 = (DWORD)exeAddr + getKiServiceTableAddr(exeAddr, sdtAddr, &peXH2);

if(fileAddr2 && (DWORD)fileServiceTable != fileAddr2)
{
printf(“Two possible addresses of KiServiceTable were found.\n\n”);
printf(“1 – %.8X\n”, fileServiceTable);
printf(“2 – %.8X (using method suggested by 90210)\n\n”, fileAddr2);
printf(“select One (1-2): “);

char choice[10];
memset(choice, 0, sizeof(choice));
fgets(choice, sizeof(choice) – 1, stdin);
printf(“\n”);
int intChoice = atoi(choice);
if(intChoice < 1 || intChoice > 2)
{
printf(“Invalid selection!\n”);
unmapPhyMem((DWORD)ptr);
return 1;
}
else if(intChoice == 2)
fileServiceTable = (DWORD *)fileAddr2;
}

if(!IsBadReadPtr(fileServiceTable, sizeof(DWORD)) &&
!IsBadReadPtr(&fileServiceTable[sdtCount-1], sizeof(DWORD)))
{
DWORD hookCount = 0;
for(DWORD i = 0; i < sdtCount; i++) { if((serviceTable - PROT_MEMBASE - kernelOffset) != fileServiceTable) { printf("%-25s %3X --[hooked by unknown at %X]--\n", (nativeApiNames ? nativeApiNames : "Unknown API"), i, serviceTable); hookCount++; } } printf("\nNumber of Service Table entries hooked = %u\n", hookCount); if(hookCount) { printf("\nWARNING: THIS IS EXPERIMENTAL CODE. FIXING THE SDT MAY HAVE GRAVE\n" "CONSEQUENCES, SUCH AS SYSTEM CRASH, DATA LOSS OR SYSTEM CORRUPTION.\n" "PROCEED AT YOUR OWN RISK. YOU HAVE BEEN WARNED.\n"); printf("\nFix SDT Entries (Y/N)? : "); char inputReply[10]; memset(inputReply, 0, sizeof(inputReply)); fgets(inputReply, sizeof(inputReply) - 1, stdin); printf("\n"); if(stricmp(inputReply, "y\n") == 0) { 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); } } } else printf("[-] SDT Entries NOT fixed.\n"); } } 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"); } } unmapPhyMem((DWORD)ptr); } } return 0; } [/code]

关注微信公众号,手机阅读更方便: 程序员的阅微草堂

知识共享许可协议莿鸟栖草堂CFC4N 创作,采用 知识共享 署名-非商业性使用-相同方式共享(3.0未本地化版本)许可协议进行许可。基于http://www.cnxct.com上的作品创作。转载请注明转自:检测并恢复 ssdt

发表评论

电子邮件地址不会被公开。 必填项已用*标注