逆向 string.h 函数库 memset、strcpy、strcmp 函数
memset 函数
- 函数原型:void *memset(void *str, int c, size_t n)
- 主要功能:复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符
- C/C++ 实现:
#include <string.h>
#include <iostream>
#include <stdio.h>
using namespace std;
int main(int argc, char ** argv)
{
char str[20] = "AAAAAAAAA";
memset(str, 'v', 10);
cout << str << endl;
return 0;
}
- 以上程序的作用是循环复制 10 个字符 ‘v’ 到 str 所指向的字符串,执行结果:

- 函数运行步骤:

- 逆向分析:这个函数的作用主要是将特定的字符覆盖规定的字符串,所以首先需要取出传入 memset 函数的三个参数,并且判断第三个和第二个参数是否为 0。如果第三个参数为 0,则直接返回第一个参数所指向的字符串的首地址;如果第二个参数为 0 且第三个参数小于 100,则程序继续往下执行,反之跳出函数进行错误处理

- 之后判断第三个参数是否小于 4,为什么要进行这个判断呢,这个先不说答案在下面。然后判断参数一传入的字符串地址是否数据对齐,如果没有对齐就对其进行对齐操作

- 对齐判断后就开始复制字符串了,值得注意的是这里复制字符串的方式并不是循环复制 1 个字节,而是 4 个字节 4 个字节的复制,最后剩余的再每次 1 个字节的循环复制。比如我要复制 10 个字节,先循环两次复制 8 个字节,最后循环两次每次复制 1 个字节,还记得上面判断第三个参数为什么小于 4 吗,因为小于 4 的话就不必要每次 4 个字节的复制了,直接跳转到每次复制 1 个字节处即可


- 最后将覆盖完的字符串首地址放入 eax 中,函数返回

- 总结:memset 流程图

strcpy 函数
- 函数原型: char *strcpy(char *dest, const char *src)
- 作用:把 src 所指向的字符串复制到 dest
- C/C++ 实现:
#include <string.h>
#include <iostream>
#include <stdio.h>
using namespace std;
int main(int argc, char ** argv)
{
char str[20] = "AAAAAAAAA";
strcpy(str, "BBBBBB");
cout << str << endl;
return 0;
}
- 以上程序的主要作用是将 “BB…” 复制到 str 字符串中,运行结果如图所示

- 函数运行步骤

- 逆向分析:首先取出传入 strcpy 函数的第一个和第二个参数


- 之后判断第二个参数所指向的字符串是否数据对齐

- 由于 0040121B 并不是 4 的倍数,显然数据没有对齐,从而发生跳转,进行数据对齐操作

注:对第二个参数的字符串是如何进行数据对齐操作的呢?方法简单,每次从第二个参数取出一个字符存入第一个参数指向的字符串的地址,并且第二个字符串指针加一,之后再次判断第二个参数的字符串地址是否数据对齐,如果还没有对齐就循环上述操作,直到数据对齐完毕
- 进行完数据对齐操作之后,将第二个参数所指向的字符串循环每次复制 4 个字节到第一个参数所指向的字符串 + 2 的地址中(因为进行了对齐操作),直到这 4 个字节当中不包含 00 标志(00 表示字符串结尾)

- 由于下面 4 个字节包含 00 字符,所以只是复制了一次

- 最后计算出 00 字符处于 4 个字节的哪一个位置,如图所示处于第 4 个位置,故发生跳转

- 之后把 00 字符串传入 edi 所指向的内存地址

- 寄存器状态和内存分布如图所示:


- strcpy 函数的流程图:

strcmp 函数
- 函数原型:int strcmp(const char *str1, const char *str2)
- 函数功能:把 str1 所指向的字符串和 str2 所指向的字符串进行比较(按字符的 Ascii 码进行比较)
- C/C++ 实现:
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[15];
char str2[15];
int ret;
strcpy(str1, "AAAaaa");
strcpy(str2, "AAaaaa");
ret = strcmp(str1, str2);
if(ret < 0)
{
printf("str1 小于 str2");
}
else if(ret > 0)
{
printf("str1 大于 str2");
}
else
{
printf("str1 等于 str2");
}
return(0);
}
- 以上程序的功能十分简单,比较 str1 和 str2 的大小,并且打印出结果。运行结果如图所示:

