about summary refs log tree commit diff
path: root/qemu_mode/README.persistent.md
blob: ab45860dde5d61c85c33e644f22e3ef83d81e584 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# How to use the persistent mode in AFL++'s QEMU mode

## 1) Introduction

Persistent mode lets you fuzz your target persistently between two addresses -
without forking for every fuzzing attempt. This increases the speed by a factor
between x2 and x5, hence it is very, very valuable.

The persistent mode is currently only available for x86/x86_64, arm, and aarch64
targets.

## 2) How use the persistent mode

### 2.1) The START address

The start of the persistent loop has to be set with environment variable
`AFL_QEMU_PERSISTENT_ADDR`.

This address can be the address of whatever instruction. Setting this address to
the start of a function makes the usage simple. If the address is, however,
within a function, either RET, OFFSET, or EXITS (see below in 2.2, 2.3, 2.6)
have to be set. This address (as well as the RET address, see below) has to be
defined in hexadecimal with the 0x prefix or as a decimal value.

If both RET and EXITS are not set, QEMU will assume that START points to a
function and will patch the return address (on stack or in the link register) to
return to START (like WinAFL).

*Note:* If the target is compiled with position independent code (PIE/PIC) qemu
loads these to a specific base address. For 64 bit you have to add 0x4000000000
(9 zeroes) and for 32 bit 0x40000000 (7 zeroes) to the address. On strange
setups the base address set by QEMU for PIE executable may change. You can check
it printing the process map using `AFL_QEMU_DEBUG_MAPS=1 afl-qemu-trace
TARGET-BINARY`.

If this address is not valid, afl-fuzz will error during startup with the
message that the forkserver was not found.

### 2.2) The RET address

The RET address is the last instruction of the persistent loop. The emulator
will emit a jump to START when translating the instruction at RET. It is
optional and only needed if the return should not be at the end of the function
to which the START address points into, but earlier.

It is defined by setting `AFL_QEMU_PERSISTENT_RET`, and too 0x4000000000 has to
be set if the target is position independent.

### 2.3) The OFFSET

This option is valid only for x86/x86_64 only, arm/aarch64 do not save the
return address on stack.

If the START address is *not* the beginning of a function, and *no* RET has been
set (so the end of the loop will be at the end of the function but START will
not be at the beginning of it), we need an offset from the ESP pointer to locate
the return address to patch.

The value by which the ESP pointer has to be corrected has to be set in the
variable `AFL_QEMU_PERSISTENT_RETADDR_OFFSET`.

Now to get this value right here is some help:
1. Use gdb on the target.
2. Set a breakpoint to "main" (this is required for PIE/PIC binaries so the
   addresses are set up).
3. "run" the target with a valid commandline.
4. Set a breakpoint to the function in which START is contained.
5. Set a breakpoint to your START address.
6. "continue" to the function start breakpoint.
7. Print the ESP value with `print $esp` and take note of it.
8. "continue" the target until the second breakpoint.
9. Again print the ESP value.
10. Calculate the difference between the two values - and this is the offset.

### 2.4) Resetting the register state

It is very, very likely you need to restore the general purpose registers state
when starting a new loop. Because of this 99% of the time you should set

```
AFL_QEMU_PERSISTENT_GPR=1
```

An example is when you want to use `main()` as persistent START:

```c
int main(int argc, char **argv) {

  if (argc < 2) return 1;

  // do stuff

}
```

If you don't save and restore the registers in x86_64, the parameter `argc` will
be lost at the second execution of the loop.

### 2.5) Resetting the memory state

This option restores the memory state using the AFL++ Snapshot LKM if loaded.
Otherwise, all the writeable pages are restored.

To enable this option, set `AFL_QEMU_PERSISTENT_MEM=1`.

### 2.6) Reset on exit()

The user can force QEMU to set the program counter to START instead of executing
the exit_group syscall and exit the program.

The environment variable is `AFL_QEMU_PERSISTENT_EXITS`.

### 2.7) Snapshot

`AFL_QEMU_SNAPSHOT=address` is just a "syntactical sugar" environment variable
that is equivalent to the following set of variables:

```
AFL_QEMU_PERSISTENT_ADDR=address
AFL_QEMU_PERSISTENT_GPR=1
AFL_QEMU_PERSISTENT_MEM=1
AFL_QEMU_PERSISTENT_EXITS=1
```

## 3) Optional parameters

### 3.1) Loop counter value

The more stable your loop in the target, the longer you can run it, the more
unstable it is the lower the loop count should be. A low value would be 100, the
maximum value should be 10000. The default is 1000. This value can be set with
`AFL_QEMU_PERSISTENT_CNT`.

This is the same concept as in the llvm_mode persistent mode with
`__AFL_LOOP()`.

### 3.2) A hook for in-memory fuzzing

You can increase the speed of the persistent mode even more by bypassing all the
reading of the fuzzing input via a file by reading directly into the memory
address space of the target process.

All this needs is that the START address has a register that can reach the
memory buffer or that the memory buffer is at a known location. You probably
need the value of the size of the buffer (maybe it is in a register when START
is hit).

The persistent hook will execute a function on every persistent iteration (at
the start START) defined in a shared object specified with
`AFL_QEMU_PERSISTENT_HOOK=/path/to/hook.so`.

The signature is:

```c
void afl_persistent_hook(struct ARCH_regs *regs,
                         uint64_t guest_base,
                         uint8_t *input_buf,
                         uint32_t input_buf_len);
```

Where ARCH is one of x86, x86_64, arm or arm64. You have to include
`path/to/qemuafl/qemuafl/api.h`.

In this hook, you can inspect and change the saved GPR state at START.

You can also initialize your data structures when QEMU loads the shared object
with:

`int afl_persistent_hook_init(void);`

If this routine returns true, the shared mem fuzzing feature of AFL++ is used
and so the input_buf variables of the hook becomes meaningful. Otherwise, you
have to read the input from a file like stdin.

An example that you can use with little modification for your target can be
found here: [utils/qemu_persistent_hook](../utils/qemu_persistent_hook)