逆向 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函数只对其数组部分产生影响, 而另一部分则对整个 ...
随机推荐
- numpy函数的使用
NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库. 数据分析三剑客:Numpy,Pandas ...
- DES加密--不安全加密
package test; import java.security.InvalidKeyException; import java.security.Key; import java.securi ...
- Windows包管理器——Scoop 包管理器
Scoop 包管理器 目录 Scoop 包管理器 参考 官方 博客 声明 目录 scoop 安装&&卸载 安装( 使用 powershell) 卸载(软件的使用权归自己所有,一言不合即 ...
- Java 语言基础 (初识Java语言, 变量和数据类型, 运算符, 流程控制语句, 数组)
初始 Java 语言 Java SE -- Java Platform, Standard Edition 是 Java 平台的基础 Java SE 以前称为 J2SE, 可以编写桌面应用和基于 we ...
- 攻防世界 reverse easy_Maze
easy_Maze 从题目可得知是简单的迷宫问题 int __cdecl main(int argc, const char **argv, const char **envp) { __int64 ...
- java例题_25 判断是否为回文数!
1 /*25 [程序 25 求回文数] 2 题目:一个 5 位数,判断它是不是回文数.即 12321 是回文数,个位与万位相同,十位与千位相同. 3 */ 4 5 /*分析 6 * 先用%和/将5个数 ...
- 1W字|40 图|硬核 ES 实战
前言 上篇我们讲到了 Elasticsearch 全文检索的原理<别只会搜日志了,求你懂点检索原理吧>,通过在本地搭建一套 ES 服务,以多个案例来分析了 ES 的原理以及基础使用.这次我 ...
- Azure data studio 跨平台数据库管理工具试用
最近折腾 azure sql database 的时候发现了微软的一款新的数据库管理工具: azure data studio.从名字上看 azure data studio 好像是专门为 azure ...
- java面试系列<4>——IO
面试系列--javaIO 一.概述 java的IO主要分为以下几类: 磁盘操作:File 字节操作:InputStream 和 OutputStream 字符操作:Reader 和 Writer 对象 ...
- JAVAEE_Servlet_23_路径编写总结和url_pattern的编写方式
路径编写总结和url_pattern的编写方式 路径的编写 超链接 form表单的action属性 重定向 response.sendRedirect("/项目名/资源路径"): ...

