1

I'm hooking kernel32!DeviceIoControl Windows API (using Python Deviare). Is there a way to retrieve the device name corresponding to the device handle passed as the first parameter of this API ?

    BOOL WINAPI DeviceIoControl(
  _In_        HANDLE       hDevice, <---------- This one
  _In_        DWORD        dwIoControlCode,
  _In_opt_    LPVOID       lpInBuffer,
  _In_        DWORD        nInBufferSize,
  _Out_opt_   LPVOID       lpOutBuffer,
  _In_        DWORD        nOutBufferSize,
  _Out_opt_   LPDWORD      lpBytesReturned,
  _Inout_opt_ LPOVERLAPPED lpOverlapped
);

I've made some tries using DuplicateHandle and NtQueryObject API, but i do not manage to make it works. I'm not sure that it's possible this way. Here is the code I'm talking about :

class LSA_UNICODE_STRING(Structure):
    """Represent the LSA_UNICODE_STRING on ntdll."""
    _fields_ = [
        ("Length", USHORT),
        ("MaximumLength", USHORT),
        ("Buffer", LPWSTR),
    ]

class PUBLIC_OBJECT_TYPE_INFORMATION(Structure):
    """Represent the PUBLIC_OBJECT_TYPE_INFORMATION on ntdll."""
    _fields_ = [
        ("Name", LSA_UNICODE_STRING),
        ("Reserved", ULONG * 22),
    ]

class OBJECT_NAME_INFORMATION (Structure):
    """Represent the OBJECT_NAME_INFORMATION on ntdll."""
    _fields_ = [
        ("Name", LSA_UNICODE_STRING),
    ]


STATUS_INFO_LENGTH_MISMATCH = 0xC0000004
STATUS_BUFFER_OVERFLOW = 0x80000005L
STATUS_INVALID_HANDLE = 0xC0000008L
STATUS_BUFFER_TOO_SMALL = 0xC0000023L
STATUS_SUCCESS = 0


def GetProcessHandle(pid, handle):
    """Get a handle for the process with the given pid"""
    try:
        hProcess = OpenProcess(win32con.PROCESS_DUP_HANDLE, 0, pid)
        dupHandle = DuplicateHandle(hProcess, handle, GetCurrentProcess(), 0, 0, win32con.DUPLICATE_SAME_ACCESS)
        return dupHandle
    except ApiError,(errno, errctx, errmsg):
        if errno in (
            winerror.ERROR_ACCESS_DENIED,
            winerror.ERROR_INVALID_PARAMETER,
            winerror.ERROR_INVALID_HANDLE,
            winerror.ERROR_NOT_SUPPORTED
        ):
            return None
        else:
            raise

def GetTypeInfo(handle):
    """ Get the handle type information """
    public_object_type_information = PUBLIC_OBJECT_TYPE_INFORMATION()
    size = DWORD(sizeof(public_object_type_information))
    NtQueryObject = windll.ntdll.NtQueryObject

    while True:
        result = NtQueryObject(handle, 2, byref(public_object_type_information), size, None)
        print result

        if result == STATUS_SUCCESS:
            return public_object_type_information.Name.Buffer
        elif result == STATUS_INFO_LENGTH_MISMATCH:
            size = DWORD(size.value * 4)
            resize(public_object_type_information, size.value)
        elif result == STATUS_INVALID_HANDLE:
            return "[invalid handle]"
            #print "invalid handle"
            #return "INVALID HANDLE: {0}".format(hex(handle))
        else:
            return "[error]"


def GetNameInfo(handle):
    """ Get the handle name information """
    object_name_information = OBJECT_NAME_INFORMATION()
    size = DWORD(sizeof(object_name_information))
    NtQueryObject = windll.ntdll.NtQueryObject

    while True:
        result = NtQueryObject(handle, 1, byref(object_name_information), size, None)
        print result

        if result == STATUS_SUCCESS:
            return object_name_information.Name.Buffer
        elif result in (STATUS_BUFFER_OVERFLOW, STATUS_BUFFER_TOO_SMALL, STATUS_INFO_LENGTH_MISMATCH):
            size = DWORD(size.value * 4)
            resize(object_name_information, size.value)
        else:
            return "[name retrieval error]"

