Skip to content

Commit e316c38

Browse files
authored
Merge pull request GJDuck#63 from GJDuck/full-coverage
100% coverage mode
2 parents 057e0a0 + 350b071 commit e316c38

14 files changed

Lines changed: 454 additions & 72 deletions

File tree

doc/e9patch.1

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ The first phase tracks instruction patching success or failure:
2828
.IP
2929
\fB\[char46]\fR = Instruction was patched successfully
3030
.br
31+
\fBT\fR = Instruction was patched with tactic B0
32+
.br
3133
\fBX\fR = Instruction could not be patched
3234
.PP
3335
The second phase tracks physical memory compression:
@@ -159,6 +161,8 @@ automatically/randomly. The special value "none" leaves the
159161
original base intact.
160162
.br
161163
Default: \fBnone\fR (disabled)
164+
.IP "\fB\-\-tactic\-B0\fR[=\fI\,false\/\fR]" 4
165+
.PD 0
162166
.IP "\fB\-\-tactic\-B1\fR[=\fI\,false\/\fR]" 4
163167
.PD 0
164168
.IP "\fB\-\-tactic\-B2\fR[=\fI\,false\/\fR]" 4
@@ -169,12 +173,14 @@ Default: \fBnone\fR (disabled)
169173
.PD 0
170174
.IP "\fB\-\-tactic\-T3\fR[=\fI\,false\/\fR]" 4
171175
.PD
172-
Enables [disables] the corresponding tactic (B1/B2/T1/T2/T3).
176+
Enables [disables] corresponding tactic (B1/B2/T1/T2/T3).
173177
.br
174-
Default: \fBtrue\fR (enabled)
175-
.IP
178+
Default: \fBtrue\fR (enabled) for B1/B2/T1/T2/T3
179+
\fBfalse\fR (disabled) for B0
180+
.TP
176181
\fB\-\-tactic\-backward\-T3\fR[=\fI\,false\/\fR]
177-
Enable [disables] backward jumps for tactic T3.
182+
Enables [disables] backward jumps for tactic T3.
183+
.br
178184
Default: \fBtrue\fR (enabled)
179185
.TP
180186
\fB\-\-trap\fR=\fI\,ADDR\/\fR
@@ -185,11 +191,13 @@ the trampoline using GDB.
185191
\fB\-\-trap\-all\fR[=\fI\,false\/\fR]
186192
Enable [disable] the insertion of a trap (int3) instruction at
187193
all trampoline entries.
194+
.br
188195
Default: \fBfalse\fR (disabled)
189196
.TP
190197
\fB\-\-trap\-entry\fR[=\fI\,false\/\fR]
191198
Enable [disable] the insertion of a trap (int3) at the program
192199
loader entry\-point.
200+
.br
193201
Default: \fBfalse\fR (disabled)
194202
.TP
195203
\fB\-\-version\fR

