Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When the call is far, zydisdecoderdecodefull cannot work normally #360

Closed
injertao opened this issue Jul 7, 2022 · 13 comments
Closed

When the call is far, zydisdecoderdecodefull cannot work normally #360

injertao opened this issue Jul 7, 2022 · 13 comments
Labels
A-decoder Area: Decoder C-bug-invalid Category: Invalid bug report

Comments

@injertao
Copy link

injertao commented Jul 7, 2022

00600000 - FF15 02000000 EB08 8967452301000000 - call 123456789
Zydisdecoderdecodefull will only decode into:
1CA8E63D630 - FF 15 02000000 - call qword ptr [1CA8E63D638]

@mappzor
Copy link
Contributor

mappzor commented Jul 7, 2022

00600000 - FF15 02000000 EB08 8967452301000000 - call 123456789
Zydisdecoderdecodefull will only decode into:
1CA8E63D630 - FF 15 02000000 - call qword ptr [1CA8E63D638]

ZydisDecoderDecodeFull decodes first instruction from that byte stream which is call qword ptr ds:[rip+0x02]. In your case you provided runtime address of 1CA8E63D630, so 1CA8E63D630 + 6 (instruction length) + 2 (displacement) = 1CA8E63D638. Everything seems to be fine here.

210E848A - 8B 05 DC4A9D01 - mov eax,[22ABCF6C] { (-1) }
Unable to be decoded correctly, lacking a lot of key data

What's the problem here? What "key data" is missing? Zydis decodes this as mov eax, dword ptr ds:[rip+0x19D4ADC], so again: 210E848A + 6 (instruction length) + 19D4ADC (displacement) = 22ABCF6C. Everything is fine here as well.

You seem to be having trouble with understanding how RIP-relative operands work. Decoding process itself does not depend on runtime address. In above example (mov eax, dword ptr ds:[rip+0x19D4ADC]) Zydis will decode 2nd operand as memory operand with operand.mem.base == ZYDIS_REGISTER_RIP and operand.mem.disp.value == 0x19D4ADC. That's raw data in decoded operand structure. All the math to take runtime address into account is done inside the formatter. If you need to perform such calculations for your own needs use ZydisCalcAbsoluteAddress.

@injertao
Copy link
Author

injertao commented Jul 8, 2022

@mappzor Thank you for your answer, but my first question doesn't seem to have been solved.
00600000 - FF15 02000000 EB08 8967452301000000 - call 123456789
Zydisdecoderdecodefull will only decode into:
1CA8E63D630 - FF 15 02000000 - call qword ptr [1CA8E63D638]

FF15 02000000 EB08 8967452301000000
Belongs to absolute address call.
There will be no instruction change, which is independent of the runtime address

@flobernd
Copy link
Member

flobernd commented Jul 8, 2022

FF15 02000000 EB08 8967452301000000 is not a single instruction:

0:  ff 15 02 00 00 00       call   DWORD PTR ds:0x2
6:  eb 08                   jmp    0x10
8:  89 67 45                mov    DWORD PTR [edi+0x45],esp
b:  23 01                   and    eax,DWORD PTR [ecx]
d:  00 00                   add    BYTE PTR [eax],al

Did you mess up the encoding in the byte-stream? I can see the 8967452301 value right after EB08 (jmp +8) which makes no sense at all.

@injertao
Copy link
Author

injertao commented Jul 8, 2022

@flobernd
Maybe you can check this code through cheatengine.
0xFF,0x15,0x02,0x00,0x00,0x00,0xEB,0x08,0x89,0x67,0x45,0x23,0x01,0x0,0x0,0x0
Then use zydis to decode
x64 mode

@flobernd
Copy link
Member

flobernd commented Jul 8, 2022

Online Disassembler:

0:  ff 15 02 00 00 00       call   QWORD PTR [rip+0x2]        # 0x8
6:  eb 08                   jmp    0x10
8:  89 67 45                mov    DWORD PTR [rdi+0x45],esp
b:  23 01                   and    eax,DWORD PTR [rcx]
d:  00 00                   add    BYTE PTR [rax],al

Zydis:

call qword ptr ds:[0x0000000000000008]
jmp 0x0000000000000010
mov dword ptr ds:[rdi+0x45], esp
and eax, dword ptr ds:[rcx]
add byte ptr ds:[rax], al

Not sure what you expect here? I don't use CheatEngine, but these are definitely multiple instructions. Besides that, there is no call absolute instruction in the x86_64 isa set. You can use call [rip+offset] (relative) or call rax (indirect absolute):

See https://c9x.me/x86/html/file_module_x86_id_26.html

@injertao
Copy link
Author

injertao commented Jul 8, 2022

image
In x64, this is a single instruction

@flobernd
Copy link
Member

flobernd commented Jul 8, 2022

Yeah, you are looking at a RIP-rel instruction (like @mappzor explained to you).

0:  ff 15 02 00 00 00       call   QWORD PTR [rip+0x2]        # 0x8

This points to the memory [rip+0x2] -> [8] at which you find the payload 8967452301000000 (the target address).

Whatever generated this call, placed data (8967452301000000) between the code (which will confuse disassemblers using a linear sweep approach). The disassembler needs to jump over the data payload to get to the next actual instruction. This can be done by creating a flow graph.

Remember: Zydis is a low level diassembler that operates on single instructions. The demo/example demonstrates simple linear sweep disassembly strategy (which fails in your case). It's up to you to implement a more advanced strategy 🙂

@injertao
Copy link
Author

injertao commented Jul 8, 2022

@flobernd
Thank you for your answer, but I still can't understand. How can I get the same decoding as cheatengine?
If it is not feasible for the time being, I will try to familiarize myself with it and improve it

@flobernd
Copy link
Member

flobernd commented Jul 8, 2022

The generic solution is very complex (you have to create a control flow graph).

If it's always this specific case all the time, you could try to detect that pattern and jump over the data payload. Something like (pseudo code):

if (instr.mnemonic == CALL && instr.operands[0].base == RIP && instr.operands[0].disp == 2)
{
    code += 10; // payload is 8 byte in 64-bit mode + 2 byte are placed inbetween (EB08)
    offset += 10;
}
...

@injertao
Copy link
Author

injertao commented Jul 8, 2022

@flobernd
The last instruction is wrong, which is meaningless, because I applied for an empty memory, just to demonstrate an instruction

@tremalrik
Copy link

This issue seems very similar to issue #188 from a year ago - what happens appears to be that someone somewhere has come up with some cute code idioms to express functionality that does not natively exist as x86 instructions (jmp/call with absolute address), and with both #188 and this issue, CheatEngine will recognize the idiom and display it as an instruction, even though it really isn't.

Looking at CheatEngine's disassembler source code (see e.g. https://github.com/cheat-engine/cheat-engine/blob/master/Cheat%20Engine/disassembler.pas#L15381 ), its recognizers appear to be very specific - CheatEngine will very specifically recognize exactly the 8-byte sequence ff 15 02 00 00 00 eb 08 as the first 8 bytes of a call-absolute-address instruction (with the next 8 bytes being the absolute-address) and display it as such.

Since this construct is not a 'real' instruction - it consists of two instructions and one 64-bit data item, after all - it's not going to be recognized by Zydis as such (nor do I think it should be). If OP wants to recognize it, they should probably add their own test specifically for the 8-byte ff 15 02 00 00 00 eb 08 sequence.

(similarly, for the jump in issue #188, if CheatEngine-like behavior is what they want, they should add their own test for the 6-byte sequence ff 25 00 00 00 00.)

@Originns
Copy link

As far as I can tell, both of these issues (#188 and this one) originate from MinHook. MinHook is a hooking library which allocates rwx memory for their trampoline allowing them to mix data and code for this weird jmp/call to an absolute address (see https://github.com/TsudaKageyu/minhook/blob/4a455528f61b5a375b1f9d44e7d296d47f18bb18/src/trampoline.c#L79)

@athre0z athre0z added A-decoder Area: Decoder C-bug-invalid Category: Invalid bug report labels Aug 4, 2022
@athre0z
Copy link
Member

athre0z commented Aug 4, 2022

Closing because this is not actually an issue in Zydis.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-decoder Area: Decoder C-bug-invalid Category: Invalid bug report
Projects
None yet
Development

No branches or pull requests

6 participants