逆向 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 函数运行结果:

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

- 由于这个函数比较简单,所以代码量不是很大:

- 流程图如下图所示:

- 主要算法:字符串对齐算法 + 判断字符串结束算法

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


注:为什么使用 test ecx,3 能判断数据是否对齐呢,假如字符串的首地址是 0019FEC4,那么二进制表示就为 110011111111011000100,而 3 的二进制是 011,那么最后两位就不为 0,表示数据已经对齐:
那么如果字符串的首地址是 0019FEC3,二进制表达为 110011111111011000011,这样的话数据就不会对齐:
- 假如跳转没有实现,就循环的将 ecx 中储存的字符串头指针往右移动,每次移动一个字节,直到数据对齐为止。值得注意的是期间会使用 test al,al 判断 al 是否为 0,表示字符串是否到达结尾

- 判断字符串结束算法:这个算法就比较有趣了,主要分为两个步骤,首先在数据对齐后会跳转到 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 结束标志


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

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 函数运行结果:

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

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

- 接下来对判断字符串的长度是否小于 4 个字节,如果小于 4 个字节直接返回

- 之后将指定字符(第二个参数),填充满 ebx,为下面的异或操作做基础


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

- 循环之后,判断刚才存在指定字符的 4 个字节中指定字符的位置,如果字符是 4 个字节中的第 2 个那么就将指针减去 3 个字节,这样就刚好指在了指定字符的位置

- 最后函数返回字符串中第一个指定字符的指针,所以函数打印出了指定字符 ‘.’ 及以后的字符

- 总结 memchr 函数的运行流程图:


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

- 函数运行步骤:

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

注:程序中对数据对齐的判断是指数据存放的地址是否为 4 的倍数
- 之后会以一个 DWORD 类型为大小循环判断参数一是否包含字符串结尾标志 00

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

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

注:值得注意的是,参数二字符串的对齐操作有一点特殊,该对齐操作每次一个字节的循环将参数二字符串添加到参数一字符串的结尾,并且将参数二字符串的指针 + 1,之后再次判断数据是否对齐,如果没有再次循环
- 最后在满足没有字符串结尾标志 00 的情况下,每次 4 个字节将参数二字符串循环复制参数一字符串的结尾,当 4 个字节中包含 00 结尾时就计算 00 的位置,以便完成接下来的复制

逆向 strlen、memchr、strcat 函数到此结束,如有错误,欢迎指正
逆向 string.h 函数库 strlen、memchr、strcat 函数的更多相关文章
- numpy函数库中一些常用函数的记录
##numpy函数库中一些常用函数的记录 最近才开始接触Python,python中为我们提供了大量的库,不太熟悉,因此在<机器学习实战>的学习中,对遇到的一些函数的用法进行记录. (1) ...
- 逆向 string.h 函数库 memset、strcpy、strcmp 函数
memset 函数 函数原型:void *memset(void *str, int c, size_t n) 主要功能:复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符 ...
- 逆向 time.h 函数库 time、gmtime 函数
0x01 time 函数 函数原型:time_t time(time_t *t) 函数功能:返回自纪元 Epoch(1970-01-01 00:00:00 UTC)起经过的时间,以秒为单位.如果 se ...
- C语言中的string.h中的内存字符串处理函数
转载请注明出处:http://blog.csdn.net/zhubin215130/article/details/8993403 void *memcpy(void *dest, const voi ...
- 使用c函数库的两个函数strtok, strncpy遇到的问题记录
1. strtok 问题背景: 解析形如 “1,2,3,4,5”字符串到整数数组 (1)计算个数 char* delim = ","; int count = 0; int *nu ...
- 字符串操作函数<string.h>相关函数strcpy,strcat,等源码。
首先说一下源码到底在哪里找. 我们在文件中包含<cstring>时,如果点击右键打开文档, 会打开cstring,我们会发现路径为: D:\Program Files\visual stu ...
- Linux C函数库参考手册
目录 第1章 字符测试函数 isalnum(测试字符是否为英文字母或数字) isalpha(测试字符是否为英文字母) isascii(测试字符是否为ascii码字符) isblank(测试字符是否为空 ...
- Linux C函数库大全
(1)字符测试函数 isalnum(测试字符是否为英文字母或数字) isalpha(测试字符是否为英文字母) isascii(测试字符是否为ASCII码字符) isblank(测试字符是否为空格字符) ...
- Lua 的函数库 01
这里只介绍和插件编写比较有关的几个函数. 详细的Lua手册请参照Lua Reference Manual 5.1. table函数库 一部分的table函数只对其数组部分产生影响, 而另一部分则对整个 ...
随机推荐
- 漏洞复现-CVE-2015-1427-Groovy远程代码执行
0x00 实验环境 攻击机:Win 10 靶机也可作为攻击机:Ubuntu18 (docker搭建的vulhub靶场) 0x01 影响版本 Elasticsearch 1.3.0-1.3. ...
- python面试题总结
Python语言特性 1. Python的函数参数传递 看两个如下例子,分析运行结果 #代码1 a = 1 def fun(a): a = 2 fun(a) print(a) #1 #代码2 a ...
- 使用C# (.NET Core) 实现单体设计模式 (Singleton Pattern)
本文的概念内容来自深入浅出设计模式一书 由于我在给公司做内培, 所以最近天天写设计模式的文章.... 单体模式 Singleton 单体模式的目标就是只创建一个实例. 实际中有很多种对象我们可能只需要 ...
- 以Aliyun体验机为例,从零搭建LNMPR环境(上)
使用云服务器搭建 Web 运行环境,尤其是搭建常见的 LNMPR(Linux+Nginx+MySQL+PHP+Redis) 环境,对于开发人员是必备的职场基本技能之一.在这里,借着搭建我的" ...
- Flex属性你真的搞清楚了吗?我深表怀疑
背景 在使用弹性布局实现两侧宽度固定,中间宽度自适应的效果时,发现自己理解的和实际效果不一致,所以亲自实践验证了一个flex属性的诸多场景的表现,不仅解开了我之前使用过程遇到的疑惑,而且发现了许多自己 ...
- 第27 章 : Kubernetes 安全之访问控制
Kubernetes 安全之访问控制 本文将主要分享以下三方面的内容: Kubernetes API 请求访问控制 Kubernetes 认证 Kubernetes RBAC Security Con ...
- var=value?export前后差在哪?-- Shell十三问<第五问>
var=value?export前后差在哪?-- Shell十三问<第五问> 这次让我们暂时丢开 command line ,先来了解一下 bash 变量(variable)吧.所谓的 变 ...
- VIM 编辑器操作详解
1 vim 使用介绍 1.1 vim 安装 # CentOS 安装: yum install -y vim # Ubuntu 安装: sudu apt-get install vim 安装完成后,可使 ...
- Spring IOC 特性有哪些,不会读不懂源码!
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 多线程.锁.JVM调优,都背出花啦,怎么一写代码还是乱糟糟? 为什么这些无论从书本. ...
- jq分页功能。
最近在写官网的分页功能.在网上找了很多案例都太复杂也太重.所以准备写一个简单一点的分页. 需求:把请求到的数据做分页. 准备:使用了网上一个简单的分页插件. 思路:分页相当于tab切换功能.具体实操把 ...

