检测并恢复 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 <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <aclapi.h>

#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 = cr&#101;ateFile(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("sel&#101;ct 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 sel&#101;ction!\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 lik&#101;ly 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;
}

随机日志

发表评论

0 评论.

Leave a Reply



[ Ctrl + Enter ]

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word

CNXCT小组的博客 is Stephen Fry proof thanks to caching by WP Super Cache