windows系统在某些版本下对某些内存区域启用了写保护的功能,因为这些区域一般合法程序是不能修改其内容的,那么我们如何来写这些内存呢?
PS:1) 这些系统包括:windows xp与windows 2003
2) CPU提供写保护的功能是从486开始的
3) 一般合法程序不包括杀毒软件,因为他们在Hook SSDT中是直接改ServiceTableBase,而没有用inline的方法
我们就用SSDT做例子吧,在Hook SSDT时不用innline hook方法,我们就要修改SSDT这个系统服务描述表;而这个表是被写保护了,在ring0下也是没有写的权限。
方法一:
首先我们来看一下CR0寄存器的格式
|31|30| |18|17|16| 5|4|3|2|0|1|
|P |C | |A | |W | N|E|T|E|M|P|
|G |D | |M | |P | E|T|S|M|P|E|
我们主要注意这个WP这位,其他的请参考IA-32 Volume 3A;
WP——Write Protect,当设置为1时只提供读页权限
PE——Paging,当设置为1时提供分页
MP——Protection Enable,当设置为1时进入保护模式
所以我们只要把WP这一位设置为0时,就可以修改SSDT了
//1 关闭写保护
__asm
{
push eax
mov eax, CR0
and eax, 0FFFEFFFFh
mov CR0, eax
pop eax
}
//2 打开写保护
__asm
{
push eax
mov eax, CR0
or eax, NOT 0FFFEFFFFh
mov CR0, eax
pop eax
}
通过上面的第一组指令我们就可以正常修改SSDT,记得修改后要还原。
方法二:
此方法是盖茨提供的,在内存描述表(MDL)中描述一块内存区域,MDL包含此内存区域的起始地址,拥有者进程,字节数量以及标志。
//在ddk中的描述
typedef struct _MDL {
struct _MDL *Next;
CSHORT Size;
CSHORT MdlFlags;
struct _EPROCESS *Process;
PVOID MappedSystemVa;
PVOID StartVa;
ULONG ByteCount;
ULONG ByteOffset;
} MDL, *PMDL;
#define MDL_MAPPED_TO_SYSTEM_VA 0x0001
#define MDL_PAGES_LOCKED 0x0002
#define MDL_SOURCE_IS_NONPAGED_POOL 0x0004
#define MDL_ALLOCATED_FIXED_SIZE 0x0008
#define MDL_PARTIAL 0x0010
#define MDL_PARTIAL_HAS_BEEN_MAPPED 0x0020
#define MDL_IO_PAGE_READ 0x0040
#define MDL_WRITE_OPERATION 0x0080
#define MDL_PARENT_MAPPED_SYSTEM_VA 0x0100
#define MDL_LOCK_HELD 0x0200
#define MDL_PHYSICAL_VIEW 0x0400
#define MDL_IO_SPACE 0x0800
#define MDL_NETWORK_HEADER 0x1000
#define MDL_MAPPING_CAN_FAIL 0x2000
#define MDL_ALLOCATED_MUST_SUCCEED 0x4000
// Declarations
#pragma pack(1)
typedef struct ServiceDescriptorEntry {
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase;
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} SSDT;
#pragma pack()
__declspec(dllimport) SSDTKeServiceDescriptorTable;
PMDL g_pmdlSystemCall;
PVOID *MappedSystemCallTable;
// save old system call locations
// Map the memory into our domain to change the permissions on
// the MDL
g_pmdlSystemCall = MmCreateMdl(NULL,
KeServiceDescriptorTable.ServiceTableBase,
KeServiceDescriptorTable.NumberOfServices*4);
if(!g_pmdlSystemCall)
return STATUS_UNSUCCESSFUL;
MmBuildMdlForNonPagedPool(g_pmdlSystemCall);
// Change the flags of the MDL
g_pmdlSystemCall->MdlFlags = g_pmdlSystemCall->MdlFlags |
MDL_MAPPED_TO_SYSTEM_VA;
MappedSystemCallTable = MmMapLockedPages(g_pmdlSystemCall, KernelMode);
MappedSystemCallTable就是SSDT的地址,现在可以放心的操作它吧!用完了最好MmFreePagesFromMdl。
sinister
以下是 Mark Russinovich 很早写的一段代码,目的也是写只读内存。
NTSTATUS WriteReadOnlyMemory (char *dest, char *source, int length)
{
KSPIN_LOCK tempSpinLock;
KIRQL oldirql;
PMDL mdl;
PVOID writableAddress;
mdl = IoAllocateMdl((PVOID) dest, length, FALSE, FALSE, NULL);
if (mdl == NULL)
return STATUS_UNSUCCESSFUL;
MmBuildMdlForNonPagedPool(mdl);
MmProbeAndLockPages(mdl, KernelMode, IoWriteAccess);
writableAddress = MmMapLockedPages(mdl, KernelMode);
if (writableAddress == NULL) {
MmUnlockPages(mdl);
IoFreeMdl(mdl);
return STATUS_UNSUCCESSFUL;
}
KeInitializeSpinLock(&tempSpinLock);
KeAcquireSpinLock(&tempSpinLock, &oldirql);
RtlCopyMemory(writableAddress, source, length);
KeReleaseSpinLock(&tempSpinLock, oldirql);
MmUnmapLockedPages(writableAddress, mdl);
MmUnlockPages(mdl);
IoFreeMdl(mdl);
return STATUS_SUCCESS;
}
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1740349