一、研发背景

这是中级上保护模式和驱动开发章节的综合练习。程序可以监视系统API调用,和三期的那个函数调用监视器不同,三期的只能HOOK本进程的API,而这个项目可以监视所有进程。

项目源码:https://github.com/Kwansy98/MsgBoxAWatcher/tree/main

编译环境是 VS2010 + WDK7600,运行环境是32位XP。

每个人做这个作业时都有不同的写法,本文仅供参考,有什么疑问或建议,欢迎留言或私信,也可以加q群 1046088090 讨论。

二、项目展示

程序输出非常简单,只要有进程调用了 MessageBoxA,监控程序就会打印。

这里并没有把字符串也打印出来,原因有二:
一是要切换进程CR3读字符串,再发给监控程序,工作量大(主要原因);
二是扩展性的考虑,我一开始是想监控所有user32.dll的导出函数的,所以直接传参数的值(不经转化)是最方便的。

三、涉及的技术

以下是老师在课堂上提到的,可能用到的主要的技术:

//1、自己写代码加载,卸载驱动程序
//2、段页的知识;绕写拷贝
//3、写HOOK
//4、shellcode

老师说要用shellcode,是因为正常的做法是向某个系统dll注入shellcode,让MessageBoxA执行时跳过去的,这是典型的Inline hook操作。

我的项目中没有用这种方法,因此也就没有涉及shellcode,以下是我在实际开发中使用的技术:

  • 驱动编程
  • 0环-3环通信常规方式(设备读写)
  • 修改PTE过写拷贝
  • 调用门提权(我写了一套API实现以系统权限调用用户程序里的裸函数,支持传参)
  • 中断门HOOK(这个名字是我自己想的)
  • 设备扩展内存
  • 双端链表模拟队列

四、3环监控程序详解

项目源码:https://github.com/Kwansy98/MsgBoxAWatcher/tree/main

3环程序是一个控制台程序,执行流程是非常清晰简单的,只做了几件事。

加载驱动

调用封装函数 LoadDriver ,加载驱动,通过驱动,我们可以操作高2G的内存。

LoadDriver(DRIVER_NAME, DRIVER_PATH)

这个函数没什么好说的,里面就是调用Windows API 加载驱动。

过写拷贝

BypassApiWriteCopy();

写拷贝我在之前的博客里介绍过:https://blog.csdn.net/Kwansy/article/details/109232856

过写拷贝最简单的办法就是将线性地址的 R/W 位改成1,我就是这么做的,我把这步放在3环做了。为了操作页表,我的代码必须有系统权限,我的做法是用调用门提权,然后调用裸函数。

为了提高代码复用,我写了一套API,可以很方便地以0环权限调用应用程序里的裸函数。

这部分我觉得是我在这个项目里写的最漂亮的代码了= =

// 以0环权限调用某个裸函数,支持传参
BOOL CallInRing0(PVOID pFuncion, PDWORD pParam, DWORD nParam);

有了这个函数,我们就可以很方便地过写拷贝了。

// 过写拷贝
void BypassApiWriteCopy()
{// MessageBoxA 挂物理页,不这样操作,MessageBoxA的PTE可能是无效的__asm{mov eax, dword ptr ds:[MessageBoxA];mov eax,[eax];}// MessageBoxA过写拷贝   DWORD pParam[3];pParam[0] = (DWORD)MessageBoxA;pParam[1] = (DWORD)GetPDE(pParam[0]);pParam[2] = (DWORD)GetPTE(pParam[0]);CallInRing0(BypassApiWriteCopyNaked, pParam,3);
}

HOOK MessageBoxA

HOOK这步我也放在3环完成。分为两步:

  1. 首先在0环构造一个提权中断门,返回中断号,例如 0x20
  2. 修改MessageBoxA头两字节的 mov edi,edi 指令,改成 INT 0x20,刚好也是2字节。

当调用 MessageBoxA,就会触发 0x20 中断,跳转到0环提供的监控函数里执行。这部分我留到待会介绍驱动的时候说。

循环打印调用记录

HOOK 完成后,主函数进入一个死循环,不断地从驱动里读取 MessageBoxA 调用记录,并打印到控制台。当用户输入Q键,退出循环。

收尾工作

取消 HOOK,卸载驱动,打印提示程序结束的信息。

// 取消HOOK
((PUSHORT)MessageBoxA)[0] = 0xff8b;
UnLoadDriver(DRIVER_NAME);
printf("敲任意按键退出程序.\n");

五、驱动程序详解

项目源码:https://github.com/Kwansy98/MsgBoxAWatcher/tree/main

驱动是被动响应3环监控程序的,主要功能如下:

  • 为3环构造提权调用门,返回调用门描述符
  • 为3环构造提权中断门,返回中断号
  • HOOK API后,通过提权中断门跳转到监控函数
  • 返回API调用记录给3环

重点说一下监控函数,监控函数有两个,一个是触发中断跳转进去的裸函数,裸函数内部调用了另一个函数(裸函数里写C代码不方便)

