These are code-reuse attacks meaning they do not provide code, but rather use data in non-executable pages to cause snippets of code (called gadgets) in executable pages such as libc or the program being attacked, to be run under the control of said data. An example means of doing this is by overrunning a data buffer that is known to be unfortunately located on the stack. They execute code by providing a sequence of addresses to return to (ROP) in the stack-based approach or to jump to (JOP). These addresses always point to the gadgets that are within existing software that is fully allowed to run. Both JOP and ROP rely on the ability of hackers to analyze library and program code to locate the gadgets in the versions of each that they are targeting.
Gadgets typically consist of just one or two instructions,
followed by either an indirect jump instruction in the case of JOP, or a return instruction
in the case of ROP.
When the targeted software is updated, it is quite possible that
some gadgets will disappear and others will move, rendering the addresses
to them useless. Thus code reuse attacks have a shorter
shelf life than did older
code injection attacks.
A ROP attack works by using a buffer overrun on the stack to overwrite the stack with a series of return addresses that point to these gadgets, which are then executed sequentially.
With JOP, it is more complicated. A buffer overrun in the heap must overwrite addresses for instance in a setjmp buffer or elsewhere, causing a jump to a dispatcher gadget. Without a dispatcher, JOP cannot work. JOP then proceeeds sequentially through a set of addresses to gadgets, and each gadget must be able to increment the index into the sequence and jump back to the dispatcher.
Both ROP and JOP have been proven by researchers and hackers to work.
AntiJOP works by rewriting assembly language code, so it must be able to take as input the assembly code generated by a compiler, and then provide output to the assembler and linker. Naturally this process requires recompilation of existing, installed software to achieve protection.
My program currently takes as its input NASM- or YASM-formatted assembly code, whereas popular compilers like GCC and LLVM use a different format. I may eventually update AntiJOP to accept these compilers' syntax.
Assembly code rewriting works as follows. ROP and JOP gadgets terminate with certain instructions that contain certain bytes. AntiJOP replaces those specific bytes wherever they may occur. In the case of ROP on x86 each gadget typically ends with the near-return instruction 0xC3 or 0xC2. A gadget of a JOP exploit typically ends with any of a range of indirect jump instructions that start with 0xFF.
Note that there is a second way to classify gadgets besides the ROP versus JOP distinction:
On the x86, gadgets that start within instructions are more numerous than those based on intended instructions.
AntiJOP must address both intentional and unintentional types of gadgets. I do this in the case of JOP gadgets by attempting to replace every instance of an 0xFF byte with something else, and for ROP gadgets I attempt to replace 0xC3 and 0xC2.
Where to find the 0xFF, 0xC3 and 0xC2? They can begin in any part of an x86 instruction, and the meaning of each value is different in each place.
scale-index-baseor SIB byte, this can signify EDI*8 + EDI.
This software is in an alpha state.
AntiJOP still parses NASM/YASM assembly code, not the less legible GCC/LLVM format.