逆向 string.h 函数库 strlen、memchr、strcat 函数

strlen 函数

  • 主要功能:返回字符串的长度
  • C/C++ 实现:
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std; int main(int argc, char **agrv)
{
// OD 字符串查找,便于定位 main 函数
char a[20] = "AAAAAAAAAAAAAA";
const char str[] = "http://www.runoob.com";
int ret = strlen(str); cout << "字符串的长度为: " << ret << endl;
return(0);
}
  • strlen 函数运行结果:

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 逆向分析:将程序载入 OD 后,查找 ‘AAAAA…’ 字符串的位置(由于程序是由 VS2017 编写,所以 main 函数初始化较为复杂,直接跳过即可),下图中的 NpCxyFw.00411091 函数就是 strlen 函数,push eax 压入的是需要计算大小的字符串 str,F7 进入 strlen 函数

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 由于这个函数比较简单,所以代码量不是很大:

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 流程图如下图所示:

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 主要算法:字符串对齐算法 + 判断字符串结束算法

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 字符串对齐算法:该算法的主要目的是判断字符串是否对于 CPU 对齐,也就是说是否字符串的开头位置是否对于 DWORD 字节对齐。首先会使用 test ecx,3 对字符串的头地址进行按位与操作,判断是否进行了数据对齐,如果数据对齐跳转就会实现

    逆向 string.h 函数库 strlen、memchr、strcat 函数

    逆向 string.h 函数库 strlen、memchr、strcat 函数

注:为什么使用 test ecx,3 能判断数据是否对齐呢,假如字符串的首地址是 0019FEC4,那么二进制表示就为 110011111111011000100,而 3 的二进制是 011,那么最后两位就不为 0,表示数据已经对齐:

逆向 string.h 函数库 strlen、memchr、strcat 函数

那么如果字符串的首地址是 0019FEC3,二进制表达为 110011111111011000011,这样的话数据就不会对齐:

逆向 string.h 函数库 strlen、memchr、strcat 函数

  • 假如跳转没有实现,就循环的将 ecx 中储存的字符串头指针往右移动,每次移动一个字节,直到数据对齐为止。值得注意的是期间会使用 test al,al 判断 al 是否为 0,表示字符串是否到达结尾

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 判断字符串结束算法:这个算法就比较有趣了,主要分为两个步骤,首先在数据对齐后会跳转到 0x008F6E0,之后从头开始循环取出 4 个字节大小数据存放在 eax 中,那么字符串 str 就被分成了 6 部分 http ://w ww.r unoo b.co m,之后通过 (eax + 7EFEFEFF) ^ (eax ^ -1) 计算出的结果与 81010100 做 test 判断,判断指定的位是否为 0
  • 为什么要这么做呢,意义主要是判断字符是否含有字符串结尾标志 00,如 str 字符串所示最后一个字符是 m,但由于字符串的结尾结束符是 00,所以最后取出 4 个字节就包含 00 结束标志

    逆向 string.h 函数库 strlen、memchr、strcat 函数

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 由第一个步骤可以判断出字符串的结尾处在哪里,可是这个是 4 个字节大小的判断,并没有精确到字节单位,所以第二个步骤判断字符串结尾标志 00 在 4 个字节的哪个位置(test al、ah FF0000、FF000000),并把此位置的字符串指针储存在 eax 中,表示字符串结尾指针(注意不包含 00 结尾标志)
  • 最后用字符串结尾指针 0019FED9 减去开头指针 0019FEC4 就得出了字符串的长度 21,ecx 中储存的是字符串开头指针

    逆向 string.h 函数库 strlen、memchr、strcat 函数

