4

So I'm attempting to use the windows API (DbgEng.h /.lib) 'Disassemble' function to view the instructions of a certain function (which i know is exported) in a module. However....It's returning an unexpected error.

IDebugClient* clt;
IDebugControl* ctrl;

void InitializeInterfaces(void)
{
    HRESULT status;

    if ((status = DebugCreate(__uuidof(IDebugClient), (void**)&clt)) != S_OK) {
        Utils::add_log("IDebugClient DebugCreate failed: 0x%X\n", status);
    }

    clt->AttachProcess(NULL, GetProcessId(GetCurrentProcess()), DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND);

    if ((status = clt->QueryInterface(__uuidof(IDebugControl), (void**)&ctrl)) != S_OK) {
        Utils::add_log("IDebugControl QueryInterface failed: 0x%X\n", status);
    }
}

void print_bytes(const void *object, size_t size, const char* funcname)
{
    size_t i;
    Utils::add_log("|%s function bytes| ", funcname);

    for (i = 0; i < size; i++)
    {
        Utils::add_log_raw("%02x ", ((const unsigned char *)object)[i] & 0xff);
    }

    Utils::add_log_raw("\n");
}

void main()
{
    InitializeInterfaces();

    ULONG64 bc = (ULONG64)GetProcAddress(GetModuleHandleA("lua_shared.dll"), "luaL_loadbufferx");

    PSTR buff;
    HRESULT size = ctrl->Disassemble(bc, DEBUG_DISASM_EFFECTIVE_ADDRESS, (PSTR)&buff, 16, NULL, NULL);

    //error catching incase the disassemble fails (which it does fuck -___-)
    if (size == S_OK) {
        Utils::add_log("%s", buff);
    }
    else if (size == S_FALSE) {
        Utils::add_log("[error] buffer too small to recieve proccess instructions");
    }
    else {
        Utils::add_log("IDebugControl process disassemble failed: 0x%X\n", size);
    }

    //troubleshooting
    Utils::add_log_raw("\nTROUBLESHOOTING INFO\n");
    Utils::add_log("|bc function pointer| 0x%p\n", bc);
    print_bytes((const void*)bc, sizeof bc, "bc");
}

This is current code I'm using, and this is what it outputs:

[04:09:56] IDebugControl process disassemble failed: 0x8000FFFF

TROUBLESHOOTING INFO

[04:09:56] |bc function pointer| 0x640FE750

[04:09:56] |bc function bytes| 55 8b ec 83 e4 f8 83 ec

Anyone know what I'm missing to cause it to throw that error? 0x8000FFFF is equivalent to E_UNEXPECTED enum. The function pointer is valid, the bytes seem fine, I'm lost.

Thanks in advance.

Community
  • 1
  • 1
  • You're passing NULL for the last argument (EndOffset) but the docs don't say this is optional. – Jonathan Potter Jul 28 '15 at 20:23
  • I added a argument for the last parameter - " HRESULT size = ctrl->Disassemble(bc, DEBUG_DISASM_EFFECTIVE_ADDRESS, (PSTR)&buff, 16, NULL, (PULONG64)&endOffset);", it still seems to fail. – Skyler Nelson Jul 28 '15 at 20:29
  • Try setting the flags to 0 instead of DEBUG_DISASM_EFFECTIVE_ADDRESS (it sounds like that's only meant to be used when actually debugging a process). – Jonathan Potter Jul 28 '15 at 20:31
  • Set the flag to 0 - still returning the same E_UNEXPECTED error. – Skyler Nelson Jul 28 '15 at 20:34
  • Maybe the whole API is only meant to be used for debugging a process; it might not be intended as a general purpose disassembler. – Jonathan Potter Jul 28 '15 at 20:36
  • I had the same thoughts - but you would think that even if it wasn't INTENDED for general use it would still work fine right? Seems odd. – Skyler Nelson Jul 28 '15 at 20:38
  • That seems like a big conclusion to jump to :) – Jonathan Potter Jul 28 '15 at 20:52
  • 1
    You're probably getting the error because you're not debugging any process. The `AttachProcess` method call would have failed because a process can't debug itself. – Ross Ridge Jul 28 '15 at 23:24
  • In which case, you should spawn a separate sub-process to debug the main process that contains the code you want to disassemble, and then use an IPC mechanism to have the sub-process send the disassembly back to the main process. – Remy Lebeau Jul 28 '15 at 23:38

2 Answers2

1

I ran into the exact same issue. What fixed it for me was simply loading an IDebugSymbols interface as well. E.g.:

IDebugClient* clt;
IDebugControl* ctrl;
IDebugSymbols* symbols;

void InitializeInterfaces(void)
{
    HRESULT status;

    if ((status = DebugCreate(__uuidof(IDebugClient), (void**)&clt)) != S_OK) {
        Utils::add_log("IDebugClient DebugCreate failed: 0x%X\n", status);
    }

    clt->AttachProcess(NULL, GetProcessId(GetCurrentProcess()), DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND);

    if ((status = clt->QueryInterface(__uuidof(IDebugControl), (void**)&ctrl)) != S_OK) {
        Utils::add_log("IDebugControl QueryInterface failed: 0x%X\n", status);
    }

    if ((status = clt->QueryInterface(__uuidof(IDebugSymbols), (void**)&symbols)) != S_OK) {
        Utils::add_log("IDebugSymbols QueryInterface failed: 0x%X\n", status);
    }

Once I did that disassembly worked fine. (I was also doing an in-proc attach with the exact same flags you were using, for what it's worth.)

No idea why this little tidbit isn't documented on MSDN. I only thought to try it based on some pseudo-code referenced in this article: https://www.gamedev.net/blog/909/entry-2254516-leveraging-windows-built-in-disassembler/

1

You need to call IDebugControl::WaitForEvent(DEBUG_WAIT_DEFAULT, timeout) first.

user541686
  • 205,094
  • 128
  • 528
  • 886