1 /*
2 
3    american fuzzy lop - extract tokens passed to strcmp / memcmp
4    -------------------------------------------------------------
5 
6    Written and maintained by Michal Zalewski <lcamtuf@google.com>
7 
8    Copyright 2016 Google Inc. All rights reserved.
9 
10    Licensed under the Apache License, Version 2.0 (the "License");
11    you may not use this file except in compliance with the License.
12    You may obtain a copy of the License at:
13 
14      http://www.apache.org/licenses/LICENSE-2.0
15 
16    This Linux-only companion library allows you to instrument strcmp(),
17    memcmp(), and related functions to automatically extract tokens.
18    See README.tokencap for more info.
19 
20  */
21 
22 #include <stdio.h>
23 #include <string.h>
24 #include <ctype.h>
25 
26 #include "../types.h"
27 #include "../config.h"
28 
29 #ifndef __linux__
30 #  error "Sorry, this library is Linux-specific for now!"
31 #endif /* !__linux__ */
32 
33 
34 /* Mapping data and such */
35 
36 #define MAX_MAPPINGS 1024
37 
38 static struct mapping {
39   void *st, *en;
40 } __tokencap_ro[MAX_MAPPINGS];
41 
42 static u32   __tokencap_ro_cnt;
43 static u8    __tokencap_ro_loaded;
44 static FILE* __tokencap_out_file;
45 
46 
47 /* Identify read-only regions in memory. Only parameters that fall into these
48    ranges are worth dumping when passed to strcmp() and so on. Read-write
49    regions are far more likely to contain user input instead. */
50 
__tokencap_load_mappings(void)51 static void __tokencap_load_mappings(void) {
52 
53   u8 buf[MAX_LINE];
54   FILE* f = fopen("/proc/self/maps", "r");
55 
56   __tokencap_ro_loaded = 1;
57 
58   if (!f) return;
59 
60   while (fgets(buf, MAX_LINE, f)) {
61 
62     u8 rf, wf;
63     void* st, *en;
64 
65     if (sscanf(buf, "%p-%p %c%c", &st, &en, &rf, &wf) != 4) continue;
66     if (wf == 'w' || rf != 'r') continue;
67 
68     __tokencap_ro[__tokencap_ro_cnt].st = (void*)st;
69     __tokencap_ro[__tokencap_ro_cnt].en = (void*)en;
70 
71     if (++__tokencap_ro_cnt == MAX_MAPPINGS) break;
72 
73   }
74 
75   fclose(f);
76 
77 }
78 
79 
80 /* Check an address against the list of read-only mappings. */
81 
__tokencap_is_ro(const void * ptr)82 static u8 __tokencap_is_ro(const void* ptr) {
83 
84   u32 i;
85 
86   if (!__tokencap_ro_loaded) __tokencap_load_mappings();
87 
88   for (i = 0; i < __tokencap_ro_cnt; i++)
89     if (ptr >= __tokencap_ro[i].st && ptr <= __tokencap_ro[i].en) return 1;
90 
91   return 0;
92 
93 }
94 
95 
96 /* Dump an interesting token to output file, quoting and escaping it
97    properly. */
98 
__tokencap_dump(const u8 * ptr,size_t len,u8 is_text)99 static void __tokencap_dump(const u8* ptr, size_t len, u8 is_text) {
100 
101   u8 buf[MAX_AUTO_EXTRA * 4 + 1];
102   u32 i;
103   u32 pos = 0;
104 
105   if (len < MIN_AUTO_EXTRA || len > MAX_AUTO_EXTRA || !__tokencap_out_file)
106     return;
107 
108   for (i = 0; i < len; i++) {
109 
110     if (is_text && !ptr[i]) break;
111 
112     switch (ptr[i]) {
113 
114       case 0 ... 31:
115       case 127 ... 255:
116       case '\"':
117       case '\\':
118 
119         sprintf(buf + pos, "\\x%02x", ptr[i]);
120         pos += 4;
121         break;
122 
123       default:
124 
125         buf[pos++] = ptr[i];
126 
127     }
128 
129   }
130 
131   buf[pos] = 0;
132 
133   fprintf(__tokencap_out_file, "\"%s\"\n", buf);
134 
135 }
136 
137 
138 /* Replacements for strcmp(), memcmp(), and so on. Note that these will be used
139    only if the target is compiled with -fno-builtins and linked dynamically. */
140 
141 #undef strcmp
142 
strcmp(const char * str1,const char * str2)143 int strcmp(const char* str1, const char* str2) {
144 
145   if (__tokencap_is_ro(str1)) __tokencap_dump(str1, strlen(str1), 1);
146   if (__tokencap_is_ro(str2)) __tokencap_dump(str2, strlen(str2), 1);
147 
148   while (1) {
149 
150     unsigned char c1 = *str1, c2 = *str2;
151 
152     if (c1 != c2) return (c1 > c2) ? 1 : -1;
153     if (!c1) return 0;
154     str1++; str2++;
155 
156   }
157 
158 }
159 
160 
161 #undef strncmp
162 
strncmp(const char * str1,const char * str2,size_t len)163 int strncmp(const char* str1, const char* str2, size_t len) {
164 
165   if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1);
166   if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1);
167 
168   while (len--) {
169 
170     unsigned char c1 = *str1, c2 = *str2;
171 
172     if (!c1) return 0;
173     if (c1 != c2) return (c1 > c2) ? 1 : -1;
174     str1++; str2++;
175 
176   }
177 
178   return 0;
179 
180 }
181 
182 
183 #undef strcasecmp
184 
strcasecmp(const char * str1,const char * str2)185 int strcasecmp(const char* str1, const char* str2) {
186 
187   if (__tokencap_is_ro(str1)) __tokencap_dump(str1, strlen(str1), 1);
188   if (__tokencap_is_ro(str2)) __tokencap_dump(str2, strlen(str2), 1);
189 
190   while (1) {
191 
192     unsigned char c1 = tolower(*str1), c2 = tolower(*str2);
193 
194     if (c1 != c2) return (c1 > c2) ? 1 : -1;
195     if (!c1) return 0;
196     str1++; str2++;
197 
198   }
199 
200 }
201 
202 
203 #undef strncasecmp
204 
strncasecmp(const char * str1,const char * str2,size_t len)205 int strncasecmp(const char* str1, const char* str2, size_t len) {
206 
207   if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1);
208   if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1);
209 
210   while (len--) {
211 
212     unsigned char c1 = tolower(*str1), c2 = tolower(*str2);
213 
214     if (!c1) return 0;
215     if (c1 != c2) return (c1 > c2) ? 1 : -1;
216     str1++; str2++;
217 
218   }
219 
220   return 0;
221 
222 }
223 
224 
225 #undef memcmp
226 
memcmp(const void * mem1,const void * mem2,size_t len)227 int memcmp(const void* mem1, const void* mem2, size_t len) {
228 
229   if (__tokencap_is_ro(mem1)) __tokencap_dump(mem1, len, 0);
230   if (__tokencap_is_ro(mem2)) __tokencap_dump(mem2, len, 0);
231 
232   while (len--) {
233 
234     unsigned char c1 = *(const char*)mem1, c2 = *(const char*)mem2;
235     if (c1 != c2) return (c1 > c2) ? 1 : -1;
236     mem1++; mem2++;
237 
238   }
239 
240   return 0;
241 
242 }
243 
244 
245 #undef strstr
246 
strstr(const char * haystack,const char * needle)247 char* strstr(const char* haystack, const char* needle) {
248 
249   if (__tokencap_is_ro(haystack))
250     __tokencap_dump(haystack, strlen(haystack), 1);
251 
252   if (__tokencap_is_ro(needle))
253     __tokencap_dump(needle, strlen(needle), 1);
254 
255   do {
256     const char* n = needle;
257     const char* h = haystack;
258 
259     while(*n && *h && *n == *h) n++, h++;
260 
261     if(!*n) return (char*)haystack;
262 
263   } while (*(haystack++));
264 
265   return 0;
266 
267 }
268 
269 
270 #undef strcasestr
271 
strcasestr(const char * haystack,const char * needle)272 char* strcasestr(const char* haystack, const char* needle) {
273 
274   if (__tokencap_is_ro(haystack))
275     __tokencap_dump(haystack, strlen(haystack), 1);
276 
277   if (__tokencap_is_ro(needle))
278     __tokencap_dump(needle, strlen(needle), 1);
279 
280   do {
281 
282     const char* n = needle;
283     const char* h = haystack;
284 
285     while(*n && *h && tolower(*n) == tolower(*h)) n++, h++;
286 
287     if(!*n) return (char*)haystack;
288 
289   } while(*(haystack++));
290 
291   return 0;
292 
293 }
294 
295 
296 /* Init code to open the output file (or default to stderr). */
297 
__tokencap_init(void)298 __attribute__((constructor)) void __tokencap_init(void) {
299 
300   u8* fn = getenv("AFL_TOKEN_FILE");
301   if (fn) __tokencap_out_file = fopen(fn, "a");
302   if (!__tokencap_out_file) __tokencap_out_file = stderr;
303 
304 }
305 
306