Live data from Hacker News

Weird things I learned while writing an x86 emulator

timdbg.com

31–40 of 75 posts

Re: Weird things I learned while writing an x86 emulator

#31

An ADD instruction will update the carry flag but the INC instruction does not! Neither does DEC, and I believe the original reason for this was multiple-precision arithmetic routines --- you need to propagate the carry flag, but also update loop counters and pointers. It seems that the actual behavior of the undefined flags is related to the internal implementation of the shift operation, and is different between di…

> "Undefined" values are often used by anti-debugging/anti-emulation/VM detection code to determine if the CPU is real hardware or not, so it's actually quite important to emulate them correctly. That seems quite brittle, unless all real CPUs implement them the same way. If they do, one has to wonder whether it's really undefined or an undocumented part of the x86 spec instead.

I'd guess it's "undocumented", not "undefined". Don't know how the situation is on x86, but on the Z80 there were indeed some slight differences in undocumented behaviour between CPU vendors, but those are so obscure that it hardly affected any real world code (they affected the undocumented flag bits 3 and 5, and their behaviour was only properly 'decoded' in the 2000's (https://github.com/floooh/emu-info/blob/master/z80/memptr_en...).

The only CPU I know with actual 'undefined' behaviour is the 6502 for some of the undocumented/illegal opcodes which can yield different results based on things like current CPU temperature (see the ANE/XAA instruction description: https://www.masswerk.at/nowgobang/2021/6502-illegal-opcodes)

Re: Weird things I learned while writing an x86 emulator

#32

Earlier quoted context omitted.

The (in)famous A86/A386 assembler claimed to use this technique to identify code produced with it. As far as I know, it was just a simple inversion of reg-reg ModRMs depending on the register values being used, although I didn't test it too exhaustively.

Speaking of things with the same names, what happened to Linux a386 and what the heck was it even? I can't find a link nowadays, but it was something like User Mode Linux, but not really.

Here’s some links I could dig up from the internet archive:

- https://web.archive.org/web/20060108072152/http://linux.a386...

- https://web.archive.org/web/20060111133928/http://a386.nocre...

Re: Weird things I learned while writing an x86 emulator

#34

This is a good list! Another fun quirk: because x86 is a register-memory architecture and allows all kinds of variants of reg/mem operand encodings, there are a handful of equivalent encodings with exactly the same lengths (and just slightly different ModR/M bytes). You can take advantage of this to do software fingerprinting or, in my case, steganography without changing an executable’s size or semantics[1]. [1]: ht…

Used the exact same trick when researching dumb ways to break AV signatures at uni :)

You can also abuse the displacement math with eiz:

  ff 34 24 push   DWORD PTR [esp]
  ff 34 e4 push   DWORD PTR [esp+eiz*8]
Or some useless prefixes may work:

  80 c0 53 add    al,0x53
  36 04 53 ss add al,0x53

Re: Weird things I learned while writing an x86 emulator

#35

This is a good list! Another fun quirk: because x86 is a register-memory architecture and allows all kinds of variants of reg/mem operand encodings, there are a handful of equivalent encodings with exactly the same lengths (and just slightly different ModR/M bytes). You can take advantage of this to do software fingerprinting or, in my case, steganography without changing an executable’s size or semantics[1]. [1]: ht…

Used the exact same trick when researching dumb ways to break AV signatures at uni :) You can also abuse the displacement math with eiz: ff 34 24 push DWORD PTR [esp] ff 34 e4 push DWORD PTR [esp+eiz*8] Or some useless prefixes may work: 80 c0 53 add al,0x53 36 04 53 ss add al,0x53

This is the first time I learned about eiz. Cool trick!

What’s eiz: https://stackoverflow.com/a/2553556/3125367

Re: Weird things I learned while writing an x86 emulator

#37
post #30

Has anyone used TTD, or rr for that purpose in Linux world, to debug a complex multi-threaded application? What is the main use-case of these tools? For example, limitations of rr seem to suggest that it is almost of no use for multi-threaded programs so I have never actually tried it. I don't know about the TTD though. > rr limitations > ... > emulates a single-core machine. So, parallel programs incur the slowdown…

It can still debug multi-threaded programs, but if your bug relies on multi-core interactions it won't appear under rr (you can still catch a lot of race conditions though, and it has a 'chaos mode' which generate irregular scheduling intended to increase the likelyhood of such bugs appearing). It's been used to debug race conditions as well as other bugs in firefox, for example.

That's what I thought as well, limited to the single-core race conditions era. Thanks for the evidence.

Re: Weird things I learned while writing an x86 emulator

#38
post #3

> You can add quite a few until you get to 15 bytes. This length is a hard limit on current x86-compatible CPUs. Any instruction longer than 15 bytes is considered invalid and will generate an exception. There's a few valid 16 byte instructions though.. Sandpile lists a few examples: https://www.sandpile.org/x86/opc_enc.htm 36 67 8F EA 78 10 84 24 disp32 imm32 = bextr eax,[ss:esp*1+disp32],imm32 64 67 8F EA F8 10 84…

The longest structurally valid x86 instruction is 26 bytes, from some research I did a few years ago[1]. But as others have noted, structurally valid does not mean that any x86 CPU will accept them: they’ll all produce #UD or similar, including these 16 byte ones. [1]: https://yossarian.net/res/pub/mishegos-langsec-2021.pdf

You can just keep sticking prefixes on an instruction to get something longer, no? The processor will refuse to decode it but it's "legal" otherwise.

Re: Weird things I learned while writing an x86 emulator

#39

Earlier quoted context omitted.

Would love to learn more and help with this project if you need extra work done

It’s relatively feature complete, but there are some ideas for increasing the steganographic capacity listed in the README and issues! In particular, we could use the flexibility of the multi-byte NOP sequences to hide some more information.

What are some interesting use-cases for information hiding in the binaries?

Re: Weird things I learned while writing an x86 emulator

#40

Earlier quoted context omitted.

The longest structurally valid x86 instruction is 26 bytes, from some research I did a few years ago[1]. But as others have noted, structurally valid does not mean that any x86 CPU will accept them: they’ll all produce #UD or similar, including these 16 byte ones. [1]: https://yossarian.net/res/pub/mishegos-langsec-2021.pdf

You can just keep sticking prefixes on an instruction to get something longer, no? The processor will refuse to decode it but it's "legal" otherwise.

Ostensibly, only one prefix from each group can matter. Although I did notice that XACQUIRE and LOCK are both group 1 prefixes, which makes that statement kind of a lie (but it's an intentional design to make XACQUIRE do nothing on processors that don't support it).

In any case, there's only a finite number of prefixes you can meaningfully stick onto an instruction, and repeating the same prefix will do absolutely nothing.

Post reply on HN