一些关于SSDT的东西
先转载下某个博客园网友的文章,这里表示下感谢。http://www.cnblogs.com/hhuai/archive/2010/03/01/1675389.html
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
网上其实有很多介绍ssdt还原的文章,看起来也确实是一个很简单,套用一句网友的话,就是菜B才用的技术。但我着实也费了一番工夫,也可能真是我自己的理解能力太差。一个人搞了两三天才弄出来,其实真的很简单,只是有些文章说得很含糊。 下面我说几个要点: 1. ssdt已存在于原始pe文件中。(具体是ntoskrnl.exe、ntkrnlpa.exe、ntkrnlmp.exe、ntkrpamp.exe 自已想法确定) 操作系统根据是否是多处理器平台和是否支持PAE(Physical Address Extension)来选择合适的系 Picasa Content 统文件。 ntoskrnl.exe 单x86处理器,使用不超过4GB的物理内存。 ntkrnlpa.exe 单x86处理器,支持PAE。 ntkrnlmp.exe 多处理器,使用不超过4GB的物理内存。 ntkrpamp.exe 多处理器,支持PAE。 //如何确定系统的内核是ntoskrnl.exe还是ntkrnlpa.exe? 这里没有考虑多核 int nPaeOpen = 0; WCHAR* kernelName= NULL; __asm{ _emit 0x0f _emit 0x20 _emit 0xe0 mov edx, 0x1 shl edx, 5 and eax, edx shr eax, 5 mov nPaeOpen,eax } kernelName = nPaeOpen?L"ntoskrnl.exe":L"ntkrnlpa"; 2. 位置的计算牵涉到几个地址: a. ssdt现存地址,可以通过windbg演示一下. (0x80504960 中存放地址表) b. 找出当前内核基址 (804d8000) c. 有了这两个地址之后就可以算出RVA = 0x80504960 – 0x804d8000 = 0x2C960 d.我们再去ntkrnlpa.exe中寻找对应的File offset (2BF60) e. 拿UE找出实际值 (04 C1 4C 00 也就是 0x004CC104) f. 搞定,最后验证一下, 0x004CC104 – ImageBase + 内核基址 0x004CC104 – 0x00400000 + 0x804d8000 = 805A4104 在windbg中看一下,地址符合,不符的话,肯定就是被 hook掉了。 //根据RVA获取原始PE文件中的SSDT表 void GetSSDTFromPE(const SSDT_PARAM& ssdt_param ){ gvSSDTList.clear(); const DWORD rva = ssdt_param.ServiceTableBase-ssdt_param.nKernelImageBase; HANDLE hMapFile =NULL; HANDLE hFile =NULL; SIZE_T size = 0; int ECode = 0; PVOID pLoadBase =NULL; __try{ __try{ hFile = CreateFile(ssdt_param.kernelpath, FILE_ALL_ACCESS, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if( hFile == INVALID_HANDLE_VALUE){ DisplayError(_T("CreateFile"),GetLastError()); RaiseException(1, // 抛?出?异?常?码?为?的?SEH异?常? 0, 0, NULL); // 没?有?参?数? __leave; } DWORD dwBytesInBlock = GetFileSize(hFile,NULL); //文?件?长?度? hMapFile = CreateFileMapping( hFile, // use paging file NULL, // default security PAGE_READWRITE, 0, // max. object size dwBytesInBlock, // buffer size NULL); // name of mapping object CloseHandle(hFile); if( hMapFile == NULL){ RaiseException(2, 0, 0, NULL); __leave; } pLoadBase = MapViewOfFile(hMapFile,FILE_MAP_ALL_ACCESS,0,0,dwBytesInBlock); if(pLoadBase == NULL){ RaiseException(3, 0, 0, NULL); __leave; } //_tprintf(_T("ImageBase = 0x%x\n"),pLoadBase); PIMAGE_NT_HEADERS pImageNtHeader = ImageNtHeader(pLoadBase); if(pImageNtHeader == NULL){ RaiseException(3, 0, 0, NULL); __leave; } DWORD ImageBase = pImageNtHeader->OptionalHeader.ImageBase; DWORD* VaAddr = (DWORD*)ImageRvaToVa(pImageNtHeader,pLoadBase,rva,NULL); if(VaAddr == NULL){ RaiseException(4, 0, 0, NULL); __leave; } //_tprintf(_T("Index\tAddress\n")); for(DWORD i = 1; i <= ssdt_param.nCount; ++i,++VaAddr){ //_tprintf(TEXT("0x%x\t[0x%x]\n"),i,*VaAddr-ImageBase+ssdt_param.nKernelImageBase); gvSSDTList.push_back(*VaAddr-ImageBase+ssdt_param.nKernelImageBase); } } __except(ECode=GetExceptionCode()) { DisplayError(_T(" "),GetLastError()); } } __finally{ if(pLoadBase) UnmapViewOfFile(pLoadBase); if(hMapFile) CloseHandle(hMapFile); if(hFile) CloseHandle(hFile); } return ; }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
我系统里面的内核文件是ntkrnlpa.exe,首先要明白的是ntkrnlpa.exe这个文件导出了SSDT表,即SSDT本来表就存在于ntkrnlpa.exe的某处,并不是凭空产生的。只是ntkrnlpa.exe这个PE文件的IMAGEBASE是00400000,所以SSDT表的地址也是0040XXXX的某地址处,那么这个XXXX就是SSDT表的RVA了,RVA是什么意思呢?就是说不管你ntkrnlpa.exe加载地址是哪里,只要用加载地址加上这个RVA就能得到SSDT表的实际地址了。
这就是比较通用的一种获得SSDT表地址的方法了。
ntkrnlpa.exe的加载地址+SSDT表的RVA=SSDT表的实际地址
ntkrnlpa.exe的加载地址获取方法网上貌似有4种吧,而获取SSDT表的RVA的方法是通过MapViewOfFile(或者LoadLibraryEx也可以) ntkrnlpa到进程空间,然后解析重定位表(重定位表包括至少一个重定位块)。
待续。。。