您的位置:首页 > 其它

一些关于SSDT的东西

2012-07-12 09:50 246 查看
先转载下某个博客园网友的文章,这里表示下感谢。/article/5920327.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到进程空间,然后解析重定位表(重定位表包括至少一个重定位块)。

待续。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: