/* Copyright 2016 Google LLC All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* american fuzzy lop - extract tokens passed to strcmp / memcmp ------------------------------------------------------------- Written and maintained by Michal Zalewski This Linux-only companion library allows you to instrument strcmp(), memcmp(), and related functions to automatically extract tokens. See README.tokencap for more info. */ #include #include #include #include "../types.h" #include "../config.h" #ifndef __linux__ # error "Sorry, this library is Linux-specific for now!" #endif /* !__linux__ */ /* Mapping data and such */ #define MAX_MAPPINGS 1024 static struct mapping { void *st, *en; } __tokencap_ro[MAX_MAPPINGS]; static u32 __tokencap_ro_cnt; static u8 __tokencap_ro_loaded; static FILE* __tokencap_out_file; /* Identify read-only regions in memory. Only parameters that fall into these ranges are worth dumping when passed to strcmp() and so on. Read-write regions are far more likely to contain user input instead. */ static void __tokencap_load_mappings(void) { u8 buf[MAX_LINE]; FILE* f = fopen("/proc/self/maps", "r"); __tokencap_ro_loaded = 1; if (!f) return; while (fgets(buf, MAX_LINE, f)) { u8 rf, wf; void* st, *en; if (sscanf(buf, "%p-%p %c%c", &st, &en, &rf, &wf) != 4) continue; if (wf == 'w' || rf != 'r') continue; __tokencap_ro[__tokencap_ro_cnt].st = (void*)st; __tokencap_ro[__tokencap_ro_cnt].en = (void*)en; if (++__tokencap_ro_cnt == MAX_MAPPINGS) break; } fclose(f); } /* Check an address against the list of read-only mappings. */ static u8 __tokencap_is_ro(const void* ptr) { u32 i; if (!__tokencap_ro_loaded) __tokencap_load_mappings(); for (i = 0; i < __tokencap_ro_cnt; i++) if (ptr >= __tokencap_ro[i].st && ptr <= __tokencap_ro[i].en) return 1; return 0; } /* Dump an interesting token to output file, quoting and escaping it properly. */ static void __tokencap_dump(const u8* ptr, size_t len, u8 is_text) { u8 buf[MAX_AUTO_EXTRA * 4 + 1]; u32 i; u32 pos = 0; if (len < MIN_AUTO_EXTRA || len > MAX_AUTO_EXTRA || !__tokencap_out_file) return; for (i = 0; i < len; i++) { if (is_text && !ptr[i]) break; switch (ptr[i]) { case 0 ... 31: case 127 ... 255: case '\"': case '\\': sprintf(buf + pos, "\\x%02x", ptr[i]); pos += 4; break; default: buf[pos++] = ptr[i]; } } buf[pos] = 0; fprintf(__tokencap_out_file, "\"%s\"\n", buf); } /* Replacements for strcmp(), memcmp(), and so on. Note that these will be used only if the target is compiled with -fno-builtins and linked dynamically. */ #undef strcmp int strcmp(const char* str1, const char* str2) { if (__tokencap_is_ro(str1)) __tokencap_dump(str1, strlen(str1), 1); if (__tokencap_is_ro(str2)) __tokencap_dump(str2, strlen(str2), 1); while (1) { unsigned char c1 = *str1, c2 = *str2; if (c1 != c2) return (c1 > c2) ? 1 : -1; if (!c1) return 0; str1++; str2++; } } #undef strncmp int strncmp(const char* str1, const char* str2, size_t len) { if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1); if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1); while (len--) { unsigned char c1 = *str1, c2 = *str2; if (!c1) return 0; if (c1 != c2) return (c1 > c2) ? 1 : -1; str1++; str2++; } return 0; } #undef strcasecmp int strcasecmp(const char* str1, const char* str2) { if (__tokencap_is_ro(str1)) __tokencap_dump(str1, strlen(str1), 1); if (__tokencap_is_ro(str2)) __tokencap_dump(str2, strlen(str2), 1); while (1) { unsigned char c1 = tolower(*str1), c2 = tolower(*str2); if (c1 != c2) return (c1 > c2) ? 1 : -1; if (!c1) return 0; str1++; str2++; } } #undef strncasecmp int strncasecmp(const char* str1, const char* str2, size_t len) { if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1); if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1); while (len--) { unsigned char c1 = tolower(*str1), c2 = tolower(*str2); if (!c1) return 0; if (c1 != c2) return (c1 > c2) ? 1 : -1; str1++; str2++; } return 0; } #undef memcmp int memcmp(const void* mem1, const void* mem2, size_t len) { if (__tokencap_is_ro(mem1)) __tokencap_dump(mem1, len, 0); if (__tokencap_is_ro(mem2)) __tokencap_dump(mem2, len, 0); while (len--) { unsigned char c1 = *(const char*)mem1, c2 = *(const char*)mem2; if (c1 != c2) return (c1 > c2) ? 1 : -1; mem1++; mem2++; } return 0; } #undef strstr char* strstr(const char* haystack, const char* needle) { if (__tokencap_is_ro(haystack)) __tokencap_dump(haystack, strlen(haystack), 1); if (__tokencap_is_ro(needle)) __tokencap_dump(needle, strlen(needle), 1); do { const char* n = needle; const char* h = haystack; while(*n && *h && *n == *h) n++, h++; if(!*n) return (char*)haystack; } while (*(haystack++)); return 0; } #undef strcasestr char* strcasestr(const char* haystack, const char* needle) { if (__tokencap_is_ro(haystack)) __tokencap_dump(haystack, strlen(haystack), 1); if (__tokencap_is_ro(needle)) __tokencap_dump(needle, strlen(needle), 1); do { const char* n = needle; const char* h = haystack; while(*n && *h && tolower(*n) == tolower(*h)) n++, h++; if(!*n) return (char*)haystack; } while(*(haystack++)); return 0; } /* Init code to open the output file (or default to stderr). */ __attribute__((constructor)) void __tokencap_init(void) { u8* fn = getenv("AFL_TOKEN_FILE"); if (fn) __tokencap_out_file = fopen(fn, "a"); if (!__tokencap_out_file) __tokencap_out_file = stderr; }