main^2
em0 8 months ago
parent 7f8464c6db
commit 33fad2a6fa

@ -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;
}

Loading…
Cancel
Save