SwapContext函数调用频繁,我们就hook它
思路
1.首先我们需要找到SwapContext函数的地址 可通过特征码搜索,也可以通过windbg直接查看
2.需要一个执行我们xx操作的函数
3.需要关闭写保护,因为我们要操作内核文件
4.操作完我们的函数后需要返回SwapContext中正常执行
去除写保护
VOID RemoveWProtect()
{
__asm
{
cli
push eax
mov eax,cr0
and eax,not 0x10000
mov cr0,eax
pop eax
}
return;
}
VOID SetWProtect()
{
__asm
{
push eax
mov eax, cr0
or eax, 0x10000
mov cr0, eax
pop eax
sti
}
return;
}
我们自己的函数
void _declspec(naked) HookSwapContextFunction()
{
_asm
{
mov dword ptr[CurrentThread], edi
mov dword ptr[NextThread], esi
}
_asm
{
pushad
pushfd
}
DbgPrint("当前线程为:%x\t\t下一个线程为:%x\n", CurrentThread, NextThread);
_asm
{
popfd
popad
mov byte ptr es:[esi + 2Dh],0x2
ret
}
}
特征码搜索SwapContext函数地址
//特征码搜索SwapContext函数的地址
UINT32 shellcode[] = {
0xc626c90a,0x9c022d46,0xbb830b8b,0x00000994,
0x850f5100,0x00000134,0x310c3d83,0x0f008056,
0x0000fe85,0xc5200f00,0x4e8ad58b,0x504b882c
};
UINT32 shellcodeLength = sizeof(shellcode);
UINT32 base = NULL;
UINT32 baseSize = NULL;
UNICODE_STRING baseName;
LIST_ENTRY* current_entry = (LIST_ENTRY*)pDriver->DriverSection;
UNICODE_STRING ntName;
RtlInitUnicodeString(&ntName, L"ntoskrnl.exe");
do {
base = *(UINT32*)((UINT32)current_entry + 0x18);
baseSize = *(UINT32*)((UINT32)current_entry + 0x20);
baseName = *(PUNICODE_STRING)((UINT32)current_entry + 0x2c);
if (RtlCompareUnicodeString(&ntName, &baseName, TRUE) == 0)
{
for (int i = base; i < base + baseSize - shellcodeLength; i++)
{
if (RtlCompareMemory(shellcode, i, shellcodeLength) == shellcodeLength)
{
SwapContext = i + 2;
break;
}
}
break;
}
current_entry = current_entry->Blink;
} while (1);
//判断函数地址找到没有
if (SwapContext == NULL)
{
DbgPrint("SwapContext func addr is not find \n");
return STATUS_ABANDONED;
}
驱动代码
#include <ntifs.h>
#include <ntddk.h>
//操作码:0x0-0x7FF 被保留,0x800-0xFFF 可用
#define ADDR_HOOK CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define ADDR_UNHOOK CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS)
//设备对象
PDEVICE_OBJECT devObj;
//符号链接
UNICODE_STRING symbolLink;
//SwapContext的地址
UINT32 SwapContext = NULL;
//当前线程
UINT32 CurrentThread = NULL;
//下一个线程
UINT32 NextThread = NULL;
//原字节
UINT32 lowData,highData,oldLowData, oldHighData;
NTSTATUS DEVICE_CONTROL_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS DEVICE_CREATE_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS DEVICE_CLOSE_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp);
VOID RemoveWProtect()
{
__asm
{
cli
push eax
mov eax,cr0
and eax,not 0x10000
mov cr0,eax
pop eax
}
return;
}
VOID SetWProtect()
{
__asm
{
push eax
mov eax, cr0
or eax, 0x10000
mov cr0, eax
pop eax
sti
}
return;
}
void _declspec(naked) HookSwapContextFunction()
{
_asm
{
mov dword ptr[CurrentThread], edi
mov dword ptr[NextThread], esi
}
_asm
{
pushad
pushfd
}
DbgPrint("当前线程为:%x\t\t下一个线程为:%x\n", CurrentThread, NextThread);
_asm
{
popfd
popad
mov byte ptr es:[esi + 2Dh],0x2
ret
}
}
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
//删除符号链接
IoDeleteSymbolicLink(&symbolLink);
//删除设备
IoDeleteDevice(devObj);
DbgPrint("卸载成功!!!\n");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath)
{
try {
//unload
pDriver->DriverUnload = DriverUnload;
//创建设备和3环通信
UNICODE_STRING deviceName;
RtlInitUnicodeString(&deviceName,L"\\Device\\firstDevice");
NTSTATUS status = IoCreateDevice(
pDriver,
0,
&deviceName,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&devObj
);
DbgPrint("创建设备 : %d~~\n", status);
//创建符号链接 (3环需要这个符号链接才可以找到)
RtlInitUnicodeString(&symbolLink,L"\\??\\HOOKTOOL");
IoCreateSymbolicLink(&symbolLink,&deviceName);
//设置通信方式
pDriver->Flags |= DO_BUFFERED_IO;
//设置派遣函数
pDriver->MajorFunction[IRP_MJ_CREATE] = DEVICE_CREATE_Dispatch;
pDriver->MajorFunction[IRP_MJ_CLOSE] = DEVICE_CLOSE_Dispatch;
pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DEVICE_CONTROL_Dispatch;
//特征码搜索SwapContext函数的地址
UINT32 shellcode[] = {
0xc626c90a,0x9c022d46,0xbb830b8b,0x00000994,
0x850f5100,0x00000134,0x310c3d83,0x0f008056,
0x0000fe85,0xc5200f00,0x4e8ad58b,0x504b882c
};
UINT32 shellcodeLength = sizeof(shellcode);
UINT32 base = NULL;
UINT32 baseSize = NULL;
UNICODE_STRING baseName;
LIST_ENTRY* current_entry = (LIST_ENTRY*)pDriver->DriverSection;
UNICODE_STRING ntName;
RtlInitUnicodeString(&ntName, L"ntoskrnl.exe");
do {
base = *(UINT32*)((UINT32)current_entry + 0x18);
baseSize = *(UINT32*)((UINT32)current_entry + 0x20);
baseName = *(PUNICODE_STRING)((UINT32)current_entry + 0x2c);
if (RtlCompareUnicodeString(&ntName, &baseName, TRUE) == 0)
{
for (int i = base; i < base + baseSize - shellcodeLength; i++)
{
if (RtlCompareMemory(shellcode, i, shellcodeLength) == shellcodeLength)
{
SwapContext = i + 2;
break;
}
}
break;
}
current_entry = current_entry->Blink;
} while (1);
//判断函数地址找到没有
if (SwapContext == NULL)
{
DbgPrint("SwapContext func addr is not find \n");
return STATUS_ABANDONED;
}
DbgPrint("dllbase : %08x\n", base);
DbgPrint("SwapContext func addr is : %08x\n", SwapContext);
UINT32 callAddr = (UINT32)HookSwapContextFunction - SwapContext - 5;
//这里不懂的,可以去看看E8 call 的硬编码知识,不难
lowData = (callAddr << 8) + 0xE8;
highData = (callAddr >> 24) + 0x0b8b9c00;
} __except(EXCEPTION_EXECUTE_HANDLER) {
DbgPrint("run error~~\n");
return STATUS_SUCCESS;
}
return STATUS_SUCCESS;
}
NTSTATUS DEVICE_CONTROL_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
//NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
NTSTATUS status = STATUS_SUCCESS;
//获取PIRP的数据
PIO_STACK_LOCATION psLocation = IoGetCurrentIrpStackLocation(pIrp);
//获取控制码
ULONG code = psLocation->Parameters.DeviceIoControl.IoControlCode;
//获取缓冲区地址(输入和输出都是同一个)
PVOID bufferAddress = pIrp->AssociatedIrp.SystemBuffer;
//3环发送的数据字节数
ULONG threeLength = psLocation->Parameters.DeviceIoControl.InputBufferLength;
//0环发送的数据字节数
ULONG zeroLength = psLocation->Parameters.DeviceIoControl.OutputBufferLength;
//PEPROCESS
//PETHREAD peprocess;
PEPROCESS peprocess1;
PEPROCESS peprocess2;
switch (code)
{
case ADDR_HOOK :
//去除写保护
RemoveWProtect();
__asm
{
pushad
pushfd
mov edi, SwapContext
mov edx, [edi+4]
mov eax, [edi]
mov dword ptr oldHighData, edx
mov dword ptr oldLowData, eax
mov ecx, highData
mov ebx, lowData
lock CMPXCHG8B qword ptr [edi]
/*
* cmpxchg8b mem64 指令的工作如下:
比较 mem64 和 EDX:EAX
如果相等,那么把 ECX:EBX 存储到 mem64
如果不相等,那么把 mem64 存储到 EDX:EAX
我们要一次性改8字节内存,用的是相等的情况,先把要写入的内容放到 ECX:EBX ,然后调用 cmpxchg8b 指令即可
*/
popfd
popad
}
//设置写保护
SetWProtect();
DbgPrint("Hook success ~~\n");
break;
case ADDR_UNHOOK :
//去除写保护
RemoveWProtect();
__asm
{
pushad
pushfd
mov edi, SwapContext
mov edx, [edi + 4]
mov eax, [edi]
mov ecx, oldHighData
mov ebx, oldLowData
lock CMPXCHG8B qword ptr[edi]
popfd
popad
}
//设置写保护
SetWProtect();
DbgPrint("UnHook success ~~\n");
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
DbgPrint("3环发送的数据长度 %d~~\n", threeLength);
DbgPrint("0环发送的数据长度 %d~~\n", zeroLength);
//设置返回状态,默认是失败的哦
pIrp->IoStatus.Status = status;
//返回给3环多少字节数据,没有填0
pIrp->IoStatus.Information = 8;
//
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DEVICE_CREATE_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("CREATE SUCCESS~~\n");
//设置返回状态
pIrp->IoStatus.Status = STATUS_SUCCESS;
//返回给3环多少字节数据,没有填0
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp,IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DEVICE_CLOSE_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("CLOSE SUCCESS~~\n");
//设置返回状态
pIrp->IoStatus.Status = STATUS_SUCCESS;
//返回给3环多少字节数据,没有填0
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
3环代码
#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
#include <winioctl.h>
#define symbolLinkName L"\\\\.\\HOOKTOOL"
//操作码:0x0-0x7FF 被保留,0x800-0xFFF 可用
#define ADDR_HOOK CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define ADDR_UNHOOK CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS)
int main(int argc, char* argv[])
{
//create device link
HANDLE device = CreateFileW(
symbolLinkName,//创建或打开的文件或设备的名称
GENERIC_READ | GENERIC_WRITE,//请求对文件或设备的访问权限
0,//文件或设备请求的共享模式,参数为零且 CreateFile 成功,则文件或设备无法共享,并且无法在文件或设备的句柄关闭之前再次打开
0,//确定返回的句柄是否可以由子进程继承
OPEN_EXISTING,//仅当文件或设备存在时,才打开该文件或设备
FILE_ATTRIBUTE_NORMAL,
NULL);
if (device == INVALID_HANDLE_VALUE)
{
printf("device open error ......");
system("pause");
return 0;
}
DWORD pid;
DWORD outBuffer;
DWORD lbret;//actually out buffer size
if (DeviceIoControl(device,ADDR_HOOK,&pid,sizeof(pid),&outBuffer,sizeof(outBuffer),&lbret,NULL))
{
printf("ADDR_HOOK success , please test......\n");
}
system("pause");
if (DeviceIoControl(device,ADDR_UNHOOK,&pid,sizeof(pid),&outBuffer,sizeof(outBuffer),&lbret,NULL))
{
printf("ADDR_UNHOOK success , please test......\n");
}
system("pause");
CloseHandle(device);
return 0;
}