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 ![在这里插入图片描述](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;
}
  • 以上程序的运行结果如图所示:

  • 函数运行步骤:

  • 逆向分析:首先取出传入 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 函数的更多相关文章

  1. numpy函数库中一些常用函数的记录

    ##numpy函数库中一些常用函数的记录 最近才开始接触Python,python中为我们提供了大量的库,不太熟悉,因此在<机器学习实战>的学习中,对遇到的一些函数的用法进行记录. (1) ...

  2. 逆向 string.h 函数库 memset、strcpy、strcmp 函数

    memset 函数 函数原型:void *memset(void *str, int c, size_t n) 主要功能:复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符 ...

  3. 逆向 time.h 函数库 time、gmtime 函数

    0x01 time 函数 函数原型:time_t time(time_t *t) 函数功能:返回自纪元 Epoch(1970-01-01 00:00:00 UTC)起经过的时间,以秒为单位.如果 se ...

  4. C语言中的string.h中的内存字符串处理函数

    转载请注明出处:http://blog.csdn.net/zhubin215130/article/details/8993403 void *memcpy(void *dest, const voi ...

  5. 使用c函数库的两个函数strtok, strncpy遇到的问题记录

    1. strtok 问题背景: 解析形如 “1,2,3,4,5”字符串到整数数组 (1)计算个数 char* delim = ","; int count = 0; int *nu ...

  6. 字符串操作函数<string.h>相关函数strcpy,strcat,等源码。

    首先说一下源码到底在哪里找. 我们在文件中包含<cstring>时,如果点击右键打开文档, 会打开cstring,我们会发现路径为: D:\Program Files\visual stu ...

  7. Linux C函数库参考手册

    目录 第1章 字符测试函数 isalnum(测试字符是否为英文字母或数字) isalpha(测试字符是否为英文字母) isascii(测试字符是否为ascii码字符) isblank(测试字符是否为空 ...

  8. Linux C函数库大全

    (1)字符测试函数 isalnum(测试字符是否为英文字母或数字) isalpha(测试字符是否为英文字母) isascii(测试字符是否为ASCII码字符) isblank(测试字符是否为空格字符) ...

  9. Lua 的函数库 01

    这里只介绍和插件编写比较有关的几个函数. 详细的Lua手册请参照Lua Reference Manual 5.1. table函数库 一部分的table函数只对其数组部分产生影响, 而另一部分则对整个 ...

随机推荐

  1. 在windows上安装MySQL数据库注意点及Navicat Premium 15的破解

    在windows上安装MySQL数据库  跟随慕课网教程(http://www.imooc.com/wiki/mysqllesson/mysqlwindows.html)下载安装MySQL: 其中注意 ...

  2. SpringMVC执行流程及源码分析

    SpringMVC流程及源码分析 前言 ​ 学了一遍SpringMVC以后,想着做一个总结,复习一下.复习写下面的总结的时候才发现,其实自己学的并不彻底.牢固.也没有学全,视频跟书本是要结合起来一起, ...

  3. Python3读取网页HTML代码,并保存在本地文件中

    旧版Python中urllib模块内有一个urlopen方法可打开网页,但新版python中没有了,新版的urllib模块里面只有4个子模块(error,request,response,parse) ...

  4. 记客户端WebBrowser控件修改版本的问题

    保留在本地电脑的一篇记录,第二条描述是在网上看来的,忘记在哪看的了,也就没注明出处,望见谅. 1.Winform内置浏览器控件的底层调用与系统IE浏览器的底层调用相同. 2.IE8 对渲染引擎做了很大 ...

  5. Apache配置 6. 访问日记切割

    日志一直记录总有一天会把整个磁盘占满,所以有必要让它自动切割,并删除老的日志文件 (1)配置 (1)配置 # vim /usr/local/apache2 .4/conf/extra/httpd-vh ...

  6. 2019 GDUT Rating Contest I : Problem H. Mixing Milk

    题面: H. Mixing Milk Input file: standard input Output file: standard output Time limit: 1 second Memory ...

  7. 关于asyncio知识一

    一.介绍 asyncio 是python3.4 引入的一个新的并发模块,主要通过使用coroutines 和 futures 来让我们更容易的去实现异步的功能,并且几乎和写同步代码一样的写代码,还没有 ...

  8. 数位dp 模板加例题

    概念:所谓数位"dp",是指对数字的"位"进行的与计数有关的DP.一个数一个位,十位,百位,千位等,数的每一位就是数位.数位DP用来解决与数字操作有关的问题.例 ...

  9. 【linux】系统编程-4-共享内存

    目录 前言 6. 共享内存 6.1 概念 6.2 操作函数 6.2.1 shmget() 6.2.2 shmat() 6.2.3 shmdt() 6.2.4 shmctl() 6.3 例子 参考: 前 ...

  10. Powerful Number 筛学习笔记

    Powerful Number 筛学习笔记 用途 \(Powerful\ number\) 筛可以用来求出一类积性函数的前缀和,最快可以达到根号复杂度. 实现 \(Powerful\ number\) ...