// User32.dll 导出函数的钩子函数
// 调用方式:修改API函数头2字节,使API函数触发中断,通过提权中断门调用本函数
void __declspec(naked) User32ApiSpyNaked()
{__asm{pushad; // esp - 0x20pushfd; // esp - 0x04mov eax,[esp + 0x24];     mov ecx,[esp + 0x24 + 0x0C];push eax; // EIP3push ecx; // ESP3call User32ApiSpy;popfd;popad;iretd;}
}// 此处需要完成的工作:读取3环EIP,判断API来源,读取3环ESP,获取参数,传给3环控制程序
void __stdcall User32ApiSpy(UINT32 ESP3, UINT32 EIP3)
{UINT32 ApiAddress;// EIP3-0x02是API的地址// ESP3是3环的ESP,可以用来读参数__asm push fs;ApiAddress = EIP3 - 2;//DbgPrint("ESP3: %08x, API: %08x\n", ESP3, ApiAddress);// 判断API地址if (ApiAddress == 0x77d507ea){PAPICALLRECORD pApiCallRecord = NULL;// 添加调用记录到队列,监视进程通过IRP消息读取队列pApiCallRecord = (PAPICALLRECORD)ExAllocatePool(PagedPool,sizeof(APICALLRECORD));pApiCallRecord->nParam = 4;pApiCallRecord->pApiAddress = ApiAddress;pApiCallRecord->Param[0] = ((PUINT32)ESP3)[1];pApiCallRecord->Param[1] = ((PUINT32)ESP3)[2];pApiCallRecord->Param[2] = ((PUINT32)ESP3)[3];pApiCallRecord->Param[3] = ((PUINT32)ESP3)[4];PushApiCallQueue(&g_ApiCallRecordQueue, (PAPICALLRECORD)pApiCallRecord);}__asm pop fs;
}

可以看到,监控函数的工作就是记录API的地址(判断是哪个API,MessageBoxA还是别的什么函数),以及记录参数,这些信息都会存储到一个队列里,然后3环监控程序请求数据的时候,就会从队列里弹出数据返回给3环监控程序。

为了存储调用记录,我定义了一个结构体和一套队列操作的API,详见代码,此处不细说。

// API调用记录
typedef struct _APICALLRECORD
{LIST_ENTRY ApiCallRecordList; // 链表UINT32 pApiAddress; // API函数地址UINT32 nParam; // 参数个数UINT32 Param[32]; // 参数列表
} APICALLRECORD, *PAPICALLRECORD;void InitApiCallQueue(IN PAPICALLRECORD QueueHead);
void PushApiCallQueue(IN PAPICALLRECORD QueueHead, IN PAPICALLRECORD pApiCallRecord);
void PopApiCallQueue(IN PAPICALLRECORD QueueHead, OUT PAPICALLRECORD * pApiCallRecord);
UINT32 GetCountApiCallQueue(IN PAPICALLRECORD QueueHead);
void FreeApiCallQueue(IN PAPICALLRECORD QueueHead);

六、开发中遇到的坑点和难点

坑点

1、DebugPrint与push fs
在裸函数内调用 DebugPrint,需要保存FS

__asm push fs;
DbgPrint(...);
__asm pop fs;

2、驱动中使用全局变量和static变量
全局变量貌似可以用,但是static就建议不要用了,调用驱动的函数时,全局变量和static变量的地址会变,还会重新初始化,这方面细节我也不是很清楚。建议使用设备扩展内存代替。

3、MessageBoxA 线性地址 PTE 无效的处理方法
过写拷贝前,先读一下 MessageBoxA 里面的数据,否则 PTE 是无效的。

难点

1、3环提权调用裸函数框架
这部分主要是传参不好写,我用了一个DWORD数组表示参数,用一个DWORD表示参数个数,然后循环push参数。详细请看这个函数:

// 以0环权限调用某个裸函数,支持传参
BOOL CallInRing0(PVOID pFuncion, PDWORD pParam, DWORD nParam)

2、遍历GDT/IDT
你也可以不遍历,比如说 0x8003f048 和 0x8003f500 这两个就是无效的GDT和 IDT ,可以直接用,但是不保证每次都好使。遍历的代码在这两个函数里:

// 构造提权中断门,返回中断号
USHORT SetIntGate(UINT32 pFuncion);
// 构造提权调用门,返回调用门选择子
USHORT SetCallGate(UINT32 pFunction, UINT32 nParam);

3、驱动中实现队列
这个队列是用来存API调用记录的,方法比较多,我这里用双向链表模拟队列实现,详细请看驱动部分的代码。

4、设备扩展内存

// 创建设备
status = IoCreateDevice(pDriver,DeviceExtendSize,&DeviceName,FILE_DEVICE_UNKNOWN,FILE_DEVICE_SECURE_OPEN,FALSE,&pDeviceObj);

创建设备时,第二个参数是设备扩展内存的大小,你可以在这申请一块非分页内存,只要设备还在,就都可以用,可以用它代替全局变量。

七、完整代码

虽然已经上传了github,但是还是在这里也留一个备份吧。

驱动

#include <ntifs.h>
#include <wdm.h>#define DEVICE_NAME L"\\Device\\MsgBoxAWatcherDriverDev"
#define DRIVER_LINK L"\\??\\MsgBoxAWatcherDriverLnk"// 申请了4KB设备扩展内存,用于替代全局变量
// 0-3字节:调用门描述符地址(GDT)
// 4-7字节:中断门描述符地址(IDT)
#define DeviceExtendSize 0x1000// 3环发 IRP_MJ_DEVICE_CONTROL 的操作编号
#define OPER_CALL_GATE_R0 CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define OPER_HOOK CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define OPER_GET_APICALLRECORD CTL_CODE(FILE_DEVICE_UNKNOWN,0x802,METHOD_BUFFERED,FILE_ANY_ACCESS)// 结构声明
typedef struct _LDR_DATA_TABLE_ENTRY
{LIST_ENTRY InLoadOrderLinks;LIST_ENTRY InMemoryOrderLinks;LIST_ENTRY InInitializationOrderLinks;PVOID DllBase;PVOID EntryPoint;UINT32 SizeOfImage;UNICODE_STRING FullDllName;UNICODE_STRING BaseDllName;UINT32 Flags;USHORT LoadCount;USHORT TlsIndex;LIST_ENTRY HashLinks;PVOID SectionPointer;UINT32 CheckSum;UINT32 TimeDateStamp;PVOID LoadedImports;PVOID EntryPointActivationContext;PVOID PatchInformation;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;// API调用记录
typedef struct _APICALLRECORD
{LIST_ENTRY ApiCallRecordList; // 链表UINT32 pApiAddress; // API函数地址UINT32 nParam; // 参数个数UINT32 Param[32]; // 参数列表
} APICALLRECORD, *PAPICALLRECORD;// 全局变量
PDEVICE_OBJECT g_pDevObj = NULL; // 自定义设备,用于和3环通信
APICALLRECORD g_ApiCallRecordQueue = { 0 }; // API调用记录队列,不要直接操作该链表,使用程序提供的API// 函数声明
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING RegPath);
VOID DriverUnload(PDRIVER_OBJECT pDriver);
NTSTATUS IrpCreateProc(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS IrpCloseProc(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS IrpDeviceControlProc(PDEVICE_OBJECT pDevObj, PIRP pIrp);
UINT32 *GetPDE(UINT32 addr);
UINT32 *GetPTE(UINT32 addr);
USHORT SetCallGate(UINT32 pFunction, UINT32 nParam);
USHORT SetIntGate(UINT32 pFuncion);
void User32ApiSpyNaked();
void __stdcall User32ApiSpy(UINT32 ESP3, UINT32 EIP3);
void InitApiCallQueue(IN PAPICALLRECORD QueueHead);
void PushApiCallQueue(IN PAPICALLRECORD QueueHead, IN PAPICALLRECORD pApiCallRecord);
void PopApiCallQueue(IN PAPICALLRECORD QueueHead, OUT PAPICALLRECORD * pApiCallRecord);
UINT32 GetCountApiCallQueue(IN PAPICALLRECORD QueueHead);
void FreeApiCallQueue(IN PAPICALLRECORD QueueHead);// 入口函数
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING RegPath){NTSTATUS status;ULONG uIndex = 0;PDEVICE_OBJECT pDeviceObj = NULL; // 设备对象指针UNICODE_STRING DeviceName; // 设备名,0环用UNICODE_STRING SymbolicLinkName; // 符号链接名,3环用// 初始化调用记录队列InitApiCallQueue(&g_ApiCallRecordQueue);// 创建设备名称RtlInitUnicodeString(&DeviceName,DEVICE_NAME);// 创建设备  status = IoCreateDevice(pDriver,DeviceExtendSize,&DeviceName,FILE_DEVICE_UNKNOWN,FILE_DEVICE_SECURE_OPEN,FALSE,&pDeviceObj);if (status != STATUS_SUCCESS){IoDeleteDevice(pDeviceObj);DbgPrint("创建设备失败.\n");return status;}// 全局变量依赖于设备扩展内存// 初始化全局设备指针g_pDevObj = pDeviceObj;  // 初始化设备扩展数据memset(pDeviceObj->DeviceExtension,0,DeviceExtendSize);//DbgPrint("创建设备成功.\n");// 设置交互数据的方式pDeviceObj->Flags |= DO_BUFFERED_IO;// 创建符号链接RtlInitUnicodeString(&SymbolicLinkName, DRIVER_LINK);IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);// 设置分发函数pDriver->MajorFunction[IRP_MJ_CREATE] = IrpCreateProc;pDriver->MajorFunction[IRP_MJ_CLOSE] = IrpCloseProc;pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IrpDeviceControlProc;// 设置卸载函数pDriver->DriverUnload = DriverUnload;return STATUS_SUCCESS;
}// 卸载驱动
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{UNICODE_STRING SymbolicLinkName;// 删除GDT表项中的调用门memset((PVOID)((PUINT32)(pDriver->DeviceObject->DeviceExtension))[0],0,8);// 删除IDT表中的中断门memset((PVOID)(((PUINT32)(pDriver->DeviceObject->DeviceExtension))[1]),0,8);// 释放队列内存//DbgPrint("队列长度:%d\n", GetCountApiCallQueue(&g_ApiCallRecordQueue));FreeApiCallQueue(&g_ApiCallRecordQueue);//DbgPrint("队列长度:%d\n", GetCountApiCallQueue(&g_ApiCallRecordQueue));// 删除符号链接,删除设备RtlInitUnicodeString(&SymbolicLinkName, DRIVER_LINK);IoDeleteSymbolicLink(&SymbolicLinkName);IoDeleteDevice(pDriver->DeviceObject);DbgPrint("驱动卸载成功\n");
}// 不设置这个函数,则Ring3调用CreateFile会返回1
// IRP_MJ_CREATE 处理函数
NTSTATUS IrpCreateProc(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{//DbgPrint("应用层连接设备.\n");// 返回状态如果不设置,Ring3返回值是失败pIrp->IoStatus.Status = STATUS_SUCCESS;pIrp->IoStatus.Information = 0;IoCompleteRequest(pIrp, IO_NO_INCREMENT);return STATUS_SUCCESS;
}// IRP_MJ_CLOSE 处理函数
NTSTATUS IrpCloseProc(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{//DbgPrint("应用层断开连接设备.\n");// 返回状态如果不设置,Ring3返回值是失败pIrp->IoStatus.Status = STATUS_SUCCESS;pIrp->IoStatus.Information = 0;IoCompleteRequest(pIrp, IO_NO_INCREMENT);return STATUS_SUCCESS;
}// IRP_MJ_DEVICE_CONTROL 处理函数
NTSTATUS IrpDeviceControlProc(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;PIO_STACK_LOCATION pIrpStack;ULONG uIoControlCode;PVOID pIoBuffer;ULONG uInLength;ULONG uOutLength;// 获取IRP数据pIrpStack = IoGetCurrentIrpStackLocation(pIrp);// 获取控制码uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;// 获取缓冲区地址(输入输出是同一个)pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;// Ring3 发送数据的长度uInLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;// Ring0 发送数据的长度uOutLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;switch (uIoControlCode){case OPER_CALL_GATE_R0:{UINT32 pFunction; // 3环函数指针UINT32 nParam; // 参数个数// 给3环传进来的函数指针设置一个调用门pFunction = ((PUINT32)pIoBuffer)[0];nParam = ((PUINT32)pIoBuffer)[1];// 设置状态,返回数据((PUSHORT)pIoBuffer)[0] = SetCallGate(pFunction, nParam); // 返回调用门选择子pIrp->IoStatus.Information = 2; // 返回给3环的数据量status = STATUS_SUCCESS;break;}case OPER_HOOK:{// 返回给3环的中断号,3环根据中断号HOOK APIUSHORT IntGateNum;// 构造提权中断门IntGateNum = SetIntGate((UINT32)User32ApiSpyNaked);// 返回中断号*(PUSHORT)pIoBuffer = IntGateNum;// 设置状态,返回数据pIrp->IoStatus.Information = 2; // 返回给3环的数据量status = STATUS_SUCCESS;break;}case OPER_GET_APICALLRECORD:{PAPICALLRECORD record = NULL;PopApiCallQueue(&g_ApiCallRecordQueue, &record);if (record == NULL){// 设置状态,返回数据pIrp->IoStatus.Information = 0; // 返回给3环的数据量status = STATUS_SUCCESS;}else{memcpy(pIoBuffer, record, sizeof(APICALLRECORD));// 设置状态,返回数据pIrp->IoStatus.Information = sizeof(APICALLRECORD); // 返回给3环的数据量status = STATUS_SUCCESS;}break;}}// 返回状态如果不设置,Ring3返回值是失败pIrp->IoStatus.Status = status;IoCompleteRequest(pIrp, IO_NO_INCREMENT);return STATUS_SUCCESS;
}// 构造提权中断门,返回中断号
USHORT SetIntGate(UINT32 pFuncion)
{   UCHAR IDT[6]; // IDT寄存器UINT32 IdtAddr,IdtLen;UINT32 IntGateHi = 0,IntGateLo = 0; // 中断门描述符UINT32 *pPreIntGateAddr = (UINT32*)g_pDevObj->DeviceExtension + 1;UINT32 i;// 构造中断门描述符IntGateLo = ((pFuncion & 0x0000FFFF) | 0x00080000);IntGateHi = ((pFuncion & 0xFFFF0000) | 0x0000EE00);// 遍历IDT,找无效项__asm{sidt fword ptr IDT;}IdtAddr = *(PULONG)(IDT+2);IdtLen = *(PUSHORT)IDT;// 遍历IDT,找一个P=0的(跳过第一项)if ((*pPreIntGateAddr) == 0){              for (i = 8; i < IdtLen; i+=8){if ((((PUINT32)(IdtAddr + i))[1] & 0x00008000) == 0){// P=0,此处GDT表项无效,可以使用((PUINT32)(IdtAddr + i))[0] = IntGateLo;((PUINT32)(IdtAddr + i))[1] = IntGateHi;(*pPreIntGateAddr) = IdtAddr + i;             break;}}}else{((PUINT32)(*pPreIntGateAddr))[0] = IntGateLo;((PUINT32)(*pPreIntGateAddr))[1] = IntGateHi;}//DbgPrint("*pPreIntGateAddr: %p.\n", *pPreIntGateAddr);//DbgPrint("INT %02X\n", (USHORT)((*pPreIntGateAddr - IdtAddr) / 8));if (*pPreIntGateAddr == 0) return 0;return (USHORT)((*pPreIntGateAddr - IdtAddr) / 8);
}// 构造提权调用门,返回调用门选择子
USHORT SetCallGate(UINT32 pFunction, UINT32 nParam)
{   UINT32 CallGateHi = 0,CallGateLo = 0; // 调用门描述符UCHAR GDT[6]; // GDT寄存器UINT32 GdtAddr,GdtLen;UINT32 i;UINT32 *pPreCallGateAddr = (UINT32*)g_pDevObj->DeviceExtension;// 构造调用门CallGateHi = (pFunction & 0xFFFF0000);CallGateHi |= 0x0000EC00;CallGateHi |= nParam;CallGateLo = (pFunction & 0x0000FFFF);CallGateLo |= 0x00080000;// 获取GDT基址和大小__asm{sgdt fword ptr GDT;}GdtAddr = *(PULONG)(GDT+2);GdtLen = *(PUSHORT)GDT;// 遍历GDT,找一个P=0的(跳过第一项)if ((*pPreCallGateAddr) == 0){             for (i = 8; i < GdtLen; i+=8){//DbgPrint("%p\n",(PUINT32)(GdtAddr + i));if ((((PUINT32)(GdtAddr + i))[1] & 0x00008000) == 0){// P=0,此处GDT表项无效,可以使用((PUINT32)(GdtAddr + i))[0] = CallGateLo;((PUINT32)(GdtAddr + i))[1] = CallGateHi;(*pPreCallGateAddr) = GdtAddr + i;break;}}}else{((PUINT32)(*pPreCallGateAddr))[0] = CallGateLo;((PUINT32)(*pPreCallGateAddr))[1] = CallGateHi;}if (*pPreCallGateAddr == 0) return 0;return (USHORT)((*pPreCallGateAddr) - GdtAddr);
}// 获取PDE
UINT32 *GetPDE(UINT32 addr)
{return (UINT32 *)(0xc0600000 + ((addr >> 18) & 0x3ff8));
}// 获取PTE
UINT32 *GetPTE(UINT32 addr)
{return (UINT32 *)(0xc0000000 + ((addr >> 9) & 0x7ffff8));
}// User32.dll 导出函数的钩子函数
// 调用方式:修改API函数头2字节,使API函数触发中断,通过提权中断门调用本函数
void __declspec(naked) User32ApiSpyNaked()
{__asm{pushad; // esp - 0x20pushfd; // esp - 0x04mov eax,[esp + 0x24];     mov ecx,[esp + 0x24 + 0x0C];push eax; // EIP3push ecx; // ESP3call User32ApiSpy;popfd;popad;iretd;}
}// 此处需要完成的工作:读取3环EIP,判断API来源,读取3环ESP,获取参数,传给3环控制程序
void __stdcall User32ApiSpy(UINT32 ESP3, UINT32 EIP3)
{UINT32 ApiAddress;// EIP3-0x02是API的地址// ESP3是3环的ESP,可以用来读参数__asm push fs;ApiAddress = EIP3 - 2;//DbgPrint("ESP3: %08x, API: %08x\n", ESP3, ApiAddress);// 判断API地址if (ApiAddress == 0x77d507ea){PAPICALLRECORD pApiCallRecord = NULL;// 添加调用记录到队列,监视进程通过IRP消息读取队列pApiCallRecord = (PAPICALLRECORD)ExAllocatePool(PagedPool,sizeof(APICALLRECORD));pApiCallRecord->nParam = 4;pApiCallRecord->pApiAddress = ApiAddress;pApiCallRecord->Param[0] = ((PUINT32)ESP3)[1];pApiCallRecord->Param[1] = ((PUINT32)ESP3)[2];pApiCallRecord->Param[2] = ((PUINT32)ESP3)[3];pApiCallRecord->Param[3] = ((PUINT32)ESP3)[4];PushApiCallQueue(&g_ApiCallRecordQueue, (PAPICALLRECORD)pApiCallRecord);}__asm pop fs;
}// 初始化队列
void InitApiCallQueue(IN PAPICALLRECORD QueueHead)
{QueueHead->ApiCallRecordList.Flink = QueueHead->ApiCallRecordList.Blink = (PLIST_ENTRY)QueueHead;
}// 插入一条调用记录到队尾
void PushApiCallQueue(IN PAPICALLRECORD QueueHead, IN PAPICALLRECORD pApiCallRecord)
{// 原队尾的下一个节点指向新队尾QueueHead->ApiCallRecordList.Blink->Flink = (PLIST_ENTRY)pApiCallRecord;// 新队尾的上一个节点指向原队尾pApiCallRecord->ApiCallRecordList.Blink = QueueHead->ApiCallRecordList.Blink;// 新队尾的下一个节点指向队列头pApiCallRecord->ApiCallRecordList.Flink = (PLIST_ENTRY)QueueHead;// 队列头的上一个节点指向新队尾QueueHead->ApiCallRecordList.Blink = (PLIST_ENTRY)pApiCallRecord;
}// 从队首弹出一条调用记录
void PopApiCallQueue(IN PAPICALLRECORD QueueHead, OUT PAPICALLRECORD * pApiCallRecord)
{// 记录要弹出的节点*pApiCallRecord = (PAPICALLRECORD)(QueueHead->ApiCallRecordList.Flink);// 如果队列为空,返回NULLif (*pApiCallRecord == &g_ApiCallRecordQueue){*pApiCallRecord = NULL;}// 第二个节点的上一个节点指向队首QueueHead->ApiCallRecordList.Flink->Flink->Blink = (PLIST_ENTRY)QueueHead;// 队首的下一个节点指向第二个节点QueueHead->ApiCallRecordList.Flink = QueueHead->ApiCallRecordList.Flink->Flink;
}// 计算队列长度
UINT32 GetCountApiCallQueue(IN PAPICALLRECORD QueueHead)
{UINT32 cnt = 0;PLIST_ENTRY pList = QueueHead->ApiCallRecordList.Flink;while (pList != (PLIST_ENTRY)QueueHead){pList = pList->Flink;cnt++;}return cnt;
}// 释放队列内存
void FreeApiCallQueue(IN PAPICALLRECORD QueueHead)
{PAPICALLRECORD pApiCallRecord;while(QueueHead->ApiCallRecordList.Flink != (PLIST_ENTRY)QueueHead){     PopApiCallQueue(QueueHead, &pApiCallRecord);ExFreePool(pApiCallRecord);}
}

监控程序

// MsgBoxAWatcher_Ring3.cpp : 定义控制台应用程序的入口点。
//
//1、自己写代码加载,卸载驱动程序
//2、段页的知识;绕写拷贝
//3、写HOOK
//4、shellcode#include "stdafx.h"
#include <Windows.h>#define DRIVER_NAME L"MsgBoxAWatcher_Ring0"
#define DRIVER_PATH L"MsgBoxAWatcher_Ring0.sys"
#define DRIVER_LINK L"\\\\.\\MsgBoxAWatcherDriverLnk"#define OPER_CALL_GATE_R0 CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define OPER_HOOK CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define OPER_GET_APICALLRECORD CTL_CODE(FILE_DEVICE_UNKNOWN,0x802,METHOD_BUFFERED,FILE_ANY_ACCESS)// API调用记录父类
typedef struct _APICALLRECORD
{LIST_ENTRY ApiCallRecordList; // 链表UINT32 pApiAddress; // API函数地址UINT32 nParam; // 参数个数UINT32 Param[32]; // 参数列表
} APICALLRECORD, *PAPICALLRECORD;BOOL LoadDriver(PCWSTR lpszDriverName, PCWSTR lpszDriverPath);
void UnLoadDriver(PCWSTR lpszDriverName);
DWORD *GetPDE(DWORD addr);
DWORD *GetPTE(DWORD addr);
USHORT CreateCallGate(DWORD pBaseAddress, DWORD nParam);
BOOL CallInRing0(PVOID pFuncion, PDWORD pParam, DWORD nParam);
void BypassApiWriteCopyNaked();
void BypassApiWriteCopy();
BOOL HookUser32Api();
void UpdateApiCallRecord();int _tmain(int argc, _TCHAR* argv[])
{   // 加载驱动if (!LoadDriver(DRIVER_NAME, DRIVER_PATH)){printf("驱动服务加载失败.\n");getchar();return 1;}else{printf("驱动服务加载成功.\n");}// 过写拷贝BypassApiWriteCopy();// HOOK MessageBoxAif (HookUser32Api()){printf("HOOK MessageBoxA 成功,现在可以在其他程序里调用 MessageBoxA.\n");     }else{printf("HOOK MessageBoxA 失败.\n");}// 读取调用记录UpdateApiCallRecord();// 取消HOOK((PUSHORT)MessageBoxA)[0] = 0xff8b;UnLoadDriver(DRIVER_NAME);printf("敲任意按键退出程序.\n");getchar();return 0;
}// 加载驱动
BOOL LoadDriver(PCWSTR lpszDriverName, PCWSTR lpszDriverPath)
{// 获取驱动完整路径WCHAR szDriverFullPath[MAX_PATH] = { 0 };GetFullPathNameW(lpszDriverPath,MAX_PATH,szDriverFullPath,NULL);//printf("%s\n", szDriverFullPath);// 打开服务控制管理器SC_HANDLE hServiceMgr = NULL; // SCM管理器句柄   hServiceMgr = OpenSCManagerW(NULL,NULL,SC_MANAGER_ALL_ACCESS);if (NULL == hServiceMgr){printf("OpenSCManagerW 失败, %d\n", GetLastError());return FALSE;}//printf("打开服务控制管理器成功.\n");// 创建驱动服务SC_HANDLE hServiceDDK = NULL; // NT驱动程序服务句柄hServiceDDK = CreateServiceW(hServiceMgr,lpszDriverName,lpszDriverName,SERVICE_ALL_ACCESS,SERVICE_KERNEL_DRIVER,SERVICE_DEMAND_START,SERVICE_ERROR_IGNORE,szDriverFullPath,NULL,NULL,NULL,NULL,NULL);if (NULL == hServiceDDK){DWORD dwErr = GetLastError();if (dwErr != ERROR_IO_PENDING && dwErr != ERROR_SERVICE_EXISTS){printf("创建驱动服务失败, %d\n", dwErr);return FALSE;}}//printf("创建驱动服务成功.\n");// 驱动服务已经创建,打开服务hServiceDDK = OpenServiceW(hServiceMgr,lpszDriverName,SERVICE_ALL_ACCESS);if (!StartService(hServiceDDK, NULL, NULL)){DWORD dwErr = GetLastError();if (dwErr != ERROR_SERVICE_ALREADY_RUNNING){printf("运行驱动服务失败, %d\n", dwErr);return FALSE;}}//printf("运行驱动服务成功.\n");if (hServiceDDK){CloseServiceHandle(hServiceDDK);}if (hServiceMgr){CloseServiceHandle(hServiceMgr);}return TRUE;
}// 卸载驱动
void UnLoadDriver(PCWSTR lpszDriverName)
{SC_HANDLE hServiceMgr = OpenSCManagerW(0,0,SC_MANAGER_ALL_ACCESS);SC_HANDLE hServiceDDK = OpenServiceW(hServiceMgr,lpszDriverName,SERVICE_ALL_ACCESS);SERVICE_STATUS SvrStatus;ControlService(hServiceDDK,SERVICE_CONTROL_STOP,&SvrStatus);DeleteService(hServiceDDK);if (hServiceDDK){CloseServiceHandle(hServiceDDK);}if (hServiceMgr){CloseServiceHandle(hServiceMgr);}
}// 获取PDE
DWORD *GetPDE(DWORD addr)
{return (DWORD *)(0xc0600000 + ((addr >> 18) & 0x3ff8));
}// 获取PTE
DWORD *GetPTE(DWORD addr)
{return (DWORD *)(0xc0000000 + ((addr >> 9) & 0x7ffff8));
}// 构建调用门(提权、有参)
USHORT CreateCallGate(DWORD pBaseAddress, DWORD nParam)
{HANDLE hDevice = CreateFileW(DRIVER_LINK,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);if (hDevice == INVALID_HANDLE_VALUE){return 0;}USHORT CallGateDescriptor; // 调用门选择子DWORD dwRetBytes; // 返回的字节数DWORD InBuffer[2];InBuffer[0] = pBaseAddress;InBuffer[1] = nParam;DeviceIoControl(hDevice,OPER_CALL_GATE_R0,InBuffer,8,&CallGateDescriptor,sizeof(USHORT),&dwRetBytes,NULL);if (dwRetBytes != 2 || CallGateDescriptor == 0){printf("构造调用门失败.\n");return 0;}CloseHandle(hDevice);return CallGateDescriptor;
}// 以0环权限调用某个裸函数,支持传参
BOOL CallInRing0(PVOID pFuncion, PDWORD pParam, DWORD nParam)
{// 命令驱动构建调用门USHORT CallGateDescriptor = CreateCallGate((DWORD)pFuncion,nParam);if (CallGateDescriptor == 0){return FALSE;}// 构造调用门描述符USHORT buff[3] = {0};buff[2] = CallGateDescriptor;// 参数压栈if (nParam && pParam){for (DWORD i = 0; i < nParam; i++){__asm{mov eax,pParam;push [eax];}pParam++;}}   // 调用门调用__asm call fword ptr [buff]; // 长调用,使用调用门提权return TRUE;
}// API函数过写拷贝,其实就是将函数线性地址的PDE,PTE改成可写
// 参数0:要过写拷贝的函数地址
// 参数1:PDE线性地址
// 参数2:PTE线性地址
void __declspec(naked) BypassApiWriteCopyNaked()
{__asm{pushad;pushfd;}__asm{        // R/W = 1, U/S = 1mov eax,[esp+0x24+0x8+0x0]; // 参数2,PTE的地址or dword ptr [eax],0x00000006;mov eax,[esp+0x24+0x8+0x4]; // 参数1,PDE的地址or dword ptr [eax],0x00000006;mov eax,[esp+0x24+0x8+0x8]; // 参数0,要过写拷贝的函数地址invlpg [eax]; // 清除TLB缓存}__asm{popfd;popad;retf 0xC;}
}// 过写拷贝
void BypassApiWriteCopy()
{// MessageBoxA 挂物理页,不这样操作,MessageBoxA的PTE可能是无效的__asm{mov eax, dword ptr ds:[MessageBoxA];mov eax,[eax];}// MessageBoxA过写拷贝   DWORD pParam[3];pParam[0] = (DWORD)MessageBoxA;pParam[1] = (DWORD)GetPDE(pParam[0]);pParam[2] = (DWORD)GetPTE(pParam[0]);CallInRing0(BypassApiWriteCopyNaked, pParam,3);
}// HOOK MessageBoxA
// 理论上可以 HOOK User32.dll 里的任意函数
BOOL HookUser32Api()
{HANDLE hDevice = CreateFileW(DRIVER_LINK,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);if (hDevice == INVALID_HANDLE_VALUE){printf("打开设备失败.\n");return FALSE;}USHORT IntGateNum; // 中断号DWORD dwRetBytes; // 返回的字节数DeviceIoControl(hDevice,OPER_HOOK,NULL,0,&IntGateNum,sizeof(USHORT),&dwRetBytes,NULL);if (dwRetBytes != 2 || IntGateNum == 0){printf("构造中断门失败.\n");return FALSE;}CloseHandle(hDevice);// HOOK MessageBoxAUSHORT IntInstructions = (IntGateNum << 8);IntInstructions |= (USHORT)0x00CD; *(PUSHORT)MessageBoxA = IntInstructions;return TRUE;
}// 从驱动获取调用记录
void UpdateApiCallRecord()
{HANDLE hDevice = CreateFileW(DRIVER_LINK,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);if (hDevice == INVALID_HANDLE_VALUE){printf("打开设备失败.\n");return;}APICALLRECORD ApiCallRecord;DWORD dwRetBytes; // 返回的字节数while (!GetAsyncKeyState('Q')){Sleep(50);DeviceIoControl(hDevice,OPER_GET_APICALLRECORD,NULL,0,&ApiCallRecord,sizeof(ApiCallRecord),&dwRetBytes,NULL);if (dwRetBytes == 0) {//printf("无API调用记录.\n");continue;}if (ApiCallRecord.pApiAddress == (DWORD)MessageBoxA){printf("MessageBoxA(%x, %x, %x, %x);\n", \ApiCallRecord.Param[0],ApiCallRecord.Param[1],ApiCallRecord.Param[2],ApiCallRecord.Param[3]);}}CloseHandle(hDevice);
}

(44)MessageBoxA 监视器(过写拷贝,不使用 shellcode 注入)相关推荐

  1. 64位虚拟机下asm()语法_一步步学写Windows下的Shellcode

    如何在WIndows下编写一个shellcode?为什么会问这个问题,前段时间在做win下的Exploit,但是都是使用大佬写的shellcode,无法实现个人的一些需求.而网络上编写shellcod ...

  2. js调用android代码怎么写,Android端使用WebView注入一段js代码实现js调用android

    需求:为网页上个链接增加点击事件,但是这个链接无法增加js代码 url:http://public.rongcloud.cn/view/D4F444BE2D94D760329F3CF38B4AE35C ...

  3. 测试具有44个漏洞点的简单小靶场-SQL注入篇

    文章目录 本地搭建 SQL注入篇 注入1(数字型注入) 注入2(闭合单引号) 注入3(闭合双引号) 注入4(闭合括号) 注入5(报错注入) 注入6(布尔盲注) 注入7(过滤了'--'和'#'以及'%2 ...

  4. 开源一个自写的病毒技术工具集

    文章目录 前言 界面 代码视图 功能介绍 基础技术 防双开 释放资源 注入技术 全局钩子注入 远程线程注入 APC注入 启动技术 三种方式创建进程 内存加载运行dll 自启动技术 注册表 快速启动目录 ...

  5. C++的拷贝构造函数、operator=运算符重载,深拷贝和浅拷贝、explicit关键字

    1.在C++编码过程中,类的创建十分频繁. 简单的功能,当然不用考虑太多,但是从进一步深刻理解C++的内涵,类的结构和用法,编写更好的代码的角度去考虑,我们就需要用到标题所提到的这些内容. 最近,在看 ...

  6. 构造函数,拷贝构造函数,赋值函数

        C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法.下面就详细比较下三者之间的区别以及它们的具体实现 1.构造函数 构造函数是一种特殊的类成员函数,是当创建一个 ...

  7. C++:10---再议拷贝构造函数

    一.概念 使用一个已经存在的对象,去构造(初始化)另一个对象 二.格式 参数加上const&,因为拷贝构造函数在几种情况下都会被隐式地使用,因此拷贝构造函数不应该是explict的 const ...

  8. C++ 拷贝构造 与 赋值运算符重载

    拷贝构造 当我们创建了一个对象时,想让它的内容和一个已经创建好的对象的内容相同,那么就必须用到拷贝构造.拷贝构造编译器也会自动生成,也是C++类中的6个默认函数之一. 拷贝构造函数格式类名(const ...

  9. 什么时候会用到拷贝构造函数?

    1-什么时候会用到拷贝构造函数?  2-什么时候有必要手动写拷贝构造函数? 1-什么时候会用到拷贝构造函数?         当任何你想复印东西的时候,而不管东西被复印成什么样子.即任何你想利用一个已 ...

最新文章

  1. 深度丨人工智能和大数据的关系及中国在AI领域如何赶超世界
  2. Swift 5 发布,ABI 终于稳定了
  3. java注解,通过反射解析注解,模仿hibernate,获取sql语句。
  4. Zynq-7000系统公共资源及特性
  5. AVA:Netflix的剧照个性化甄选平台
  6. finallshell堡垒机_用lshell+脚本实现堡垒机(跳转机)功能
  7. HierarchicalBeanFactory
  8. mybatis模糊查询不同写法
  9. 分布式事务2PC、3PC模型
  10. C++ 虚函数和虚继承解析
  11. try... except异常处理结构
  12. java中gettext方法_深入理解Java中方法的参数传递机制
  13. 【转】.NET对象序列化2
  14. 华为云 git 托管代码 教程
  15. Scrum板与Kanban如何抉择?bnartvjxv板与按照weypxh
  16. 打开计算机任务栏有桌面没,电脑桌面任务栏不显示打开的窗口怎么办
  17. 我为啥叫这个名字,莫名其妙的油葫芦
  18. 局域网访问提示无法访问检查拼写_win10访问局域网出现“请检查名称的拼写”如何解决...
  19. Google笔试集锦
  20. 【逻辑位移和算数位移】

热门文章

  1. 成功解决 pypmml.base.PmmlError: (‘PmmlException‘, ‘Not a valid PMML‘)
  2. DL之LSTM之MvP:基于TF利用LSTM基于DIY时间训练csv文件数据预测后100个数据(多值预测)状态
  3. MAT之PCA:利用PCA(四个主成分的贡献率就才达100%)降维提高测试集辛烷值含量预测准确度并《测试集辛烷值含量预测结果对比》
  4. Windows Phone 7.1 Sensor プログラミング基礎
  5. Apache开源项目
  6. python----yield(generator)生成器
  7. 简单的防盗链技术(过滤器原理)
  8. zabbix查看数据
  9. 窗口迅速关闭的解决办法/scanf/if/for/break
  10. Android直播app用什么技术可以做到延迟小一些?