3

Recently I tried to obtain OICTL Codes from the iscflashx64.sys driver and I have found it in DispatchDeviceControl function. After driver being installed and have started with SCM it displayed in WinObj. when I invoke DeviceIOControl function with this codes in my user-land app it returns "1" error code. that means my codes is invalid.

Here is IDA view capture and userapp codeenter image description here

Next:

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

And function call:

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

in userapp:

#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;
 }

After googling I found this answer. So this step by step solution does`t work for me. May be problem in switch case tables, but IDA get comments that seems like a codes like that:

loc_1C55A:
sub     esi, 222406h
jz      loc_1C6E7

What is going wrong? Will be very grateful for any suggestions or just a common techniques in using IOCTL.

P.S. Sorry for my English.

2 Answers 2

3

IDA comments list case values in decimal, so you need to either use 2237134 as decimal in your code, or convert it to hex (i.e. 0x2222CE) first.

Additional issue is that the value 0x2222CE is the full IOCTL code while the CTL_CODE macro accepts a function value (bits 2:12 of the code). Using Online IOCTL decoder, we can see that the function is 0x8b3.

So you can use either

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

or

#define IOCTL_READ_MSR 0x2222CE 

(both should result in the same value).

2
  • That works for msr reading ! Now I have BSOD with 3b code(exception while executing a routine that transitions from non-privileged code to privileged code) after invoking DeviceIOControl with my MSR_WRITE code. Any ideas?
    – B. Bot
    Commented May 6, 2017 at 11:15
  • sorry, we're not a debugging site; please try to figure it out yourself or ask for help on SO
    – Igor Skochinsky
    Commented May 6, 2017 at 19:45
2

you can use a python script to find all the IOCTL codes in your binary, here is a script created for the very same purpose : # Find the IoControlCodes corresponding to # calls to DeviceIOControl within a binary

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

run the script using IDA when disassembling the binary and it will do the magic for you, hope this helps!

5
  • Does not work in IDA 7.
    – Biswapriyo
    Commented Feb 18, 2019 at 14:48
  • @Biswapriyo Just tested the script on IDA 7 and it works fine, make sure your python version is 2.7. a stack trace of the given error would be more helpfull Commented Feb 24, 2019 at 16:37
  • Thanks for the reply. Yes I've py 2.7. Other py scripts work fine (e.g. diaphora). Though I'm not deeply familiar with python I shall find why it doesn't work.
    – Biswapriyo
    Commented Feb 25, 2019 at 17:14
  • good luck with that, I'm here to help if you need any :D Commented Feb 25, 2019 at 20:41
  • I'm on IDA 7.4 and the script doesn't work for me either. It doesn't throw any errors either.... Commented Jul 8, 2020 at 0:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.