新手初来乍到,老鸟飞过,菜鸟共同学习……
源码见附件
部分源码及分析贴出……
#include "xde.h"
#include <ntddk.h>
#include <WinDef.h>
unsigned long g_uCr0;
unsigned long Addr_ZwCreateSection,Addr_ZwCreateSection_Body;
//模仿天书InlineHook,填写绝对跳转指令
unsigned long my_address; // 我要跳转的地址
unsigned long length;
unsigned char code[12]; //使用一个比较大的空间来容纳未知指令
struct xde_instr myjmp = {0};
static unsigned char * g_new_address =NULL;
static unsigned long total_length = 0;
//关闭内存写保护 WriteProtectOff
void WriteProtectOff()
{
unsigned long uAttr;
_asm
{
push eax;
mov eax, cr0;
mov uAttr, eax;
and eax, 0FFFEFFFFh; // CR0 16 BIT = 0
mov cr0, eax;
pop eax;
cli
};
g_uCr0 = uAttr; //保存原有的 CRO 属性
}
//开启内存写保护
VOID WriteProtectOn()
{
_asm
{
sti
push eax;
mov eax, g_uCr0; //恢復原有 CR0 屬性
mov cr0, eax;
pop eax;
};
}
//由函数名 获得 函数地址
unsigned long GetFunctionAddr( IN PCWSTR FunctionName)
{
UNICODE_STRING UniCodeFunctionName;
RtlInitUnicodeString( &UniCodeFunctionName, FunctionName );
return (unsigned long)MmGetSystemRoutineAddress( &UniCodeFunctionName );
}
//Hook函数所作的工作
NTSTATUS DetourZwCreateSection( OUT PHANDLE SectionHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN PLARGE_INTEGER MaximumSize OPTIONAL, IN ULONG SectionPageProtection, IN ULONG AllocationAttributes, IN HANDLE FileHandle OPTIONAL )
{
KdPrint(("\n\nSectionHandle:%X\nFileHandle:%X\n\n",SectionHandle,FileHandle));
return STATUS_SUCCESS;
}
//中继函数
static __declspec(naked) MyZwCreateSectionRelay( OUT PHANDLE SectionHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN PLARGE_INTEGER MaximumSize OPTIONAL, IN ULONG SectionPageProtection, IN ULONG AllocationAttributes, IN HANDLE FileHandle OPTIONAL )
{//__declspec(naked)表示不希望编译器帮我们将参数压栈,所以这里需要我们手动将参数压栈
if (g_new_address == NULL)
{
__asm
{
//这里的操作是将参数压入栈空间
push ebp
mov ebp,esp
push [ebp + 1ch]
push [ebp + 18h]
push [ebp + 14h]
push [ebp + 10h]
push [ebp + 0ch]
push [ebp + 8h]
push [ebp + 4h]
//上面的代码为参数压栈操作
//下面这两句的代码是将下面的函数内指令地址(old_codes)保存到全局变量(g_new_address)中,
//以便我们向这里拷贝我们想要执行的指令,在这里是ZwCreateSection开头的total_length个字节的指令
push old_codes
pop g_new_address
//下面的代码用于参数的弹出操作
mov edx, [ebp + 4h]
pop edx
mov edx, [ebp + 8h]
pop edx
mov edx, [ebp + 0ch]
pop edx
mov edx, [ebp + 10h]
pop edx
mov edx, [ebp + 14h]
pop edx
mov edx, [ebp + 18h]
pop edx
mov edx, [ebp + 1ch]
pop edx
mov esp, ebp
pop ebp
ret 0x1c
}
}
else
{
__asm
{
//这里的操作是将参数压入栈空间
push ebp
mov ebp,esp
push [ebp + 1ch]
push [ebp + 18h]
push [ebp + 14h]
push [ebp + 10h]
push [ebp + 0ch]
push [ebp + 8h]
push [ebp + 4h]
//上面的代码为参数压栈操作
}
//这里调用我们的挂钩函数,实现我们想要的操作
DetourZwCreateSection(SectionHandle,DesiredAccess,ObjectAttributes,MaximumSize,SectionPageProtection,AllocationAttributes,FileHandle);
_asm
{
mov edx, [ebp + 4h]
pop edx
mov edx, [ebp + 8h]
pop edx
mov edx, [ebp + 0ch]
pop edx
mov edx, [ebp + 10h]
pop edx
mov edx, [ebp + 14h]
pop edx
mov edx, [ebp + 18h]
pop edx
mov edx, [ebp + 1ch]
pop edx
mov esp, ebp
pop ebp
//上面这些是为了堆栈平衡
old_codes:
//在这里写入足够多的nop,形成一片空间来容纳拷贝过来的旧代码
nop //1
nop //2
nop //3
nop //4
nop //5
nop //6
nop //7
nop //8
nop //9
nop //10
nop //11
nop //12
nop //13
nop //14
nop //15
//跳转到ZwCreateSection+total_length的位置继续执行
jmp Addr_ZwCreateSection_Body
};
}
}
//安装HOOK的函数
NTSTATUS HookZwCreateSection()
{
KIRQL Irql;
unsigned long length;
struct xde_instr code_instr = { 0 } ;
Addr_ZwCreateSection_Body = GetFunctionAddr(L"ZwCreateSection"); //获得ZwCreateSection的开始地址;
Addr_ZwCreateSection = Addr_ZwCreateSection_Body;
while (total_length < 7)
{
//反汇编一条指令
length = xde_disasm((unsigned char*)Addr_ZwCreateSection_Body,&code_instr);
if (length == 0) //如果有指令解析失败,就直接返回失败
{
return STATUS_FILE_INVALID;
}
total_length += length; //计算已经反汇编的指令的总长度
Addr_ZwCreateSection_Body += length;//解析地址移动
}
//开始InlineHook
//关闭内存写保护,在下面要有写操作
WriteProtectOff();
//提升IRQL中断级到DISPATCH_LEVEL,防止线程切换
Irql = KeRaiseIrqlToDpcLevel();
if (g_new_address != NULL)
{
//保存要Hook的内核函数前total_length个字节的内容
RtlCopyMemory(g_new_address,(unsigned char *)Addr_ZwCreateSection,total_length);
}
else
{
MyZwCreateSectionRelay(NULL,0,NULL,NULL,0,0,NULL);
//保存要Hook的内核函数前total_length个字节的内容
RtlCopyMemory(g_new_address,(unsigned char *)Addr_ZwCreateSection,total_length);
}
//向要Hook的内核函数的开头total_length个字节写入JMP指令,使其跳转到新定义的钩子函数中
//RtlCopyMemory((unsigned char *)Addr_ZwCreateSection,JmpAddress,total_length);
RtlCopyMemory((unsigned char *)Addr_ZwCreateSection,code,total_length);
//恢复IRQL中断级到
KeLowerIrql(Irql);
//开启写保护
WriteProtectOn();
return STATUS_SUCCESS;
}
//卸载Hook函数
void UnHookZwCreateSection()
{
//把开头五个字节再写会到原函数
KIRQL Irql;
//关闭写保护
WriteProtectOff();
//提升中断级
Irql = KeRaiseIrqlToDpcLevel();
//还原原来的五个字节
RtlCopyMemory((unsigned char *)Addr_ZwCreateSection,g_new_address,total_length);
//降低中断级
KeLowerIrql(Irql);
//打开写保护
WriteProtectOn();
}
//卸载函数
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
DbgPrint("My Driver Unloaded!");
UnHookZwCreateSection();
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING uniRegistry)
{
pDriverObject->DriverUnload = OnUnload;
my_address = (unsigned long)MyZwCreateSectionRelay; //我要跳转的地址,即中继函数的地址
myjmp.opcode = 0xea; //填写指令码eah ,对应的汇编码为jmp
myjmp.addrsize = 4; //地址长度为4个字节(32位)
myjmp.datasize = 2; //选择子作为立即数,长度为2个字节
myjmp.addr_l[0] = my_address;//填写我要跳转的地址
myjmp.data_s[0] = 8; //选择子,内核代码段选择子为8
length = xde_asm(code,&myjmp);//将绝对跳转指令写到code变量中
HookZwCreateSection();
return STATUS_SUCCESS;
}
环境 vs2008+ddkwizard+VMware 7.1+WinXP sp3
[课程]Linux pwn 探索篇!
上传的附件: