能力值:
( LV12,RANK:1010 )
11 楼
引用:
但是通过我跟踪堆栈调用,发现要经过很多次KeWaitForSingleObject调用,才会进入
因为楼主没有主动触发,所以要多次.
以下代码是主动触发,马上就能定位了啊
VOID
Get_MmLoadSystemImage_addr(
)
/*++
Author: sudami [sudami@163.com]
Time : 2008/12/23 [23:12:2008 - 10:23]
Routine Description:
获取MmLoadSystemImage的地址
--*/
{
NTSTATUS status ;
UNICODE_STRING uFileName;
STRING ntNameString;
CCHAR ntNameFile[128] =
"\\??\\C:\\" ;
// 得到 KeWaitForSingleObject 原始地址
g_KeWaitForSingleObject_dwAddr = GetExportFuncOrigAddr(
"KeWaitForSingleObject" ,
FALSE ) ;
dprintf(
"KeWaitForSingleObject:0x%08lX \n" , g_KeWaitForSingleObject_dwAddr ) ;
if ( 0 == g_KeWaitForSingleObject_dwAddr ) {
g_KeWaitForSingleObject_dwAddr = (
ULONG ) GetNativeFunctionBaseAddress( L
"KeWaitForSingleObject" ) ;
}
// 得到 RtlImageNtHeader 原始地址
g_RtlImageNtHeader_addr = GetExportFuncOrigAddr(
"RtlImageNtHeader" ,
FALSE ) ;
dprintf(
"RtlImageNtHeader:0x%08lX \n" , g_RtlImageNtHeader_addr ) ;
if ( 0 == g_RtlImageNtHeader_addr ) {
g_RtlImageNtHeader_addr = (
ULONG ) GetNativeFunctionBaseAddress( L
"RtlImageNtHeader" ) ;
}
// 今天才发现,不需要释放任何驱动,随便给ZwSetSystemInformation乱传一个路径,都会进入到
// MmLoadSystemImage函数中,这就够了 by sudami 26-12-2008
// GenerateRandomStrings(5) ; // 产生随机名
// if ( NULL != szTmpName )
// {
// sprintf( ntNameFile+strlen(ntNameFile), "%s.sys", szTmpName );
// RtlInitAnsiString( &ntNameString, ntNameFile);
// RtlAnsiStringToUnicodeString( &uFileName, &ntNameString, TRUE );
//
// // 释放测试驱动到C盘根目录
// status = PutFile( uFileName.Buffer, Test_sys_data, sizeof(Test_sys_data) ) ;
//
// RtlFreeUnicodeString(&uFileName);
//
// } else {
// sprintf( ntNameFile+strlen(ntNameFile), "%s.sys", szTmpName );
// RtlInitAnsiString( &ntNameString, ntNameFile);
// RtlAnsiStringToUnicodeString( &uFileName, &ntNameString, TRUE );
//
// wcscpy( g_wszName, uFileName.Buffer ) ;
//
// RtlFreeUnicodeString(&uFileName);
//
// status = PutFile( g_wszName, Test_sys_data, sizeof(Test_sys_data) ) ;
// }
//
// if ( STATUS_SUCCESS != status )
// {
// dprintf( "PutFile(), Failed!\n" ) ;
// return ;
// }
/*++
先瞬间Inline Hook掉KeWaitForSingleObject,再迅速调用ZwSetSystemInformation加载释放到C盘
根目录的测试驱动.这样会马上执行到钩子里,在此进行栈回溯,最终定位到NtSetSystemInformation
函数调用MmLoadSystemImage的地方. 得到MmLoadSystemImage的地址.
by sudami
--*/
g_ntoskrnl_addr = g_ntoskrnl_info.ntoskrnl_addr ;
g_ntoskrnl_size = g_ntoskrnl_info.ntoskrnl_size ;
if ( !MmIsAddressValid( (
PVOID )g_KeWaitForSingleObject_dwAddr )
|| g_KeWaitForSingleObject_dwAddr < g_ntoskrnl_addr )
{
dprintf(
"g_KeWaitForSingleObject_dwAddr, INVALID,Failed!\n" ) ;
return ;
}
if ( !HookCode95( (
PVOID )g_KeWaitForSingleObject_dwAddr, (
PVOID )fake_KeWaitForSingleObject, InlineHookPre1) )
{
// 前5字节JMP.
dprintf(
"HOOK KeWaitForSingleObject, Failed!\n" ) ;
return ;
}
g_tmpThread_for_KeWaitForSingleObject = (
ULONG )KeGetCurrentThread();
load_sysfile( g_wszName ) ;
g_tmpThread_for_KeWaitForSingleObject = 0 ;
UnhookCode95( fake_KeWaitForSingleObject ) ;
}
能力值:
( LV12,RANK:1010 )
12 楼
VOID
fake_KeWaitForSingleObject (
)
/*++
Author: sudami [sudami@163.com]
Time : 2008/12/23 [23:12:2008 - 7:16]
Routine Description:
栈回溯获取MmLoadSystemImage的地址
win2k下的调用链如下:
NtSetSystemInformation->MmLoadSystemImage->MiLoadSystemImage->KeWaitForSingleObject
XP以后没有中间那个>MiLoadSystemImage了.在WIN2K里要多回溯一次
--*/
{
PULONG PEBP ;
UCHAR *pOpcode;
__asm {
pushfd
pushad
}
g_bIsW2k = g_hardOffset.bIsW2k ;
if ( g_tmpThread_for_KeWaitForSingleObject != (ULONG )KeGetCurrentThread() ) {
goto _over_for_KeWaitForSingleObject ;
}
__asm
{
// int 3
mov PEBP, ebp
mov ebx , PEBP
mov ebx , [ebx ] ; 回溯1次到 call MmLoadSystemImage 空间
cmp g_bIsW2k, 1
jnz _xp_
mov ebx , [ebx ] ; WIN2K 要多回溯一次
_xp_:
mov ebx , [ebx +4] ; ebp +4 --> ret Addr
sub ebx , 5 ; ret -5 --> 由ret Addr到Call MmLoadSystemImage
mov edi , ebx ; edi 保存Call MmLoadSystemImage处的地址
/*++
ChildEBP RetAddr Args to Child
f9df7804 805a370b 80554440 00000012 00000000 VirusAnalyse!fake_KeWaitForSingleObject+0x1f
f9df79b0 80605cb2 f9df7b38 00000000 00000000 nt!MmLoadSystemImage+0x1a9 <-- 触发成功
f9df7b60 8053da28 00000026 f9df7bf8 00000008 nt!NtSetSystemInformation+0x37c<-- 到达内核态Nt*
f9df7b60 804ff711 00000026 f9df7bf8 00000008 nt!KiFastCallEntry+0xf8 <-- 经过中转站
f9df7be4 f9ca5053 00000026 f9df7bf8 00000008 nt!ZwSetSystemInformation+0x11
f9df7c0c f9ca4d0d f9df7c2c 816f5cf8 e13b815a VirusAnalyse!load_sysfile+0xc3 <-- 我们来触发它
f9df7c50 6f526d65 535c746f 65747379 5c32336d VirusAnalyse!Get_MmLoadSystemImage_addr+0x16d
nt!NtSetSystemInformation+0x365:
80605ca5 6a01 push 1
80605ca7 57 push edi
80605ca8 57 push edi
80605ca9 8d45d8 lea eax,[ebp-28h]
80605cac 50 push eax ; 我们关心的参数,里面包含加载模块的全路径
80605cad e8b0d8f9ff call nt!MmLoadSystemImage (805a3562) <-- 回溯到这里
80605cb2 8945e4 mov dword ptr [ebp-1Ch],eax
80605cb5 3bc7 cmp eax,edi
80605cb7 0f8cb0040000 jl nt!NtSetSystemInformation+0x837 (8060616d)
80605cbd ff75ac push dword ptr [ebp-54h]
80605cc0 e82b52f2ff call nt!RtlImageNtHeader (8052aef0) <-- 校验
80605cc5 898568ffffff mov dword ptr [ebp-98h],eax
80605ccb 3bc7 cmp eax,edi
80605ccd 7512 jne nt!NtSetSystemInformation+0x3ab (80605ce1)
80605ccf ff75b4 push dword ptr [ebp-4Ch]
80605cd2 e8efd2f9ff call nt!MmUnloadSystemImage (805a2fc6)<-- 顺便可以把它的地址找到
80605cd7 b87b0000c0 mov eax,0C000007Bh
80605cdc e98c040000 jmp nt!NtSetSystemInformation+0x837 (8060616d)
--*/
cmp g_ntoskrnl_addr, 0
jz _Skip2Check_
cmp g_ntoskrnl_size, 0
jz _Skip2Check_
mov ecx , g_ntoskrnl_addr
cmp ebx , ecx
jbe short _over_for_KeWaitForSingleObject
mov edx , g_ntoskrnl_size
lea esi , [edx +ecx ]
cmp ebx , esi
jnb short _over_for_KeWaitForSingleObject
lea edx , [ebx +30h]
cmp edx , ecx
jbe short _over_for_KeWaitForSingleObject
cmp edx , esi
jnb short _over_for_KeWaitForSingleObject
_Skip2Check_: // 是在内核地址范围内,进一步验证
add ebx , 5 ; 到达 call nt!MmLoadSystemImage 的下一条指令
xor ecx , ecx ; ecx == 每次调用SizeOfCode得到的指令长度
_loop_: ; 以ebx为起始地址, 往下30h字节内搜索第一个CALL,进行校验
// cmp BYTE ptr [ecx+ebx], 0E8h // 这样找太危险了.刚好碰到一种情况,数据是E8,而不是指令
// 从而导致地址不可读,BSOD了.这里还是应该带一个反汇编引擎
push ecx ; -->保存ecx
mov pOpcode, 0
lea edx , pOpcode ; UCHAR *pOpcode; [OUT] 指令内容
lea ecx , [ecx +ebx ] ; UCHAR *cPtr ; [IN] 当前指令地址
call SizeOfCode ; Length = SizeOfCode(cPtr, &pOpcode); // fastcall类型
// 此时ecx的值变成0了.故之前需要保存下
pop ecx ; -->恢复ecx
add ecx , eax ; ecx += Length
cmp eax ,0
jnz __dddd__
inc ecx ; 若反汇编引擎计算的长度为0,则地址也要往后累加
jmp _next_
__dddd__:
mov esi , [pOpcode] ; esi得到当前指令的地址
cmp BYTE ptr [esi ], 0E8h ; 是否为CALL指令
jnz short _next_
/*++
传进来的地址明明是正确的,但一调用MmIsAddressValid就崩,很奇怪很奇怪~~
STACK_TEXT:
WARNING: Frame IP not in any known module. Following frames may be wrong.
f9defc54 535c746f 65747379 5c32336d 726b746e 0x6f526d65
f9defc7c 805767dc 815d8b10 814b7000 00000000 0x535c746f <-- 明显是EIP执行到了非法地址
f9defd4c 805768eb 000000a8 00000001 00000000 nt!IopLoadDriver+0x66c
f9defd74 80534fe6 000000a8 00000000 817bbda8 nt!IopLoadUnloadDriver+0x45
f9defdac 805c5cce f7fe2cf4 00000000 00000000 nt!ExpWorkerThread+0x100
f9defddc 805421c2 80534ee6 00000001 00000000 nt!PspSystemThreadStartup+0x34
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16
// push esi ; esi == call RtlImageNtHeader 处的地址
// call MmIsAddressValid
// cmp eax, 0
// jz _next_
//
// lea edx, [esi+1]
// push edx
// call MmIsAddressValid
// cmp eax, 0
// jz _next_
--*/
lea edx , [esi +1] ; esi == call RtlImageNtHeader 处的地址
mov eax , [edx ] ; eax == offset
lea eax , [esi +eax +5]
cmp eax , g_RtlImageNtHeader_addr
jz _ok_
_next_:
cmp ecx , 30h
jb short _loop_
jmp _over_for_KeWaitForSingleObject
_ok_:
lea ecx , [edi +1] ; edi 保存Call MmLoadSystemImage处的地址
mov edx , [ecx ]
lea eax , [edx +edi +5]
mov g_MmLoadSystemImage_addr, eax ; OK! Well Done.
_over_for_KeWaitForSingleObject:
popad // VOID 类型的申明,非naked.所以编译器会自动生成一些垃圾代码
popfd // 这里的pop是为了平衡堆栈.必须的
pop edi
pop esi
pop ebx
// pop ecx // 若只有一个局部变量,编译器可能会:push ecx,而不是sub esp,4
// 故调用wy的引擎,在fake函数中若用到局部变量,最好>=2 counts
add esp , 8 // 清掉局部变量
_emit 0x5D // pop ebp
_emit 0x68 // push xxxx
_emit 0x00
_emit 0x00
_emit 0x00
_emit 0x00
ret
}
}
能力值:
( LV12,RANK:1010 )
14 楼
BOOL
load_sysfile(
IN WCHAR * szPath
)
{
PEPROCESS proc = NULL ;
BOOL bSuccess = FALSE ;
NTSTATUS status = STATUS_SUCCESS ;
SYSTEM_LOAD_AND_CALL_IMAGE GregsImage ;
if ( NULL == szPath ) {
return 0 ;
}
// 2次调用,得到csrss.exe的EPROCESS
status = GetEProcessByName_QueryInfo( L"CSRSS.EXE" , &proc );
if ( !NT_SUCCESS(status) )
{
status = GetEProcessByName_QueryInfo( L"csrss.exe" , &proc );
if ( !NT_SUCCESS(status) ) return 0 ;
}
GregsImage.ModuleName.Buffer = ExAllocatePoolWithTag(0, 100, ' kdD');
if ( NULL == GregsImage.ModuleName.Buffer )
{
dprintf( "load_sysfile() ExAllocatePool() Failed\n" ) ;
return 0 ;
}
wcscpy( GregsImage.ModuleName.Buffer, szPath);
GregsImage.ModuleName.Length = 2 * wcslen( szPath );
GregsImage.ModuleName.MaximumLength = 100 ;
// RtlInitUnicodeString( &(GregsImage.ModuleName), szPath );
KeAttachProcess (proc); // 附着到此进程
status = ZwSetSystemInformation(
SystemLoadAndCallImage,
&GregsImage,
sizeof (SYSTEM_LOAD_AND_CALL_IMAGE) ) ;
KeDetachProcess(); // Detach
if ( !NT_SUCCESS( status ) )
{
dprintf( "load_sysfile() Failed: 0x%08lx\n" , status ) ;
ExFreePool( GregsImage.ModuleName.Buffer);
return 0 ;
}
ExFreePool( GregsImage.ModuleName.Buffer);
dprintf( "load_sysfile() ,OK!!!\n" ) ;
return 1 ;
}
能力值:
( LV13,RANK:290 )
22 楼
驱动层的HOOK,, 留贴备查,,,