3 ms·
It might be worth noting that strictly speaking this isn't safe to apply to an arbitrary binary. That is 31 c0 and 33 c0 might behave identically when executed
by BeeOnRope 6y ago
It might be worth noting that strictly speaking this isn't safe to apply to an arbitrary binary. That is 31 c0 and 33 c0 might behave identically when executed as xor, but they could, for example, also be part of a larger instruction where the swap results in a change in behavior.
Presumably this implementation disassembles the binary which gives a very high probability of determining whether these bytes are only executed as xor, but this isn't in general enough since the way the program is executed at runtime may not correspond to disassembly [0].
Other patterns that might trip this up are programs that re-use some program bytes as constants (e.g., if you needed the 16-bit constant 0xc031 you could just point to this existing pattern in the instruction stream [1]) or which otherwise examine or modify their instruction bytes at runtime.
Now of course this caveat doesn't apply to almost any "vanilla program" compiled by a normal compiler. It's only likely to come up in hand-written assembly or as a result of some tool or process that purposes does this weird stuff (e.g., as an anti-debugging measure). 99.99% of the time these swaps will work out fine.
---
[0] Further, you can't even necessarily unambiguously assign a byte to a particular instruction, even based on the dynamic behavior, since a given byte might be used in two different instructions based on an earlier jump which result in different parsed boundaries. This is really really unusual and usually in the realm of demos or anti-debugging techniques, etc.
[1] This is not actually a good idea for performance because modern CPUs hate it when you use the same bytes for both code and data.
- woodruffw 6y ago> It might be worth noting that strictly speaking this isn't safe to apply to an arbitrary binary. That is 31 c0 and 33 c0 might behave identically when executed as xor, but they could, for example, also be part of a larger instruction where the swap results in a change in behavior. > Presumably this implementation disassembles the binary which gives a very high probability of determining whether these bytes are only executed as xor, but this isn't in general enough since the way the program is executed at runtime may not correspond to disassembly [0]. Yep: steg86 uses iced[1] internally to decode and re-encode instruction sequences. Arbitrarily replacing `31 C0` with `33 C0` (or vice versa) in the instruction text would certainly not go well. And yes: it's possible to contrive pathological programs that make these kinds of patches impossible to perform safely. But those fall under the "what did you expect" support category, at least in terms of the current implementation. A compiler-based implementation would certainly have an easier time. [1]: https://github.com/0xd4d/iced https://github.com/0xd4d/iced
- BeeOnRope 6y agoYes, I think the most practical scenarios this would fail in the real world would be: 1) Signed binaries or other similar concepts such as "anti-cheat" stuff that tries to detect binary modification. 2) Code that actually does do really weird stuff in order to fool static disassembly, e.g., obfuscation which is not uncommon in games and some other binary types. Of course, if you created the binary yourself, you'd be aware of all these gotchas, so this would only come as a surprise if you were applying it as a "third party" to some arbitrary binary.
- ornxka 6y ago>It might be worth noting that strictly speaking this isn't safe to apply to an arbitrary binary. That is 31 c0 and 33 c0 might behave identically when executed as xor, but they could, for example, also be part of a larger instruction where the swap results in a change in behavior. Technically speaking, you can't apply any transformation to arbitrary binaries, because then the encoding changes and the code can look back at itself. This actually features heavily in a recent Hacker News article https://news.ycombinator.com/item?id=23557998 https://news.ycombinator.com/item?id=23557998 about reverse engineering the Snapchat app, where the code regularly took checksums of itself to frustrate debuggers and take different codepaths if tampering (such as to insert breakpoints) was detected. Assembly is kind of, in a sense, the ultimate homoiconic programming language.
- BeeOnRope 6y agoI tried to cover that in the catchall "[...] or which otherwise examine [...] their instruction bytes at runtime". > Technically speaking, you can't apply any transformation to arbitrary binaries Well in some cases it may be possible to prove that a binary doesn't do anything weird (given the entry point which is usually fixed by convention, e.g., _start), although that is undecidable in general.