-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathwebkitdebug.s
316 lines (243 loc) · 7.85 KB
/
webkitdebug.s
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
.arm
.text
/*
Build with: arm-none-eabi-as -o webkitdebug.elf webkitdebug.s && arm-none-eabi-objcopy -O binary webkitdebug.elf webkitdebug.bin
0x0FFF8000 structure:
+0: Reserved.
+4: Code to jump to for executing the original webkit_free code. This should be 8-bytes following the "push" instruction.
+8: Base address for the memalloc area. The 0x1000-bytes before this should be unmapped.
+12: Size of the memalloc area.
+16: Address which will be used for the next memalloc.
The above structure must be initialized before any of this code is executed. The memalloc area *must* be located at an address higher than the normal webkit heap.
After initializing that^, the code called by wkc_malloc/wkc_free must be overwritten with code for jumping to the below code. This could be used with non-webkit heap as well with some adjustments.
For example, with this binary being stored at 0x00100000, you can write "0xe51ff004 0x00100000" for the above malloc code, and "0xe51ff004 0x00100004" for the above free code.
This was inspired by: https://developer.apple.com/library/mac/documentation/Performance/Conceptual/ManagingMemory/Articles/MallocDebug.html
NOTE: Originally each allocation had a page-guard before/after the allocated memory. This was disabled due to running out of memory.
This is used for overriding the memory allocation/free functions with the below code. Each allocation will use a dedicated set of memory page(s). Hence, for example, an allocation less than 0x100-bytes will result in 0x1000-bytes being allocated with svcControlMemory. Right before the returned buffer is a 12-byte chunk header. Prior to that is unmapped memory(page-guard), likewise for the end of the allocated buffer. This will never allocate memory at the same vaddr more than once.
This will trigger crashes when there's no more memory available in the memalloc area, or when svcControlMemory fails.
When using this with spider, patching the spider exheader so that memregion=APPLICATION and reslimit=0(APPLICATION) is highly recommended, since there's not much free memory available otherwise.
Hence, this can be used for detecting the following:
Use-after-free: Once memory is freed, that memory will never be mapped again at that vaddr. Hence, if any use-after-free memory r/w access occur, a crash would instantly trigger.
Out-of-bounds memory access / buffer overflow: There's a 0x1000-byte page-guard(unmapped memory) right before the chunk header located before each buffer, and at mappedmem+alignedpagesize. Hence, if any memory access goes out-of-bounds enough to access those guards, a crash will instantly trigger. If out-of-bounds memwrite corrupts the last word in the chunk header(wkc_malloc()-4) without accessing the page-guard, the memfree code below will trigger a crash once the memory is attempted to be freed. There's also a magicnum stored @ <actualbuffer_addr> + <actualbuffer_size>, the memfree code below will trigger a crash if this is corrupted.
Double-free / invalid memptr free: A crash will be triggered by the below memfree code if freeing memory with svcControlMemory fails.
TODO: Figure out some way to automatically set this up during browser boot, since doing it manually after startup will miss the allocations from browser startup.
Also, even after this is setup, it won't catch all memory allocation/freeing.
*/
_start:
webkitmalloc_jump:
b malloc
webkitfree_jump:
b wkc_free
webkitrealloc_jump:
b wkc_realloc
fastmalloc_reallocjump:
b fastmalloc_realloc
malloc:
mov r1, r0
add r1, r1, #16
ldr r2, =0xfff
add r1, r1, r2
bic r1, r1, r2 @ r1 = Total allocated aligned size with header.
push {r0, r1}
ldr r3, =0x0FFF8000
ldr r2, [r3, #8]
ldr r3, [r3, #12]
add r2, r2, r3 @ r2 = endaddr of our memalloc area.
ldr r3, =0x0FFF8000
ldr r3, [r3, #16]
push {r5}
//ldr r5, =0x1000
mov r5, r0
add r5, r5, r1 @ r5 = total_aligned_size with page-guard.
add r3, r3, r5 @ r3 = Endaddr of the memalloc including the page-guard.
pop {r5}
cmp r3, r2 @ Trigger a crash when there's no more memory available in our memalloc area.
ldrcs r4, =0xa4a4a4a4
strcs r3, [r4]
push {r4}
mov r0, #3 @ operation
mov r4, #3 @ permissions
mov r3, r1 @ size
ldr r1, =0x0FFF8000 @ addr0
ldr r1, [r1, #16]
mov r2, #0 @ addr1
svc 0x01
pop {r4}
cmp r0, #0 @ Trigger a crash when memalloc fails.
ldrne r3, =0x90909090
strne r0, [r3]
ldr r0, =0x0FFF8000
ldr r0, [r0, #16]
pop {r2, r3}
str r2, [r0, #0] @ Initialize the chunk header.
str r3, [r0, #4]
ldr r1, =0x42445457
str r1, [r0, #8] @ Store the magicnum at the end of the header to catch chunk corruption in the memfree code below(this is done because the only mapped memory is after this chunk, not before).
mov r1, r0
add r1, r1, #12
add r2, r2, r1
ldr r3, =0x40506073
malloc_meminit:
str r3, [r1], #4
cmp r1, r2
bcc malloc_meminit
ldr r2, [r0, #0]
ldr r3, [r0, #4]
mov r1, r0
add r1, r1, #12
add r1, r1, r2
ldr r2, =0x45425457
str r2, [r1] @ Initialize the magicnum @ <actualbuffer_addr> + <actualbuffer_size>.
//ldr r2, =0x1000
mov r2, #0
add r2, r2, r3
add r2, r2, r0
ldr r3, =0x0FFF8000
str r2, [r3, #16] @ Update the ptr used for the next memalloc.
add r0, r0, #12
bx lr
.pool
free:
cmp r0, #0
bxeq lr
ldr r3, =0x0FFF8000 @ if(inputptr < <base_memalloc_addr>)return 1;
ldr r2, [r3, #8]
cmp r0, r2
movcc r0, #1
bxcc lr
sub r0, r0, #12 @ Get the actual page address, since the first 12-bytes before the actual buffer is the header.
ldr r3, =0x42445457 @ Validate the chunk magicnum, and trigger a crash if it's invalid.
ldr r2, [r0, #8]
cmp r2, r3
ldrne r4, =0xa8a8a8a8
strne r2, [r4]
ldr r2, [r0, #0] @ Validate the magicnum @ <actualbuffer_addr> + <actualbuffer_size>, and trigger a crash if it's invalid.
add r2, r2, r0
add r2, r2, #12
ldr r2, [r2]
ldr r3, =0x45425457
cmp r2, r3
//ldrne r4, =0xacacacac
//strne r2, [r4] @ Disabled because of libcurl(?) use-after-free issues.
ldr r3, [r0, #4] @ Aligned total size.
push {r4}
mov r1, r0 @ addr0
mov r0, #1 @ operation
mov r4, #0 @ permissions
mov r2, #0 @ addr1
svc 0x01
pop {r4}
cmp r0, #0 @ If everything works as intended, this should only ever happen on double-frees / invalid inputptr.
ldrne r3, =0x94949494
strne r0, [r3]
mov r0, #0
bx lr
.pool
wkc_free:
mov r1, #0
b _free
_free:
push {r0, r1, lr}
bl free
pop {r2, r3, lr}
cmp r0, #0
bxeq lr
@ Instead of letting the normal code free the memory, just overwrite the first 0x8-bytes of the buffer with junk without freeing it at all.
/*ldr r1, =0xcccccccc
str r1, [r1]*/
mov r0, r2
/*mov r1, r3
ldr r3, =0x0FFF8000
cmp r1, #0 @ wkc_free
pusheq {r4, lr}
moveq r4, r0
ldreq r3, [r3, #4]
bxeq r3*/
/*ldr r1, [r0]
lsr r1, r1, #24
cmp r1, #0
bne _free_finish
push {r0, lr}
mov r0, #0x100
bl malloc
push {r0}
ldr r1, [sp, #4]
mov r2, #0xfc
add r0, r0, #4
bl memcpy
ldr r0, [sp, #4]
ldr r1, [sp, #0]
str r0, [r1]
pop {r3}
pop {r0, lr}
_free_finish:*/
ldr r1, =0xcccccccc
add r2, r1, #1
mov r2, r3
add r3, r2, #1
stmia r0!, {r1, r2}
/*stmia r0!, {r1, r2, r3}
add r1, r1, #3
add r2, r2, #3
add r3, r3, #3*/
//str r1, [r0]
/*stmia r0!, {r1, r2, r3}
add r1, r1, #3
add r2, r2, #3
stmia r0!, {r1, r2}*/
bx lr
.pool
wkc_realloc:
mov r2, #0
b realloc
fastmalloc_realloc:
push {r0, lr}
mov r0, r1
mov r1, r2
mov r2, #1
bl realloc
pop {r3, lr}
str r0, [r3]
bx lr
realloc: @ inr0=inmemptr, inr1=size, inr2=id
push {r0, r1, r2, r4, r5, lr}
mov r4, #0
add r5, sp, #0
ldr r0, [r5, #4]
cmp r0, #0
beq realloc_skiptofree
bl malloc
mov r4, r0
ldr r1, [r5, #0]
cmp r1, #0
beq realloc_skiptofree
ldr r2, [r5, #4]
bl memcpy
realloc_skiptofree:
ldr r0, [r5, #0]
cmp r0, #0
beq realloc_finish
/*
//This is commented-out because the libcurl(?) build used with spider oss.cro, has use-after-free issues with the realloc() input memptr.
ldr r1, [sp, #8]
bl _free
#endif*/
realloc_finish:
mov r0, r4
pop {r0, r1, r2, r4, r5, pc}
.pool
memcpy:
cmp r2, #0
bxeq lr
ldr r3, [r1], #4
str r3, [r0], #4
sub r2, r2, #4
cmp r2, #4
bcs memcpy
memcpy_finishlp:
cmp r2, #0
bxeq lr
ldrb r3, [r1], #1
strb r3, [r0], #1
sub r2, r2, #1
b memcpy_finishlp