- strcmp 函数运行步骤:

- 逆向分析:首先进入 strcmp 函数,之后会取出传入 strcmp 函数的两个参数,也就是 str1 和 str2,取完参数之后判断 str1 字符串是否数据对齐,数据对齐就是 str1 字符串的首地址是否为 4 的倍数,如果 str1 字符串没有数据对齐,那么就会跳转到 0x7571938C 的地方进行数据对齐操作,在数据完成对齐后会 jmp 到 0x7571934E 的地址继续往下执行

- 从寄存器可以看出 ecx 储存的是 str2 字符串的首地址,而 edx 储存的是 str1 字符串的首地址

- 接下来取 str1 字符串的头四个字节放在 eax 中,之后按一个字节的大小分别与 str2 字符串相应的字符做大小比较,如果不相等就发生跳转,需要注意的时,这里比较的是 ascii 码的大小,比如 ‘A’ 字符 ascii 码为 41,而 ‘a’ 字符 ascii 码为 61,这样的话 ‘a’ 就会比 ‘A’ 字符大

- 由于 str1 是 ‘AAAaaa’,而 str2 是 ‘AAaaaa’,所以在第三个字符比较时由于 A != a,故发生了跳转,如果前四个字节都相等的话,会将 str1 和 str2 的指针都向后指 4 个字节,在循环以上过程进行比较

- 值得注意的是如果字符判断相等之后,如果发现 str1 对应的字符为 0(字符串结尾标志),就都会跳转到 0x75719380 这个地址,并且函数返回 0,表示字符串相等
- 比如 str1 为 ‘Aaa’,str2 为 ‘Aaaaaa’,由于 str1 的第四个字符为 0,而前面的字符又和 str2 相等,所以判断 str1 等于 str2;但是如果 str1 为 ‘Aaaa’,而 str2 为 ‘Aaa’ 的话,则 str1 大于 str2,因为进行结尾比较的是 str1 而不是 str2,所以 str1 大于 str2

- 最后根据 CF 标志位,以 sbb 计算的结果作为函数的返回值,如图所示 eax 的值为负数,根据 strcmp 函数的返回值文档可以看出如果函数返回值小于 0,则表示第一个参数字符串小于第二个参数字符串,也就是 str1 小于 str2

- 为什么通过 sbb 操作指令和 CF 标志位能判断出字符串的大小呢,比如 str1 = ‘AAAA’,而 str2 = ‘AAAaaaBB’,由于前三个字符相等所以比较第四个字符,str1 的第四个字符为 A(ascii:41),而 str2 的第四个字符为 a(ascii:61),所以比较的指令就为 cmp 41,61,而 cmp 指令是根据两个操作数相减去影响标志位的,所以 41 - 61 就为负数,导致数据溢出位 CF 变为 1,而 sbb eax,eax 指令就相当于 eax - eax - CF,所以也就能比较字符的大小了
- 那就有人问了,假如大于的话,CF 就为 0 了,岂不是两个字符串相等了吗,其实不会这样的因为在最后使用了 add eax,0x1 的操作,所以只要是大于的话返回值就会为 1,避免了和 0 之间的冲突(shl eax,1 也是同样的道理,为的是避免 add eax,0x1 在负数加一之后和 0 引发冲突)

逆向 memset、strcpy、strcmp 函数到此结束,如有错误,欢迎指正
逆向 string.h 函数库 memset、strcpy、strcmp 函数的更多相关文章
- 实现字符串函数,strlen(),strcpy(),strcmp(),strcat()
实现字符串函数,strlen(),strcpy(),strcmp(),strcat() #include<stdio.h> #include<stdlib.h> int my_ ...
- numpy函数库中一些常用函数的记录
##numpy函数库中一些常用函数的记录 最近才开始接触Python,python中为我们提供了大量的库,不太熟悉,因此在<机器学习实战>的学习中,对遇到的一些函数的用法进行记录. (1) ...
- 逆向 string.h 函数库 strlen、memchr、strcat 函数
strlen 函数 主要功能:返回字符串的长度 C/C++ 实现: #include <iostream> #include <stdio.h> #include <st ...
- 逆向 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 ...
- 自己实现字符串操作函数strlen(),strcat(),strcpy(),strcmp()
1.strlen()函数是求解字符串的有效长度的 1)非递归实现 size_t my_strlen(const char *str) { assert(str != NULL); //断言,保证指针 ...
- Mysql 常用函数(10)- strcmp 函数
Mysql常用函数的汇总,可看下面系列文章 https://www.cnblogs.com/poloyy/category/1765164.html strcmp 的作用 比较两个字符串的顺序是否完全 ...
- 使用c函数库的两个函数strtok, strncpy遇到的问题记录
1. strtok 问题背景: 解析形如 “1,2,3,4,5”字符串到整数数组 (1)计算个数 char* delim = ","; int count = 0; int *nu ...
- 13-C语言字符串函数库
目录: 一.C语言字符串函数库 二.用命令行输入参数 回到顶部 一.C语言字符串函数库 1 #include <string.h> 2 字符串复制 strcpy(参数1,参数2); 参数1 ...
随机推荐
- linux进程隐藏手段及对抗方法
1.命令替换 实现方法 替换系统中常见的进程查看工具(比如ps.top.lsof)的二进制程序 对抗方法 使用stat命令查看文件状态并且使用md5sum命令查看文件hash,从干净的系统上拷贝这些工 ...
- CRLF注入漏洞 -配置错误
漏洞分析参考 https://i-beta.cnblogs.com/posts/edit 什么是CRLF? CRLF 指的是回车符(CR,ASCII 13,\r,%0d) 和换行符(LF,ASCII ...
- javascript 之对象-13
对象 无序属性的集合,属性可以包含基本值.对象或者函数,简单理解为对象是若干属性的集合:我们常说的面向对象(oop)编程其实是指的一种编码的思想,简单理解为用对象来封装数据,利用封装.继承.多态对代码 ...
- 远程文件管理系统(SpringBoot + Vue)
一.简介 可以实现对本地文件的 增.删.改.重命名等操作的监控,通过登录远程文件监控系统,获取一段时间内本地文件的变化情况. 系统功能图如下: 流程图如下: 二.本地文件监控程序的实现(C++) 调用 ...
- 一个C#开发编写Java框架的心路历程
前言 这一篇絮絮叨叨,逻辑不太清晰的编写Java框架的的一个过程,主要描述我作为一个java初学者,在编写Java框架时的一些心得感悟. 因为我是C#的开发者,所以,在编写Java框架时,或多或少会带 ...
- JVM笔记--如果你写JVM,最需要考虑的重要结构是什么?
开局一张图,前面已经从每一部分解析过JVM的内存结构了,现在按照顺序来分析: 整体上来看:类文件从类加载子系统,加载完成之后,主要存放在方法区(JRockit和H9没有方法区,这里指的是HotSpot ...
- CSS垂直布局
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...
- 让 Java 中 if else 更优雅的几个小技巧
对于一个高级 crud 工程师而言,if else 是写代码时使用频率最高的关键词之一,然而有时过多的 if else 会让我们优雅的 crud 代码显得不那么优雅,并且感到脑壳疼
- 6、MyBatis教程之日志实现
7.日志实现 思考:我们在测试SQL的时候,要是能够在控制台输出 SQL 的话,是不是就能够有更快的排错效率? 如果一个 数据库相关的操作出现了问题,我们可以根据输出的SQL语句快速排查问题. 对于以 ...
- Java-TreeMap和Guava-HashMultiset
一.Java-TreeMap 1.数据结构 底层数据结构是裸的红黑树,保证元素有序,没有比较器Comparator的情况按照key的自然排序,可自定义比较器.线程不安全. 可以存null,但是key不 ...
