From 33fad2a6fae7fc44a881c73e4d912797f5e84e49 Mon Sep 17 00:00:00 2001 From: em0 <1834530227@qq.com> Date: Wed, 8 Jan 2025 22:31:01 +0800 Subject: [PATCH] libtoken --- src/libtokencap/libtokencap.so.c | 158 +++++++++++++++++++++---------- 1 file changed, 106 insertions(+), 52 deletions(-) diff --git a/src/libtokencap/libtokencap.so.c b/src/libtokencap/libtokencap.so.c index 6abdf23..635ba94 100644 --- a/src/libtokencap/libtokencap.so.c +++ b/src/libtokencap/libtokencap.so.c @@ -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; }