Windows 驱动程序的 IOCTL 代码

逆向工程 艾达 视窗 内核模式 司机
2021-07-01 19:22:22

最近我试图从 iscflashx64.sys 驱动程序中获取 OICTL 代码,我在 DispatchDeviceControl 函数中找到了它。安装驱动程序并使用 SCM 启动后,它会显示在 WinObj 中。当我在用户端应用程序中使用此代码调用 DeviceIOControl 函数时,它返回“1”错误代码。这意味着我的代码无效。

这是 IDA 视图捕获和 userapp 代码在此处输入图片说明

下一个:

lea     rdx, cs:10000h
movzx   eax, ds:(byte_1C9C8 - 10000h)[rdx+rsi]
mov     ecx, ds:(off_1C988 - 10000h)[rdx+rax*4]
add     rcx, rdx
jmp     rcx             ; switch jump

和函数调用:

lea     rcx, [rsp+188h+PhysicalAddress]
call    MSR_read1
jmp     short loc_1C2BC

在用户应用程序中:

#define IOCTL_READ_MSR\
 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2237134, METHOD_BUFFERED, FILE_ANY_ACCESS)
...

    bool msr_get(unsigned int reg, unsigned long long *val)
  {
    if (hHandle == NULL)
    {
        printf("## ERROR: Not initialized\n");
        return false;
    }

    DWORD dwBytes = 0;
    UCHAR Request[0x100];
    ZeroMemory(&Request, sizeof(Request));

    *(PDWORD)(Request + 0x08) = reg;

    // send request to the driver
    if (DeviceIoControl(
        hHandle, IOCTL_READ_MSR,
        &Request, sizeof(Request), &Request, sizeof(Request),
        &dwBytes, NULL))
    {
        LARGE_INTEGER Val;

        Val.HighPart = *(PDWORD)(Request + 0x0c);
        Val.LowPart = *(PDWORD)(Request + 0x00);

        *val = Val.QuadPart;

        return true;
    }
    else
    {
        printf("## ERROR DeviceIoControl() %d\n", GetLastError());
    }

    return false;
 }

谷歌搜索后我找到了这个答案所以这个循序渐进的解决方案对我不起作用。在 switch case 表中可能有问题,但 IDA 得到的注释看起来像这样的代码:

loc_1C55A:
sub     esi, 222406h
jz      loc_1C6E7

出了什么问题?将非常感谢任何建议或只是使用 IOCTL 的常用技术。

PS对不起我的英语。

2个回答

IDA 注释以十进制形式列出案例值,因此您需要2237134在代码中使用十进制,或者0x2222CE先将其转换为十六进制(即)。

另一个问题是值 0x2222CE 是完整的IOCTL 代码,而CTL_CODE接受函数值(代码的位 2:12)。使用在线IOCTL解码器,我们可以看到函数为0x8b3。

所以你可以使用

#define IOCTL_READ_MSR\
 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x8b3, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)

或者

#define IOCTL_READ_MSR 0x2222CE 

(两者都应产生相同的值)。

您可以使用 python 脚本来查找二进制文件中的所有 IOCTL 代码,这里有一个为相同目的创建的脚本:# 在二进制文件中查找与 # 调用 DeviceIOControl 相对应的 IoControlCodes

from idaapi import *
from idautils import *
from idc import *

##########################################################
# This class implements a fifo queue
class fqueue(list):
    def __init__(self, n):
        self.n = n

    def push(self, a):
        self.append(a)
        if len(self) > self.n:
            self.pop(0)

##########################################################

gpRegList = ['eax', 'ebx', 'ecx', 'edx']
ioccList = []
callerList = []
pushQueue = fqueue(2)

dioc_ea = LocByName("DeviceIoControl") # EA

print "DeviceIoControl found at 0x%08x" % dioc_ea

for caller in XrefsTo(dioc_ea, True):
    # This is the address (within a function)
    # where the reference was made
    caller_ea = caller.frm

    if caller_ea not in callerList:
        # IDA Pro shifts duplicates, get rid of them
        callerList.append(caller_ea)
        print "xref @ 0x%08x (%s)" % (caller_ea, GetFunctionName(caller_ea))
    else:
        continue

    # The dwIoControlCode must be the second 
    # PUSH xxx before the CALL instruction
    # So we need to keep track of the PUSH instructions
    for ins in FuncItems(caller_ea):
        disasm = GetDisasm(ins)
        if "push" in disasm:
            # Save the PUSH instruction's operand
            pushQueue.push(GetOpnd(ins, 0))
        elif ins == caller_ea:
            # At this moment we hit the corresponding CALL instruction
            # First item in Queue is second "oldest" push
            iocc = pushQueue[0]

            if iocc in gpRegList:
                print "NOTE: IoControlCode was %s at 0x%08x. Check manually" % (iocc, caller_ea)
            else:
                if pushQueue[0] not in ioccList:
                    ioccList.append(pushQueue[0])
        else:
            pass

# Print all the gathered IoControlCodes

print "%d IoControlCodes found!" % len(ioccList)

for io in ioccList:
    print "[*]", io

在反汇编二进制文件时使用 IDA 运行脚本,它会为您带来魔力,希望对您有所帮助!