|
|
|
@ -33,89 +33,95 @@
|
|
|
|
|
#include "../types.h"
|
|
|
|
|
#include "../config.h"
|
|
|
|
|
|
|
|
|
|
// 检查是否为Linux系统,如果不是则报错
|
|
|
|
|
#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. */
|
|
|
|
|
|
|
|
|
|
// 功能:加载只读内存区域的映射信息
|
|
|
|
|
// 通过读取/proc/self/maps文件,识别出只读且不可写的内存区域,并存储在__tokencap_ro数组中
|
|
|
|
|
static void __tokencap_load_mappings(void) {
|
|
|
|
|
|
|
|
|
|
u8 buf[MAX_LINE];
|
|
|
|
|
FILE* f = fopen("/proc/self/maps", "r");
|
|
|
|
|
u8 buf[MAX_LINE]; // 用于存储每行读取的内存映射信息
|
|
|
|
|
FILE* f = fopen("/proc/self/maps", "r"); // 打开/proc/self/maps文件,该文件包含了当前进程的内存映射信息
|
|
|
|
|
|
|
|
|
|
__tokencap_ro_loaded = 1;
|
|
|
|
|
__tokencap_ro_loaded = 1; // 标记只读映射已加载
|
|
|
|
|
|
|
|
|
|
if (!f) return;
|
|
|
|
|
if (!f) return; // 如果文件打开失败,则直接返回
|
|
|
|
|
|
|
|
|
|
// 逐行读取文件内容
|
|
|
|
|
while (fgets(buf, MAX_LINE, f)) {
|
|
|
|
|
|
|
|
|
|
u8 rf, wf;
|
|
|
|
|
void* st, *en;
|
|
|
|
|
u8 rf, wf; // rf表示是否可读,wf表示是否可写
|
|
|
|
|
void* st, *en; // st表示内存区域的起始地址,en表示内存区域的结束地址
|
|
|
|
|
|
|
|
|
|
// 解析每行内存映射信息,提取起始地址、结束地址、是否可读和是否可写
|
|
|
|
|
if (sscanf(buf, "%p-%p %c%c", &st, &en, &rf, &wf) != 4) continue;
|
|
|
|
|
if (wf == 'w' || rf != 'r') 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);
|
|
|
|
|
fclose(f); // 关闭文件
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Check an address against the list of read-only mappings. */
|
|
|
|
|
|
|
|
|
|
// 功能:检查给定地址是否位于只读内存区域
|
|
|
|
|
// 如果未加载映射信息,则先调用__tokencap_load_mappings加载映射信息
|
|
|
|
|
static u8 __tokencap_is_ro(const void* ptr) {
|
|
|
|
|
|
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
|
|
if (!__tokencap_ro_loaded) __tokencap_load_mappings();
|
|
|
|
|
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;
|
|
|
|
|
return 0; // 如果不在任何只读内存区域内,则返回0
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Dump an interesting token to output file, quoting and escaping it
|
|
|
|
|
properly. */
|
|
|
|
|
|
|
|
|
|
// 功能:将感兴趣的数据转储到输出文件中
|
|
|
|
|
// 数据会被正确引用和转义,例如,非打印字符会被转义为\xXX的形式
|
|
|
|
|
static void __tokencap_dump(const u8* ptr, size_t len, u8 is_text) {
|
|
|
|
|
|
|
|
|
|
u8 buf[MAX_AUTO_EXTRA * 4 + 1];
|
|
|
|
|
u8 buf[MAX_AUTO_EXTRA * 4 + 1]; // 存储转义后的数据
|
|
|
|
|
u32 i;
|
|
|
|
|
u32 pos = 0;
|
|
|
|
|
u32 pos = 0; // 当前写入buf的位置
|
|
|
|
|
|
|
|
|
|
// 如果数据长度不符合要求或输出文件未打开,则直接返回
|
|
|
|
|
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:
|
|
|
|
@ -123,189 +129,237 @@ static void __tokencap_dump(const u8* ptr, size_t len, u8 is_text) {
|
|
|
|
|
case '\"':
|
|
|
|
|
case '\\':
|
|
|
|
|
|
|
|
|
|
// 对于非打印字符、双引号和反斜杠进行转义
|
|
|
|
|
sprintf(buf + pos, "\\x%02x", ptr[i]);
|
|
|
|
|
pos += 4;
|
|
|
|
|
pos += 4; // 转义后的字符串长度为4
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
|
|
// 对于可打印字符,直接复制到buf中
|
|
|
|
|
buf[pos++] = ptr[i];
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buf[pos] = 0;
|
|
|
|
|
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. */
|
|
|
|
|
|
|
|
|
|
// 功能:替换strcmp函数,用于识别并转储只读内存区域中的字符串
|
|
|
|
|
// 如果目标程序编译时使用了-fno-builtins选项并动态链接,则会使用此函数
|
|
|
|
|
#undef strcmp
|
|
|
|
|
|
|
|
|
|
int strcmp(const char* str1, const char* str2) {
|
|
|
|
|
|
|
|
|
|
// 检查str1是否位于只读内存区域,如果是,则转储其内容
|
|
|
|
|
if (__tokencap_is_ro(str1)) __tokencap_dump(str1, strlen(str1), 1);
|
|
|
|
|
// 检查str2是否位于只读内存区域,如果是,则转储其内容
|
|
|
|
|
if (__tokencap_is_ro(str2)) __tokencap_dump(str2, strlen(str2), 1);
|
|
|
|
|
|
|
|
|
|
// 实现strcmp函数的核心功能:比较两个字符串
|
|
|
|
|
while (1) {
|
|
|
|
|
|
|
|
|
|
unsigned char c1 = *str1, c2 = *str2;
|
|
|
|
|
|
|
|
|
|
// 如果两个字符不同,则返回比较结果
|
|
|
|
|
if (c1 != c2) return (c1 > c2) ? 1 : -1;
|
|
|
|
|
// 如果遇到字符串结束符,则返回0
|
|
|
|
|
if (!c1) return 0;
|
|
|
|
|
// 指向下一个字符
|
|
|
|
|
str1++; str2++;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 取消定义原本的strncmp函数,以便重新定义
|
|
|
|
|
#undef strncmp
|
|
|
|
|
|
|
|
|
|
// 自定义的strncmp函数,用于比较两个字符串的前len个字符
|
|
|
|
|
int strncmp(const char* str1, const char* str2, size_t len) {
|
|
|
|
|
|
|
|
|
|
// 检查str1是否为只读内存,如果是,则输出内存内容
|
|
|
|
|
if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1);
|
|
|
|
|
// 检查str2是否为只读内存,如果是,则输出内存内容
|
|
|
|
|
if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1);
|
|
|
|
|
|
|
|
|
|
// 循环比较两个字符串的前len个字符
|
|
|
|
|
while (len--) {
|
|
|
|
|
|
|
|
|
|
unsigned char c1 = *str1, c2 = *str2;
|
|
|
|
|
unsigned char c1 = *str1, c2 = *str2; // 获取当前字符
|
|
|
|
|
|
|
|
|
|
// 如果str1的第一个字符为0(即字符串结束),则返回0
|
|
|
|
|
if (!c1) return 0;
|
|
|
|
|
// 如果当前字符不相等,根据字符的ASCII值大小返回1或-1
|
|
|
|
|
if (c1 != c2) return (c1 > c2) ? 1 : -1;
|
|
|
|
|
str1++; str2++;
|
|
|
|
|
str1++; str2++; // 移动到下一个字符
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果循环结束,说明前len个字符都相等,返回0
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 取消定义原本的strcasecmp函数,以便重新定义
|
|
|
|
|
#undef strcasecmp
|
|
|
|
|
|
|
|
|
|
// 自定义的strcasecmp函数,用于忽略大小写的字符串比较
|
|
|
|
|
int strcasecmp(const char* str1, const char* str2) {
|
|
|
|
|
|
|
|
|
|
// 检查str1是否为只读内存,如果是,则输出内存内容
|
|
|
|
|
if (__tokencap_is_ro(str1)) __tokencap_dump(str1, strlen(str1), 1);
|
|
|
|
|
// 检查str2是否为只读内存,如果是,则输出内存内容
|
|
|
|
|
if (__tokencap_is_ro(str2)) __tokencap_dump(str2, strlen(str2), 1);
|
|
|
|
|
|
|
|
|
|
// 无限循环,逐字符比较两个字符串(忽略大小写)
|
|
|
|
|
while (1) {
|
|
|
|
|
|
|
|
|
|
unsigned char c1 = tolower(*str1), c2 = tolower(*str2);
|
|
|
|
|
unsigned char c1 = tolower(*str1), c2 = tolower(*str2); // 转换为小写后比较
|
|
|
|
|
|
|
|
|
|
// 如果当前字符不相等,根据字符的ASCII值大小返回1或-1
|
|
|
|
|
if (c1 != c2) return (c1 > c2) ? 1 : -1;
|
|
|
|
|
// 如果str1的第一个字符为0(即字符串结束),则返回0
|
|
|
|
|
if (!c1) return 0;
|
|
|
|
|
str1++; str2++;
|
|
|
|
|
str1++; str2++; // 移动到下一个字符
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 取消定义原本的strncasecmp函数,以便重新定义
|
|
|
|
|
#undef strncasecmp
|
|
|
|
|
|
|
|
|
|
// 自定义的strncasecmp函数,用于忽略大小写的字符串前len个字符比较
|
|
|
|
|
int strncasecmp(const char* str1, const char* str2, size_t len) {
|
|
|
|
|
|
|
|
|
|
// 检查str1是否为只读内存,如果是,则输出内存内容
|
|
|
|
|
if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1);
|
|
|
|
|
// 检查str2是否为只读内存,如果是,则输出内存内容
|
|
|
|
|
if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1);
|
|
|
|
|
|
|
|
|
|
// 循环比较两个字符串的前len个字符(忽略大小写)
|
|
|
|
|
while (len--) {
|
|
|
|
|
|
|
|
|
|
unsigned char c1 = tolower(*str1), c2 = tolower(*str2);
|
|
|
|
|
unsigned char c1 = tolower(*str1), c2 = tolower(*str2); // 转换为小写后比较
|
|
|
|
|
|
|
|
|
|
// 如果str1的第一个字符为0(即字符串结束),则返回0
|
|
|
|
|
if (!c1) return 0;
|
|
|
|
|
// 如果当前字符不相等,根据字符的ASCII值大小返回1或-1
|
|
|
|
|
if (c1 != c2) return (c1 > c2) ? 1 : -1;
|
|
|
|
|
str1++; str2++;
|
|
|
|
|
str1++; str2++; // 移动到下一个字符
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果循环结束,说明前len个字符都相等,返回0
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 取消定义原本的memcmp函数,以便重新定义
|
|
|
|
|
#undef memcmp
|
|
|
|
|
|
|
|
|
|
// 自定义的memcmp函数,用于比较两个内存区域的前len个字节
|
|
|
|
|
int memcmp(const void* mem1, const void* mem2, size_t len) {
|
|
|
|
|
|
|
|
|
|
// 检查mem1是否为只读内存,如果是,则输出内存内容
|
|
|
|
|
if (__tokencap_is_ro(mem1)) __tokencap_dump(mem1, len, 0);
|
|
|
|
|
// 检查mem2是否为只读内存,如果是,则输出内存内容
|
|
|
|
|
if (__tokencap_is_ro(mem2)) __tokencap_dump(mem2, len, 0);
|
|
|
|
|
|
|
|
|
|
// 循环比较两个内存区域的前len个字节
|
|
|
|
|
while (len--) {
|
|
|
|
|
|
|
|
|
|
unsigned char c1 = *(const char*)mem1, c2 = *(const char*)mem2;
|
|
|
|
|
unsigned char c1 = *(const char*)mem1, c2 = *(const char*)mem2; // 获取当前字节
|
|
|
|
|
// 如果当前字节不相等,根据字节的ASCII值大小返回1或-1
|
|
|
|
|
if (c1 != c2) return (c1 > c2) ? 1 : -1;
|
|
|
|
|
mem1++; mem2++;
|
|
|
|
|
mem1++; mem2++; // 移动到下一个字节
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果循环结束,说明前len个字节都相等,返回0
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 取消定义原本的strstr函数,以便重新定义
|
|
|
|
|
#undef strstr
|
|
|
|
|
|
|
|
|
|
// 自定义的strstr函数,用于在haystack字符串中查找needle字符串
|
|
|
|
|
char* strstr(const char* haystack, const char* needle) {
|
|
|
|
|
|
|
|
|
|
// 检查haystack是否为只读内存,如果是,则输出内存内容
|
|
|
|
|
if (__tokencap_is_ro(haystack))
|
|
|
|
|
__tokencap_dump(haystack, strlen(haystack), 1);
|
|
|
|
|
|
|
|
|
|
// 检查needle是否为只读内存,如果是,则输出内存内容
|
|
|
|
|
if (__tokencap_is_ro(needle))
|
|
|
|
|
__tokencap_dump(needle, strlen(needle), 1);
|
|
|
|
|
|
|
|
|
|
// 在haystack中查找needle
|
|
|
|
|
do {
|
|
|
|
|
const char* n = needle;
|
|
|
|
|
const char* h = haystack;
|
|
|
|
|
|
|
|
|
|
// 逐字符比较haystack和needle
|
|
|
|
|
while(*n && *h && *n == *h) n++, h++;
|
|
|
|
|
|
|
|
|
|
// 如果needle的所有字符都被匹配,返回匹配开始的位置
|
|
|
|
|
if(!*n) return (char*)haystack;
|
|
|
|
|
|
|
|
|
|
} while (*(haystack++));
|
|
|
|
|
} while (*(haystack++)); // 移动到haystack的下一个字符并继续查找
|
|
|
|
|
|
|
|
|
|
// 如果没有找到needle,返回NULL
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 取消定义原本的strcasestr函数,以便重新定义
|
|
|
|
|
#undef strcasestr
|
|
|
|
|
|
|
|
|
|
// 自定义的strcasestr函数,用于忽略大小写的字符串查找
|
|
|
|
|
char* strcasestr(const char* haystack, const char* needle) {
|
|
|
|
|
|
|
|
|
|
// 检查haystack是否为只读内存,如果是,则输出内存内容
|
|
|
|
|
if (__tokencap_is_ro(haystack))
|
|
|
|
|
__tokencap_dump(haystack, strlen(haystack), 1);
|
|
|
|
|
|
|
|
|
|
// 检查needle是否为只读内存,如果是,则输出内存内容
|
|
|
|
|
if (__tokencap_is_ro(needle))
|
|
|
|
|
__tokencap_dump(needle, strlen(needle), 1);
|
|
|
|
|
|
|
|
|
|
// 在haystack中查找needle(忽略大小写)
|
|
|
|
|
do {
|
|
|
|
|
|
|
|
|
|
const char* n = needle;
|
|
|
|
|
const char* h = haystack;
|
|
|
|
|
|
|
|
|
|
// 逐字符比较haystack和needle(忽略大小写)
|
|
|
|
|
while(*n && *h && tolower(*n) == tolower(*h)) n++, h++;
|
|
|
|
|
|
|
|
|
|
// 如果needle的所有字符都被匹配,返回匹配开始的位置
|
|
|
|
|
if(!*n) return (char*)haystack;
|
|
|
|
|
|
|
|
|
|
} while(*(haystack++));
|
|
|
|
|
} while(*(haystack++)); // 移动到haystack的下一个字符并继续查找
|
|
|
|
|
|
|
|
|
|
// 如果没有找到needle,返回NULL
|
|
|
|
|
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");
|
|
|
|
|
u8* fn = getenv("AFL_TOKEN_FILE"); // 获取环境变量AFL_TOKEN_FILE的值
|
|
|
|
|
// 如果环境变量存在,则以追加模式打开文件
|
|
|
|
|
if (fn) __tokencap_out_file = fopen(fn, "a");
|
|
|
|
|
// 如果文件打开失败,则将输出重定向到标准错误输出
|
|
|
|
|
if (!__tokencap_out_file) __tokencap_out_file = stderr;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|