zyantific / zydis

Fast and lightweight x86/x86-64 disassembler and code generation library
https://zydis.re
MIT License
3.47k stars 438 forks source link

How to get operand address as a ZyanU64? #470

Closed RCECoder closed 10 months ago

RCECoder commented 10 months ago
ZyanU8 data[] = { 0x8B, 0x05, 0x0B, 0x8F, 0x0D, 0x00, 0x65, 0x48, 0x8B, 0x0C, 0x25, 0x58, 0x00, 0x00, 0x00, 0x48,
 0x8B, 0x3C, 0xC1, 0x4C, 0x8D, 0xA4, 0x24, 0xA0, 0x00, 0x00, 0x00, 0x4C, 0x89, 0xA7, 0x08, 0x00,
 0x00, 0x00, 0x48, 0xC7, 0x44, 0x24, 0x20, 0x00, 0x00, 0x00, 0x00 };

int main()
{

    // The runtime address (instruction pointer) was chosen arbitrarily here in order to better
    // visualize relative addressing. In your actual program, set this to e.g. the memory address
    // that the code being disassembled was read from.
    ZyanU64 runtime_address = 0x000000014005462F;

    // Loop over the instructions in our buffer.
    ZyanUSize offset = 0;
    ZydisDisassembledInstruction instruction;

    while (ZYAN_SUCCESS(ZydisDisassembleIntel(
        /* machine_mode:    */ ZYDIS_MACHINE_MODE_LONG_64,
        /* runtime_address: */ runtime_address,
        /* buffer:          */ data + offset,
        /* length:          */ sizeof(data) - offset,
        /* instruction:     */ &instruction
    ))) {

        printf("%016" PRIX64 "  %s\n", runtime_address, instruction.text);
        offset += instruction.info.length;
        runtime_address += instruction.info.length;

    }

    return 0;
}

Result:

000000014005462F  mov eax, [0x000000014012D540]
0000000140054635  mov rcx, gs:[0x0000000000000058]
000000014005463E  mov rdi, [rcx+rax*8]
0000000140054642  lea r12, [rsp+0xA0]
000000014005464A  mov [rdi+0x08], r12
0000000140054651  mov qword ptr [rsp+0x20], 0x00

How can I retreive the address 0x000000014012D540 as a ZyanU64?

mappzor commented 10 months ago

Use ZydisCalcAbsoluteAddress

RCECoder commented 10 months ago

Thanks for your quick response. How to determine if this operand contains an address like in first instruction? instruction.operands->mem.type returns 0x25 (ZYDIS_REGISTER_EAX) for the first instruction.

mappzor commented 10 months ago

instruction.operands->mem.type returns 0x25 for the first instruction.

This is not a valid ZYDIS_MEMOP_TYPE_* value, so probably you are accessing the wrong operand (it should be instruction.operands[1] in your case).

You can detect addresses in memory operands by calling ZydisCalcAbsoluteAddress and checking if it fails or not. If you really want to do this manually check for displacement (mem.disp.has_displacement) and base register (must be EIP/RIP for relative addresses, for absolutes base and index must be ZYDIS_REGISTER_NONE).

RCECoder commented 10 months ago

Yes, you are correct. I used this function and got back the address in result_address. I also found a small bug in the disassembly loop logic. Some opcodes are returning invalid. ZydisDisassembleIntel really returns error but the loop must continue until consumed the size of disassemble buffer. Because it can happen that an instruction is invalid but later on there are valid instructions.

Ollydbg he is able to disassemble this successfully but only if analyze code (CTRL +A) applied.

Here how Ollydbg looks without analyzing code 1

After analyzing looks different 2

So we must continue looping through all buffer to avoid this situation.

Thanks for your help, mappzor 👍

flobernd commented 10 months ago

@RCECoder A small addition regarding "relative" instructions: You can check if the ZYDIS_ATTRIB_IS_RELATIVE is set in the attributes field of the decoded instruction, if you need to know about relative memory AND branch instructions.

Regarding your other problem: If you do a linear sweep disassembly pass, you will often encounter data mixed with actual instructions which leads to the situation like in your first Olly screenshot. There are techniques to get better analysis results, but most of them are complicated.