def GetDeviceInfo(targetPid, handle):

    #currentPid = os.getpid()
    dupHandle = GetProcessHandle(targetPid, handle)

    if dupHandle is None:
        print "Duplicate handle failed"
    else:
        typeInfo = GetTypeInfo(int(dupHandle))
        nameInfo = GetNameInfo(int(dupHandle))
        print "type = {0} - name = {1}".format(typeInfo, nameInfo)

Do you have any idea ?

Koutto
  • 11
  • 2
  • How are you calling these functions? What device are you trying to get information about? Are you actually hooking the function, or are you just trying to call the function? (Hook means something very specific, and in general you can't hook drivers from user mode.) When you say "i do not manage to make it works" can you tell us how it's failing? Is it giving you an error? Returning something unexpected? Something else? – theB Sep 08 '15 at 15:58
  • 1
    @theB: you can't hook drivers from user mode, but you can hook the DeviceIoControl() API call, so I think it reasonably safe to assume that the OP meant exactly what he said. On the other hand, it would be more sensible to start by trying to get the device path for handles that you've opened yourself, and only then trying it from inside a hook. – Harry Johnston Sep 08 '15 at 23:25
  • @HarryJohnston - I was basing that on available resources like [this answer](http://stackoverflow.com/a/12278589/5240004), which in retrospect ignores some of the less supported ways of doing it. I would still be wary, though, of globally intercepting low(ish) level system calls, and filtering them through the python interpreter. – theB Sep 09 '15 at 01:19
  • 1
    @theB: that question confused two *entirely* different sort of hooks. However, it is absolutely true that API hooking isn't formally supported (although MS do sell software for doing it) and it certainly isn't entirely safe, and trying to do it in the context of an interpretive language like Python sounds to me like a nightmare waiting to happen. I'm just pointing out that it does *exist*. :-) – Harry Johnston Sep 09 '15 at 02:42
  • The default `restype` is `c_int` (i.e. `int`), which is fine because `NTSTATUS` is signed (warnings and errors are negative values). Make the constants signed, e.g. `NTSTATUS = c_long;` `STATUS_INFO_LENGTH_MISMATCH = NTSTATUS(0xC0000004).value`. – Eryk Sun Sep 09 '15 at 03:58
  • Your approach for dealing with size mismatch may work here, but other NT APIs require the exact size that's expected; making the buffer too large will error out all the same. Use the `ReturnLength` out parameter if the function provides it. – Eryk Sun Sep 09 '15 at 04:13
  • @theB: I'm actually developping a tool aimed at monitoring calls to DeviceIoControl (from userland). Thus, I'm injecting into target process I want to monitor and then I set up a hook on this API using the excellent Deviare python library [link](http://blog.nektra.com/main/2012/07/20/windows-api-hooking-in-python-with-deviare/). So, at this point, I can easily retrieve the various parameters passed to the function (buffers, buffer sizes). The first parameter is the device handle associated with the targeted driver, I actually want to get its name. – Koutto Sep 09 '15 at 08:30
  • Did you change the status constants to use signed values as I suggested? – Eryk Sun Sep 09 '15 at 11:10
  • To clarify the language, the user-mode APIs in kernel32, etc, technically aren't system calls, but people commonly think of them as system calls for the Windows subsystem. The actual system calls are for the native NT system, i.e. the Nt exports in ntdll that call NT executive APIs, and the Nt/Zw internal functions in user32/gdi32 that call the win32k driver. A lot is also managed in user mode by messaging Windows server processes (e.g. csrss.exe, lsass.exe, services.exe), which goes through the executive's LPC system services such as `NtRequestWaitReplyPort` and `NtReplyWaitReceivePort`. – Eryk Sun Sep 09 '15 at 11:38

0 Answers0