doc/e9tool-user-guide.md

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
- [1.1 Optimization](#optimization)
88
- [1.2 Compression](#compression)
99
- [1.3 Rewriting Modes](#modes)
10+
* [1.3.1 Control-Flow Recovery Mode](#cfr_mode)
11+
* [1.3.2 Full-Coverage Mode](#100_mode)
1012
- [1.4 Disassembly and Analysis](#analysis)
1113
* [2. Matching Language](#matching)
1214
- [2.1 Attributes](#attributes)
@@ -100,16 +102,49 @@ The default compression level is 9 (most compression).
100102
---
101103
### <a id="modes">1.3 Rewriting Modes</a>
102104

103-
E9Tool (and E9Patch) support two main rewriting modes:
105+
E9Tool (and E9Patch) supports three main rewriting modes:
104106

105-
1. The *default* mode that uses binary rewriting without control-flow
106-
recovery.
107-
2. A *control-flow-recovery* (CFR) mode that uses a (conservative) CFR
107+
1. *Default*: Classic binary rewriting without control-flow recovery.
108+
This is the "official" mode of E9Tool/E9Patch for benchmarking and testing
109+
purposes.
110+
2. *CFR*: A *control-flow-recovery* (CFR) mode that uses a (conservative) CFR
108111
analysis to optimize further binary rewriting.
112+
3. *100%*: A *full-coverage* (100%) mode that ensures that every matching instruction
113+
will be patched, even if this (significantly) reduces performance.
109114

110-
The CFR mode can be enabled by passing `-X` to E9Tool, e.g.:
115+
Below if a summary of the different modes and features:
111116

112-
$ e9tool -X -M jmp -P print xterm
117+
<table border="1">
118+
<tr><th></th><th>Option</th><th>Performance</th><th>Robustness</th><th>Coverage</th></tr>
119+
<tr><td><b>Default</b></td><td></td>
120+
<td>&#9733;&#9733;&#32;</td>
121+
<td>&#9733;&#9733;&#9733;</td>
122+
<td>&#9733;&#9733;&#32;</td>
123+
</tr>
124+
<tr><td><b>CFR</b></td><td><tt>-CFR</tt></td>
125+
<td>&#9733;&#9733;&#9733;</td>
126+
<td>&#9733;&frac12;&#32;</td>
127+
<td>&#9733;&#9733;&frac12;</td>
128+
</tr>
129+
<tr><td><b>100%</b></td><td><tt>-100</tt></td>
130+
<td>0</td>
131+
<td>&#9733;&#9733;&#9733;</td>
132+
<td>&#9733;&#9733;&#9733;</td>
133+
</tr>
134+
</table>
135+
136+
Here, *Option* is the E9Tool command-line option to enable the mode,
137+
*Performance* is the relative performance of the rewitten binary,
138+
*Robustness* is the relative absense rewriting errors, and
139+
*Coverage* is the patching coverage (number of matching instructions actually
140+
patched).
141+
142+
---
143+
#### <a id="cfr_mode">1.3.1 Control-Flow-Recovery Mode</a>
144+
145+
The CFR mode can be enabled by passing `-CFR` to E9Tool, e.g.:
146+
147+
$ e9tool -CFR -M jmp -P print xterm
113148

114149
The CFR mode has pros and cons, which may or may not be acceptable
115150
depending on the application.
@@ -124,6 +159,33 @@ possible rewriting errors.
124159
Thus, the CFR mode is not as robust as the default mode, although it should
125160
be compatible with most binaries.
126161

162+
---
163+
#### <a id="100_mode">1.3.2 Full-Coverage Mode</a>
164+
165+
The 100% (full coverage) mode can be enabled by passing `-100` to E9Tool, e.g.:
166+
167+
$ e9tool -100 -M jmp -P print xterm
168+
169+
This mode aims to achieve 100% patching coverage, even if this
170+
(significantly) degrades performance.
171+
To do so, the 100% mode will resort to illegal opcodes and `SIGILL` handlers
172+
if an instruction cannot be patched using any other method.
173+
The 100% mode is useful for applications that need full coverage, even if
174+
this comes at the cost of performance.
175+
176+
Note that the 100% mode has some caveats:
177+
178+
* Any attempt by the program to install a `SIGILL` signal handler will fail
179+
with errno=`ENOSYS`.
180+
This potentially breaks transparency.
181+
* The patching coverage may not reach 100% for other reasons, such as virtual
182+
address space exhaustion.
183+
Such cases will be uncommon.
184+
185+
The 100% and CFR modes are compatible and can be combined, e.g.:
186+
187+
$ e9tool -100 -CFR -M jmp -P print xterm
188+
127189
---
128190
### <a id="analysis">1.4 Disassembly and Analysis</a>
129191

doc/e9tool.1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ For more information, please refer to the following document:
102102
.IP "" 4
103103
\fI/usr/share/doc/e9tool/e9tool-user-guide.html\fR
104104
.SH OPTIONS
105+
.IP "\fB\-100\fR" 4
106+
Enables "full coverage" mode that attempts to patch 100% of matching
107+
instructions, even at the cost of a significant reduction in performance.
108+
This is useful for applications that prioritize coverage over other
109+
considerations.
105110
.IP "\fB\-\-backend\fR PROG" 4
106111
Use PROG as the backend.
107112
The default is "e9patch".

src/e9patch/e9api.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,15 @@
4040
*/
4141
static void parseOptions(Binary *B, char * const *argv)
4242
{
43-
parseOptions(argv, /*api=*/true);
44-
switch (B->mode)
45-
{
46-
case MODE_ELF_EXE: case MODE_ELF_DSO:
47-
B->config = option_loader_base; break;
48-
default:
49-
break;
50-
}
51-
targetAnalysis(B);
43+
parseOptions(argv, /*api=*/true);
44+
switch (B->mode)
45+
{
46+
case MODE_ELF_EXE: case MODE_ELF_DSO:
47+
B->config = option_loader_base; break;
48+
default:
49+
break;
50+
}
51+
targetAnalysis(B);
5252
}
5353

5454
/*
@@ -79,7 +79,7 @@ static void queueFlush(Binary *B, intptr_t cursor)
7979
case STATE_QUEUED:
8080
for (unsigned i = 0; i < I->size; i++)
8181
{
82-
assert(I->patched.state[i] == STATE_QUEUED);
82+
assert(I->STATE[i] == STATE_QUEUED);
8383
I->STATE[i] = STATE_INSTRUCTION;
8484
}
8585
I->debug = (option_trap_all ||
@@ -217,6 +217,11 @@ static Binary *parseBinary(const Message &msg)
217217
case MODE_PE_EXE: case MODE_PE_DLL:
218218
parsePE(B);
219219
B->pic = true;
220+
if (option_tactic_B0)
221+
{
222+
warning("tactic B0 is not supported for Windows PE binaries");
223+
option_tactic_B0 = false;
224+
}
220225
break;
221226
}
222227

src/e9patch/e9elf.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,15 @@ size_t emitElf(Binary *B, const MappingSet &mappings, size_t mapping_size)
518518
size += sizeof(addr);
519519
config->num_finis++;
520520
}
521+
config->traps = (B->Traps.size() > 0? (uint32_t)(size - config_offset): 0);
522+
for (auto i = B->Traps.rbegin(); i != B->Traps.rend(); ++i)
523+
{
524+
const Alloc *A = *i;
525+
struct e9_trap_s trap = {A->I->addr, A->lb + A->entry};
526+
memcpy(data + size, &trap, sizeof(trap));
527+
size += sizeof(trap);
528+
config->num_traps++;
529+
}
521530

522531
std::vector<Bounds> bounds;
523532
intptr_t ub = INTPTR_MIN;
@@ -589,8 +598,8 @@ size_t emitElf(Binary *B, const MappingSet &mappings, size_t mapping_size)
589598
option_loader_base, ub);
590599
}
591600

592-
intptr_t fini = 0x0;
593-
size_t fini_rel8_offset = 0;
601+
intptr_t fini = 0x0, handler = 0x0;
602+
size_t fini_rel8_offset = 0, handler_rel8_offset = 0;
594603
int32_t config_rel32;
595604
if (B->finis.size() > 0)
596605
{
@@ -605,6 +614,19 @@ size_t emitElf(Binary *B, const MappingSet &mappings, size_t mapping_size)
605614
fini_rel8_offset = size;
606615
data[size++] = 0x00;
607616
}
617+
if (B->Traps.size() > 0)
618+
{
619+
handler = config->handler = (uint32_t)(size - config_offset);
620+
// lea config(%rip), %rcx
621+
// jmp _hander
622+
data[size++] = 0x48; data[size++] = 0x8D; data[size++] = 0x0D;
623+
config_rel32 = -(int32_t)((size + sizeof(int32_t)) - config_offset);
624+
memcpy(data + size, &config_rel32, sizeof(config_rel32));
625+
size += sizeof(config_rel32);
626+
data[size++] = 0xEB;
627+
handler_rel8_offset = size;
628+
data[size++] = 0x00;
629+
}
608630

609631
intptr_t entry = (option_loader_base + (size - config_offset));
610632
if (option_trap_entry)
@@ -643,6 +665,12 @@ size_t emitElf(Binary *B, const MappingSet &mappings, size_t mapping_size)
643665
/*_fini() offset=*/16);
644666
data[fini_rel8_offset] = (uint8_t)fini_rel8;
645667
}
668+
if (handler != 0x0)
669+
{
670+
int8_t handler_rel8 = (int8_t)(size - handler_rel8_offset - 1 +
671+
/*_handler() offset=*/24);
672+
data[handler_rel8_offset] = (uint8_t)handler_rel8;
673+
}
646674

647675
memcpy(data + size, e9loader_elf_bin, sizeof(e9loader_elf_bin));
648676
size += sizeof(e9loader_elf_bin);

src/e9patch/e9loader.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ struct e9_map_s
4848
uint32_t abs:1; // Absolute?
4949
};
5050

51+
struct e9_trap_s
52+
{
53+
intptr_t rip; // Trap location
54+
intptr_t trampoline; // Trampoline location
55+
};
56+
5157
struct e9_config_s
5258
{
5359
char magic[8]; // "E9PATCH\0"
@@ -64,6 +70,9 @@ struct e9_config_s
6470
uint32_t inits; // Init functions offset
6571
uint32_t num_finis; // # Fini functions
6672
uint32_t finis; // Fini functions offset
73+
uint32_t num_traps; // # Trap functions
74+
uint32_t traps; // Trap functions offset
75+
uint32_t handler; // Trap handler function
6776
};
6877

6978
/*

0 commit comments

Comments
 (0)