能力值:
( LV4,RANK:50 )
|
-
-
2 楼
似乎有点线索了...
我本机的SSDT Count应该为0x11c,这里却是0x13D,说明被扩展了,,,,
从Xuetr中可以看出是金山毒霸( )扩展的,,,
也就是说,,现在的KeServiceDescriptorTable->ServiceTableBase指向的并不是原始那份,而是金山的KAVSafe.sys所复制和扩展的...
|
能力值:
( LV12,RANK:210 )
|
-
-
10 楼
/*****************************************************************************************
*
*函数名:FindModuleByAddress
*功能描述:根据函数地址查找所属模块
*
******************************************************************************************/
/*****************************************************************************************
*
* 原理: 利用ZwQuerySystemInformation传入SystemModuleInformation(11)得到系统模块列表
* 得到每个模块的起始和结束地址
* 比对地址,在那个范围就属于哪个模块
* 得到模块名
*
******************************************************************************************/
#include "refresh.h"
void FindModuleByAddress( ULONG Address, PVOID buffer)
{
NTSTATUS status;
ULONG size;
ULONG i;
ULONG minAddress;
ULONG maxAddress;
PSYSMODULELIST List;
ZwQuerySystemInformation( SystemModuleInformation ,&size,0,&size);
KdPrint(("[FindModuleByAddress] size:0x%x\n",size));
List=(PSYSMODULELIST)ExAllocatePool(NonPagedPool,size);
if(List==NULL)
{
KdPrint(("[FindModuleByAddress] malloc memory failed\n"));
return ;
}
status=ZwQuerySystemInformation(SystemModuleInformation,List,size,0);
if(!NT_SUCCESS(status))
{
KdPrint(("[FindModuleByAddress] query failed\n"));
//打印错误
KdPrint(("[FindModuleByAddress] status: 0x%x\n",status));
ExFreePool( List );
return ;
}
//得到了模块链表
//判断模块名
for( i=0; i<List->ulCount; i++)
{
//得到模块的范围
minAddress = (ULONG)List->smi[i].Base;
maxAddress = minAddress + List->smi[i].Size;
//判断地址
if( Address >= minAddress && Address <= maxAddress )
{
memcpy( buffer, List->smi[i].ImageName,sizeof(List->smi[i].ImageName));
KdPrint(("[FindModuleByAddress] modulename: %s\n",buffer));
//释放内存
ExFreePool(List);
break;
}
}
}
/***************************************************************************************
*
* 函数名:GetOriFunctionAddress
* 功能描述:得到原始SSDT表中函数地址
*
****************************************************************************************/
/***************************************************************************************
*
* 原理: 找到内核文件,获取基址BaseAddress
* 根据内核文件查找SSDT表的文件偏移SSDTFileOffset = SSDTRVA-(节RVA-节Offset)
* 读取函数的文件偏移FunctionFileOffset
* VA=BaseAddress+FunctionFileOffset-00400000=800d8000 + FunctionFileOffset
*
*****************************************************************************************/
/****************************************************************************************
*
* 根据RVA查找所在的文件偏移:FileOffset = Rva- (节Rva - 节Offset)
* 找到区块表
*****************************************************************************************/
ULONG FindFileOffsetByRva( ULONG ModuleAddress,ULONG Rva)
{
PIMAGE_DOS_HEADER dos;
PIMAGE_FILE_HEADER file;
PIMAGE_SECTION_HEADER section;
//区块数目
ULONG number;
ULONG i;
ULONG minAddress;
ULONG maxAddress;
ULONG SeFileOffset;
ULONG FileOffset;
dos = (PIMAGE_DOS_HEADER)ModuleAddress;
file = (PIMAGE_FILE_HEADER)( ModuleAddress + dos->e_lfanew + 4 );
//得到区块数量
number = file->NumberOfSections;
KdPrint(("[FindFileOffsetByRva] number :0x%x\n",number));
//得到第一个区块地址
section = (PIMAGE_SECTION_HEADER)(ModuleAddress + dos->e_lfanew + 4 + sizeof(IMAGE_FILE_HEADER) + file->SizeOfOptionalHeader);
for( i=0;i<number;i++)
{
minAddress = section[i].VirtualAddress;
maxAddress = minAddress + section[i].SizeOfRawData;
SeFileOffset = section[i].PointerToRawData;
if( Rva > minAddress && Rva < maxAddress)
{
KdPrint(("[FindFileOffsetByRva] minAddress :0x%x\n",minAddress));
KdPrint(("[FindFileOffsetByRva] SeFileOffset :0x%x\n",SeFileOffset));
FileOffset = Rva - ( minAddress - SeFileOffset);
KdPrint(("[FindFileOffsetByRva] FileOffset :0x%x\n",FileOffset));
break ;
}
}
return FileOffset;
}
//路径解析出子进程名
void GetModuleName( char *ProcessPath, char *ProcessName)
{
ULONG n = strlen( ProcessPath) - 1;
ULONG i = n;
KdPrint(("%d",n));
while( ProcessPath[i] != '\\')
{
i = i-1;
}
strncpy( ProcessName, ProcessPath+i+1,n-i);
}
/****************************************************************************************
*
* 根据传入的服务号得到函数原始地址
*
****************************************************************************************/
ULONG FindOriAddress( ULONG index )
{
//根据传入的index得到函数VA地址
//重定位函数地址
//BaseAddress - 0x00400000 + *(PULONG)(FileOffset+(index*4))
//ZwQuerySystemInformation得到内核文件基地址
//得到SSDT表的地址
//得到SSDT RVA 查找SSDT RVA所在的节
NTSTATUS status;
ULONG size;
ULONG BaseAddress;
ULONG SsdtRva;
ULONG FileOffset = 0;
PSYSMODULELIST list;
char Name[32]={0};
char PathName[256] = "\\SystemRoot\\system32\\";
ANSI_STRING name;
UNICODE_STRING modulename;
OBJECT_ATTRIBUTES object_attributes;
IO_STATUS_BLOCK io_status = {0};
HANDLE hFile;
//读取的位置
ULONG location;
LARGE_INTEGER offset;
ULONG address;
//得到需要申请的内存大小
ZwQuerySystemInformation( SystemModuleInformation,&size,0,&size );
//申请内存
list = (PSYSMODULELIST) ExAllocatePool( NonPagedPool,size );
//验证是否申请成功
if( list == NULL)
{
//申请失败
KdPrint(("[FindOriAddress] malloc memory failed\n"));
ExFreePool(list);
return 0;
}
status = ZwQuerySystemInformation( SystemModuleInformation,list,size,0);
if( !NT_SUCCESS( status ))
{
//获取信息失败
KdPrint(("[FindOriAddress] query failed\n"));
KdPrint(("[FindOriAddress] status:0x%x\n",status));
ExFreePool(list);
return 0;
}
//得到模块基址,第一个模块为内核文件
BaseAddress = (ULONG )list->smi[0].Base;
KdPrint(("[FindOriAddress] BaseAddress:0x%x\n",BaseAddress));
//分离出内核文件名
GetModuleName(list->smi[0].ImageName,Name);
KdPrint(("[FindOriAddress] processname:%s\n",Name));
strcat(PathName,Name);
RtlInitAnsiString(&name,PathName);
RtlAnsiStringToUnicodeString(&modulename,&name,TRUE);
KdPrint(("[FindOriAddress] modulename: %wZ\n",&modulename));
ExFreePool(list);
//经验证地址正确
//得到SSDT表的Rva
SsdtRva = (ULONG)KeServiceDescriptorTable->ServiceTableBase - BaseAddress;
//验证
KdPrint(("[FindOriAddress] SsdtRva:0x%x\n",SsdtRva));
//根据RVA查找文件偏移,//得到文件偏移了
FileOffset= FindFileOffsetByRva( BaseAddress,SsdtRva);
KdPrint(("[FindOriAddress] FileOffset:0x%x\n",FileOffset));
//读取的位置
location = FileOffset + index * 4;
offset.QuadPart =location;
KdPrint(("[FindOriAddress] location:0x%x\n",location));
//利用ZwReadFile读取文件
//初始化OBJECT_ATTRIBUTES结构
InitializeObjectAttributes(
&object_attributes,
&modulename,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
//打开文件
status = ZwCreateFile(
&hFile,
FILE_EXECUTE | SYNCHRONIZE,
&object_attributes,
&io_status,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE |
FILE_RANDOM_ACCESS |
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if( !NT_SUCCESS( status ))
{
KdPrint(("[FindOriAddress] open error\n"));
KdPrint(("[FindOriAddress] status = 0x%x\n", status));
ZwClose( hFile );
return 0;
}
status = ZwReadFile(
hFile,
NULL,
NULL,
NULL,
NULL,
&address,
sizeof(ULONG),
&offset,
NULL);
if( !NT_SUCCESS( status ))
{
KdPrint(("[FindOriAddress] read error\n"));
KdPrint(("[FindOriAddress] status = 0x%x\n", status));
ZwClose( hFile );
return 0;
}
KdPrint(("[FindOriAddress] address:0x%x\n",address));
//重定位
address = BaseAddress - 0x00400000 + address;
KdPrint(("[FindOriAddress] Oriaddress:0x%x\n",address));
//释放动态分配的内存
RtlFreeUnicodeString(&modulename);
ZwClose( hFile );
return address;
}
/******************************************************************************************
*
* 得到SSDT Shadow当前地址
* 1、KeServiceDescriptorTable - 0x40 + 0x10
* 2、搜索KeAddSystemServiceTable函数,特征码
* 3、Kthread->ServiceTable指向
* 4、MJ提出的搜索特定内存
*
*******************************************************************************************/
//方式1,XP下-0x40;
ULONG GetSSDTShadowAddress1()
{
ULONG address;
ULONG ssdt;
ssdt = (ULONG)KeServiceDescriptorTable;
address = ssdt - 0x30;
KdPrint(("[GetSSDTShadowAddress] ssdt:0x%x\n",ssdt));
KdPrint(("[GetSSDTShadowAddress] address:0x%x\n",address));
return address;
}
//方式2
ULONG GetSSDTShadowAddress2()
{
ULONG address;
PUCHAR addr;
PUCHAR p;
addr = (PUCHAR)KeAddSystemServiceTable;
for( p=addr; p<addr+PAGE_SIZE; p++)
{
if(*(PUSHORT)p == 0x888D)
{
address = *(PULONG)((ULONG)p+2);
break;
}
}
address = address + 0x10;
KdPrint(("[GetSSDTShadowAddress] address:0x%x\n",address));
return address;
}
//方式3
ULONG GetSSDTShadowAddress3()
{
return 0;
}
//方式4
ULONG GetSSDTShadowAddress4()
{
return 0;
}
/*********************************************************************************
*
* 获得win32k.sys基址
* 1、ZwQuerySystemInformation
* 2、遍历DriverSection链表
*
**********************************************************************************/
ULONG GetWin32Base1()
{
NTSTATUS status;
ULONG i;
ULONG size;
ULONG address;
PSYSMODULELIST List;
ZwQuerySystemInformation( SystemModuleInformation ,&size,0,&size);
KdPrint(("[FindModuleByAddress] size:0x%x\n",size));
List=(PSYSMODULELIST)ExAllocatePool(NonPagedPool,size);
if (List==NULL)
{
KdPrint(("[FindModuleByAddress] malloc memory failed\n"));
ExFreePool( List );
return 0;
}
status=ZwQuerySystemInformation(SystemModuleInformation,List,size,0);
if (!NT_SUCCESS(status))
{
KdPrint(("[FindModuleByAddress] query failed\n"));
//打印错误
KdPrint(("[FindModuleByAddress] status: 0x%x\n",status));
ExFreePool( List );
return 0;
}
for ( i=0; i < List->ulCount; i++ )
{
if( strcmp(List->smi[i].ImageName,"\\SystemRoot\\System32\\win32k.sys") == 0)
{
KdPrint(("[GetWin32Base]name :%s\n",List->smi[i].ImageName));
address = (ULONG)List->smi[i].Base;
KdPrint(("[GetWin32Base1] win32k.sys address:0x%x\n",address));
}
}
return address;
}
/*********************************************************************************************
*
* 驱动对象DRIVER_OBJECT中的DRIVER_SECTION
* LDR_DATA_TABLE_ENTRY结构包含系统加载模块链表及基址
*
*
**********************************************************************************************/
ULONG GetWin32Base2( PDRIVER_OBJECT driver)
{
PLIST_ENTRY pList = NULL;
PLDR_DATA_TABLE_ENTRY pLdr = NULL;
ULONG BaseAddress = 0;
pList = ( (PLIST_ENTRY)driver->DriverSection )->Flink;
do
{
pLdr = CONTAINING_RECORD(
pList,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks
);
if( pLdr->EntryPoint != NULL && pLdr->FullDllName.Buffer!= NULL )
{
if( !_wcsicmp( pLdr->FullDllName.Buffer, L"\\SystemRoot\\System32\\win32k.sys"))
{
BaseAddress = (ULONG )pLdr->DllBase;
KdPrint(("[GetWin32Base2] win32k.sys address:0x%x\n",BaseAddress));
break ;
}
}
pList = pList->Flink;
}while( pList != ((PLIST_ENTRY)driver->DriverSection)->Flink );
return BaseAddress;
}
/****************************************************************************************
*
* 根据传入的服务号得到Shadow 函数原始地址
*
****************************************************************************************/
ULONG FindShadowOriAddress( ULONG index )
{
//内核文件win32k.sys基地址
//得到SSDT Shadow表的地址
//得到文件偏移
NTSTATUS status;
ULONG size;
ULONG BaseAddress;
ULONG ShadowBase;
ULONG ShadowAddress;
ULONG SsdtRva;
ULONG FileOffset = 0;
UNICODE_STRING modulename;
OBJECT_ATTRIBUTES object_attributes;
IO_STATUS_BLOCK io_status = {0};
HANDLE hFile;
//读取的位置
ULONG location;
LARGE_INTEGER offset;
ULONG address;
BaseAddress = GetWin32Base1();
KdPrint(("[FindShadowOriAddress] BaseAddress:0x%x\n",BaseAddress));
//经验证地址正确
ShadowBase = GetSSDTShadowAddress2();
ShadowAddress = *(PULONG)ShadowBase;
KdPrint(("[FindShadowOriAddress] ShadowAddress:0x%x\n",ShadowAddress));
//得到SSDT表的Rva
SsdtRva = ShadowAddress - BaseAddress;
//验证
KdPrint(("[FindOriAddress] SsdtRva:0x%x\n",SsdtRva));
//读取的位置
location = SsdtRva + index * 4;
offset.QuadPart =location;
KdPrint(("[FindOriAddress] location:0x%x\n",location));
//利用ZwReadFile读取文件
//初始化OBJECT_ATTRIBUTES结构
RtlInitUnicodeString(&modulename, L"\\SystemRoot\\system32\\win32k.sys");
InitializeObjectAttributes(
&object_attributes,
&modulename,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
//打开文件
status = ZwCreateFile(
&hFile,
FILE_EXECUTE | SYNCHRONIZE,
&object_attributes,
&io_status,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE |
FILE_RANDOM_ACCESS |
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if( !NT_SUCCESS( status ))
{
KdPrint(("[FindOriAddress] open error\n"));
KdPrint(("[FindOriAddress] status = 0x%x\n", status));
ZwClose( hFile );
return 0;
}
status = ZwReadFile(
hFile,
NULL,
NULL,
NULL,
NULL,
&address,
sizeof(ULONG),
&offset,
NULL);
if( !NT_SUCCESS( status ))
{
KdPrint(("[FindOriAddress] read error\n"));
KdPrint(("[FindOriAddress] status = 0x%x\n", status));
ZwClose( hFile );
return 0;
}
KdPrint(("[FindOriAddress] address:0x%x\n",address));
address = address;
KdPrint(("[FindOriAddress] Oriaddress:0x%x\n",address));
ZwClose( hFile );
return address;
}
|
能力值:
( LV3,RANK:20 )
|
-
-
11 楼
搜一下KiInitSystem()
mov eax, ds:_KiServiceLimit // ssdt数量
push 10h
pop ecx
mov ds:dword_48B528, eax
; ...
; ...
mov ds:_KeServiceDescriptorTable, offset _KiServiceTable // ssdt由KiServiceTable初始化的
|
能力值:
( LV9,RANK:160 )
|
-
-
12 楼
ShadowsSSDT查找KeAddXX那个函数方法相当不稳定,我自己测试过,在虚拟机上找到的是SSDT,要移动一个SSDT大小才是ShadowsSSDT,但是实际电脑上却不是这样的,一运行就蓝屏,比较稳妥的方法是从Win32k.sys驱动入口搜索进去获取
引用文章如下:
http://hi.baidu.com/%D0%A1%C2%B9%BD%A3/blog/item/92c302dc65b1751f49540318.html
读文件取ShadowSSDT函数地址2010-05-17 00:31ShadowSSDT定位,从前听说可以从KeServiceDescriptorTable进去获取地址,自己测试奇怪的是winbug里面看到一
个ShadowsSSDT地址,跑内存去看发现居然是SSDT,这个加一才是正确的ShadowsSSDT,于是虚拟机测试成功了,
到了真实电脑上测试,立马蓝屏,于是打算寻找其它获取途径,直接拿win32开刀,调试之略有心得
方法如下:
从DriverEntry(PE入口点)进去,可以找到设置ShadowSSDT的代码,提取出来就好了,代码相当简单
指令格式如下:Windows 7,XP下通用
push Offset Shadow SSDT
call KeAddSystemServiceTable
所以结构体如下:
typedef struct _RAW_ShadowSSDT
{
unsigned char PushOp;//0x68
unsigned long TableRva;
unsigned short CallOp;//0x15ff
unsinged long KeIn;
}RAW_SHADOWSSDT,*PRAW_SHADOWSSDT;
至于界限,当然可以从这附近取得,但是不通用,所以读取时注意就可以了表以00000101 01000000结尾
ULONG GetRvaOffShadowTableFromFile(UCHAR* Base)
{
UCHAR * Pointer,*OpeCode=0;
Pointer=Base;
int Len=0;
while((Len=SizeOfCode(Pointer,&OpeCode))>0)
{
if(*OpeCode==0xc2)
break;
if(*OpeCode==0x68)
{
PRAW_SHADOWSSDT pCode=(PRAW_SHADOWSSDT)Pointer;
if(pCode->CallOp==0x15ff)
{
return pCode->TableRva;
}
}
Pointer+=Len;
}
return 0;
}
最后插一句,表里面的OFFSET换算到实际内存地址是 Offset-ImageBase+模块基地址
表里面的OFFSET换算到内存文件地址是 Offset-ImageBase+文件读进来开始处
方法优点,稳定,保险(注意由于)KeServiceDescriptorTable,调用本来就比较少,可以改掉函数里的地址,这样再去读,就XXX了,自己在虚拟机里居然看这个地址是SSDT,显然被改成SSDT也是可以的
还有顺便可以把Inline Hook检测带上了……
运行某ARK结果如下:
Index:01d8当前地址:bf8f301e 原始地址:bf8f301e 挂钩类型0
Index:01d9当前地址:bf864d59 原始地址:bf864d59 挂钩类型0
Index:01da当前地址:bf803708 原始地址:bf803708 挂钩类型0
Index:01db当前地址:bf8085fa 原始地址:bf8085fa 挂钩类型3000、、inline
Index:01dc当前地址:f3ef2764 原始地址:bf89ad17 挂钩类型200
Index:01dd当前地址:bf8978a3 原始地址:bf8978a3 挂钩类型0
Index:01de当前地址:bf8bfcd0 原始地址:bf8bfcd0 挂钩类型0
Index:01df当前地址:bf911ae1 原始地址:bf911ae1 挂钩类型0
Index:01e0当前地址:bf913655 原始地址:bf913655 挂钩类型0
Index:01e1当前地址:bf911e8f 原始地址:bf911e8f 挂钩类型0
Index:01e2当前地址:bf913db4 原始地址:bf913db4 挂钩类型0
Index:01e3当前地址:f3ef27a6 原始地址:bf803ba4 挂钩类型200
Index:01e4当前地址:bf9109b0 原始地址:bf9109b0 挂钩类型0
Index:01e5当前地址:bf878b08 原始地址:bf878b08 挂钩类型0
Index:01e6当前地址:bf9118b8 原始地址:bf9118b8 挂钩类型0
Index:01e7当前地址:bf826d2d 原始地址:bf826d2d 挂钩类型
|
能力值:
( LV12,RANK:210 )
|
-
-
13 楼
4种方式获取SSDTShadow地址
//得到SSDTShadow地址
//方式1,XP下-0x40;
ULONG GetSSDTShadowAddress1()
{
ULONG address;
ULONG ssdt;
ssdt = (ULONG)KeServiceDescriptorTable;
address = ssdt - 0x30;
KdPrint(("[GetSSDTShadowAddress] ssdt:0x%x\n",ssdt));
KdPrint(("[GetSSDTShadowAddress] address:0x%x\n",address));
return address;
}
//方式2 搜索KeAddSystemServiceTable
ULONG GetSSDTShadowAddress()
{
ULONG address;
PUCHAR addr;
PUCHAR p;
addr = (PUCHAR)KeAddSystemServiceTable;
for( p=addr; p<addr+PAGE_SIZE; p++)
{
if(*(PUSHORT)p == 0x888D)
{
address = *(PULONG)((ULONG)p+2);
break;
}
}
address = address + 0x10;
KdPrint(("[GetSSDTShadowAddress] address:0x%x\n",address));
return address;
}
//方式3 遍历系统线程 TableCode指向
ULONG GetSSDTShadowAddress3()
{
//遍历线程
ULONG i;
NTSTATUS status;
ULONG thread;
ULONG address=0;
for( i=8; i<32768; i=i+4)
{
status = PsLookupThreadByThreadId( (HANDLE)i,&(PETHREAD)thread);
if(NT_SUCCESS(status))
{
if( *(PULONG)(thread+0x0e0) != (ULONG)KeServiceDescriptorTable )
{
address = *(PULONG)(thread+0x0e0);
break ;
}
}
}
return address;
}
//方式4 ,MJ所说的暴力搜索KeAddSystemServiceTable有效内存
//获取KeServiceDescriptorTableShadow地址
ULONG GetAddressOfShadowTable()
{
unsigned int i;
unsigned char *p;
unsigned int dwordatbyte;
UNICODE_STRING usKeAddSystemServiceTable;
RtlInitUnicodeString(&usKeAddSystemServiceTable,
L"KeAddSystemServiceTable");
p = (unsigned char*)MmGetSystemRoutineAddress(&usKeAddSystemServiceTable);
for(i = 0; i < 4096; i++, p++)
{
__try
{
dwordatbyte = *(unsigned int*)p;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
return 0;
}
if(MmIsAddressValid((PVOID)dwordatbyte))
{
if(memcmp((PVOID)dwordatbyte, &KeServiceDescriptorTable, 16) == 0)
{
if((PVOID)dwordatbyte == &KeServiceDescriptorTable)
{
continue;
}
return dwordatbyte;
}
}
}
return 0;
}
|