memchr 函数

  • 主要功能:返回指定字符在字符串中的位置
  • 参数:(1) 传入字符串 (2) 传入指定字符 (3) 字符串的长度
  • C/C++ 实现:
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std; int main(int argc, char **agrv)
{
char a[20] = "AAAAAAAAAAAAAA";
const char str[] = "http://www.baidu.com";
const char ch = '.';
char *ret = (char*)memchr(str, ch, strlen(str)); cout << ret << endl;
return(0);
}
  • memchr 函数运行结果:

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 逆向分析:首先取出字符串的长度(第三个参数)存放在 eax 中,并且判断字符串的长度是否为 0,是的话函数直接返回 0;之后取出字符串的首地址(第二个参数)存放在 edx, 然后将指定字符(第二个参数)存放在清空的 ebx 中

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 参数取完之后,判断字符串的首地址是否处于对齐状态,如果处于非对齐状态,需要进行数据对齐操作,值得注意的是在数据循环对齐期间如果碰到字符串长度为 1 则直接返回

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 接下来对判断字符串的长度是否小于 4 个字节,如果小于 4 个字节直接返回

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 之后将指定字符(第二个参数),填充满 ebx,为下面的异或操作做基础

    逆向 string.h 函数库 strlen、memchr、strcat 函数

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 这一步的操作是每隔 4 个字节读取一次字符串,读取的内容会储存在 ecx 中,之后将 ecx 带入这个表达式中 ecx = (ecx ^ FFFFFFFF) ^ (2E2E + 7EFEFEFF + (ecx ^ 2E2E2E2E)),并且和 81010100 做异或操作。这一步主要是判断每 4 个字节中是否有指定的字符 ‘.’

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 循环之后,判断刚才存在指定字符的 4 个字节中指定字符的位置,如果字符是 4 个字节中的第 2 个那么就将指针减去 3 个字节,这样就刚好指在了指定字符的位置

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 最后函数返回字符串中第一个指定字符的指针,所以函数打印出了指定字符 ‘.’ 及以后的字符

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 总结 memchr 函数的运行流程图:

    逆向 string.h 函数库 strlen、memchr、strcat 函数

    逆向 string.h 函数库 strlen、memchr、strcat 函数

strcat 函数

  • 函数原型:char *strcat(char *dest, const char *src)
  • 函数功能: 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。
  • C/C++ 实现:
#include <iostream>
#include <String.h> int main(int argc, char **argv)
{
char ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190515210957199.png)str1[] = "WHO ";
char str2[] = "AM ";
char str3[] = "I"; strcat(str1, str2);
strcat(str1, str3); cout << str1 << endl;
return 0;
}
  • 以上程序的运行结果如图所示:

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 函数运行步骤:

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 逆向分析:首先取出传入 strcat 函数的参数1中的字符串地址,存放在 ecx 中,之后判断 ecx 的值是否数据对齐,如果数据没有对齐,则对数据进行对齐操作

    逆向 string.h 函数库 strlen、memchr、strcat 函数

注:程序中对数据对齐的判断是指数据存放的地址是否为 4 的倍数

  • 之后会以一个 DWORD 类型为大小循环判断参数一是否包含字符串结尾标志 00

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 由于 "WHO " 只有 4 个字节,所以循环第二次才包含 00 字符,接下来就需要计算 00 字符串的位置了,由于每次是以一个 DWORD 类型为大小(4 个字节),所以 00 字符处于 eax 的低位,故经过 al 比较之后发生了跳转。这个步骤主要的作用是将字符串指针指向该字符串的结尾,也就是说指向 "WHO " 的结尾,为的是方便将第二个参数字符串追加到第一个字符串的结尾

    逆向 string.h 函数库 strlen、memchr、strcat 函数
  • 然后取出传入 strcat 函数的第二个参数,用于追加到第一个参数字符串的结尾。和上面处理第一个字符串的方法类似,首先对第二个参数字符串地址进行数据对齐判断,如果不对齐就对数据进行对齐

    逆向 string.h 函数库 strlen、memchr、strcat 函数

注:值得注意的是,参数二字符串的对齐操作有一点特殊,该对齐操作每次一个字节的循环将参数二字符串添加到参数一字符串的结尾,并且将参数二字符串的指针 + 1,之后再次判断数据是否对齐,如果没有再次循环

  • 最后在满足没有字符串结尾标志 00 的情况下,每次 4 个字节将参数二字符串循环复制参数一字符串的结尾,当 4 个字节中包含 00 结尾时就计算 00 的位置,以便完成接下来的复制

    逆向 string.h 函数库 strlen、memchr、strcat 函数

逆向 strlen、memchr、strcat 函数到此结束,如有错误,欢迎指正

上一篇:Linux开机启动十步骤


下一篇:tomcat JRE_HOME