0x01 fwrite 函数

  • 函数原型: size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
  • 函数功能:把 ptr 所指向的数组中的数据写入到给定流 stream
  • 动态链接库:ucrtbased.dll
  • C\C++ 实现:
#define _CRT_SECURE_NO_WARNINGS // 用于排除安全限制
#include <stdio.h>
#include <string.h> int main()
{
FILE* fp;
char c[] = "This is runoob";
char buffer[20]; /* 打开文件用于读写 */
fp = fopen("D:\\1.txt", "w+"); /* 写入数据到文件 */
fwrite(c, strlen(c) + 1, 1, fp); /* 查找文件的开头 */
fseek(fp, 0, SEEK_SET); /* 读取并显示数据 */
fread(buffer, strlen(c) + 1, 1, fp);
printf("%s\n", buffer);
fclose(fp); return(0);
}
  • 上述程序主要的功能就是打开路径为 D:\1.txt 文件,之后将字符串 This is runoob 写入文件后,再读取。运行结果如下图所示:

  • 调试工具:x32dbg
  • 逆向分析:要分析 fwrite 函数首先需要定位函数的位置,由于这个程序比较简单,所以直接定位 main 函数即可。如下图所示通过直接定位字符串 This is runoob 就可以找出 main 函数的位置

  • main 函数中看出有几个重要的 API 函数,例如:fopen、strlen、fwrite、fread 这几个函数



  • 而本次分析的是 fwrite 函数,因此直接定位到该函数,传入的参数如下图注释所示。需要注意的是字符串的长度是通过上面的 strlen 函数获取的,而文件的句柄是通过 fopen 函数获得的

  • 进入 fwrite 函数进行分析。首先会对传入的参数进行过滤(这也是一些 API 函数的通用流程),判断传入的字符串、文件句柄、元素的个数是否为 0,也就是是否存在的意思。如果有一个条件不符合就会调用 ucrtbased._CrtDbgReportW 函数做错误处理或直接清空 eax 返回

  • 继续向下调试,发现会调用 ucrtbased.sub_F538CD0 函数,这个函数的功能比较简单,就是将传入 fwrite 的参数赋值到 [ebp-18] 这个局部变量当中,为了方便起见将这个局部变量取名为 fileinfo,而且根据赋值的特征,这个 fileinfo 很有可能是一个结构体

  • 紧接着调用 ucrtbased.sub_F53DF10 函数,压入的第一个参数为文件的句柄(fopen 函数的返回值,类型为 FILE),第二个参数为 fileinfo 结构体

  • 进入 ucrtbased.sub_F53DF10 函数,可以看出这个函数调用了三个子函数。第一个函数 ucrtbased.sub_F51F4C0 的功能是将传入的参数一(文件句柄)放入局部变量 [ebp-8] 中,第二个参数 ucrtbased.sub_F51F4C0 的功能是将文件句柄赋值到局部变量 [ebp-c] 当中去。之后压入 [ebp-8] 后调用 ucrtbased.sub_F53DE80 函数,需要注意的是,这里还有一个局部变量 [ebp-1],通过 ecx 传入的

  • 进入 ucrtbased.sub_F53DE80 函数,发现这个函数会调用 4 个子函数,第一个函数 ucrtbased.sub_F538D10 的功能是调用 _lock_file API 函数来锁住文件,为的是为接下来写入文件做铺垫,而传入 ucrtbased.sub_F53DE80 函数的参数是通过 [ebp+8] 来获取的,也就是文件句柄



  • 既然通过了 _lock_file 来锁住文件那么之后肯定需要解锁文件,解锁文件的系统 API 函数是 _unlock_file,由该函数中的 ucrtbased.sub_F538D30 函数调用



  • ucrtbased.sub_F53DF50 函数才是实现 fwrite 功能的核心函数,传入此函数的参数如下图中的注释所示

  • ucrtbased.sub_F53DF50 函数中调用了 3 个子函数

  • 第一个函数 ucrtbased.sub_F53AF70 ,主要功能是判断文件描述符是否于指定设备想关联

  • 主要用到了 _fileno_isatty 两个 API 来判断,如果未关联就返回 _isatty 的返回值,如果没有就做一些处理,由于处理涉及调式和未调试的区别,比较复杂,所以不多述

  • 进行完设备关联判断之后,调用 _fwrite_nolock 这个 API 函数将传入的字符串写入规定的文件流,传入的参数如图所示,返回值储存在 [ebp-8]

  • 最后看一下 ucrtbased.sub_F53AFA0 函数

  • 在这个函数内部首先会判断传入的第一个参数是否为 0,根据实际的运行流程在 test ecx,ecx 命令判断之后直接实现了跳转,函数直接返回了。为了详细的了解程序,还是看了一下这几个函数都调用了哪些 API 函数,经过查找后发现在 ucrtbased.sub_F539070 函数中调用了 _fileno_write,这个和上面处理的流程还是蛮类似的

  • 最后函数返回,返回值就是 _fwrite_nolock 函数的返回值







  • 最后完成对 fwrite 函数的调用

  • 函数运行流程:开始->参数过滤->_lock_file->_file_no->_isatty->_fwrite_nolock->unlock_file->结束

逆向 stdio.h 库的 fwrite 函数到此结束,如有错误,欢迎指正

逆向 stdio.h 函数库 fwrite 函数(调试版本)的更多相关文章

  1. 逆向 stdio.h 函数库 fseek 函数(调试版本)

    0x01 fseek 函数 函数原型:int fseek(FILE *stream, long int offset, int whence) 函数功能:设置流 stream 的文件位置为给定的偏移 ...

  2. 逆向 stdio.h 函数库 fopen 函数(调试版本)

    0x01 fopen 函数 函数原型:FILE *fopen(const char *filename, const char *mode) 返回值为 FILE 类型 函数功能:使用给定的模式 mod ...

  3. 走进C标准库(2)——"stdio.h"中的fopen函数

    其他的库文件看起来没有什么实现层面的知识可以探究的,所以,直接来看stdio.h. 1.茶余饭后的杂谈,有趣的历史 在过去的几十年中,独立于设备的输入输出模型得到了飞速的发展,标准C从这个改善的模型中 ...

  4. fopen函数和fread函数、fwrite函数

    fopen(打开文件) 相关函数 open,fclose 表头文件 #include<stdio.h> 定义函数 FILE * fopen(const char * path,const ...

  5. 【转】fread函数和fwrite函数

    1.函数功能   用来读写一个数据块. 2.一般调用形式   fread(buffer,size,count,fp);   fwrite(buffer,size,count,fp); 3.说明   ( ...

  6. strtok()函数、fseek()函数、fwrite()函数、fread()函数的使用

    在电子词典这个项目过程中遇到了几个主要的问题,第一个是怎么解决把翻译分开这个.第二个事情就是怎么把结构体写到文件中.这两个问题,一个是关于字符串的操作一个是关于文件的操作. strtok函数 char ...

  7. fread函数和fwrite函数

    1.函数功能   用来读写一个数据块. 2.一般调用形式   fread(buffer,size,count,fp);   fwrite(buffer,size,count,fp); 3.说明   ( ...

  8. python函数库及函数标准库

    一.系统库提供的内部函数 字符函数库: 1)str.islower() :字符串是否全部是小写 2)str.isspace() :字符串是否为空 3)help(str):查询字符串函数库 4)str. ...

  9. C函数库stdio.h概况

    库变量 size_t    这是无符号整数类型,它是 sizeof 关键字的结果. FILE      这是一个适合存储文件流信息的对象类型. fpos_t   这是一个适合存储文件中任何位置的对象类 ...

随机推荐

  1. Excel_不打开文件进行跨工作簿查询

    在使用Excel时,我们经常会遇到这种问题,我的数据源在表1里面,但是我要在表2里面做报表,用lookup和offset等公式都需要打开表1操作,否则就会报错.那么有没有办法在不打开表1的情况下在表2 ...

  2. EF Core中通过Fluent API完成对表的配置

    EF Core中通过Fluent API完成对表的配置 设置实体在数据库中的表名 通过ToTable可以为数据模型在数据库中自定义表名,如果不配置,则表名为模型名的复数形式 public class ...

  3. FreeBSD jail 折腾记(一)

    创建jail目录 mkdir -p /usr/jail/ 放入基本系统 方案一 make buildworld #编译基本系统 make installworld DESTDIR=/usr/jail/ ...

  4. Java 重入锁和读写锁

    本文部分摘自<Java 并发编程的艺术> 重入锁 重入锁 ReentrantLock,顾名思义,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁.除此之外,该锁还支持获取锁时 ...

  5. Django 使用 pycharm 创建新的app(可以理解为模块)

    创建工程的时候,注意选择Existing interpreter 选择对应的 python 解释器,电脑如果安装有多个版本的 Python 的话,注意python版本的问题, 以上即是创建的项目目录, ...

  6. iOS 面试秘籍全套

    栏目将持续更新--请iOS的小伙伴关注!   (答案不唯一,仅供参考,文章最后有福利) iOS面试题大全(上) iOS面试题大全(下) 目录: iOS面试题:Run Loop iOS面试题:性能优化 ...

  7. Java 树结构实际应用 四(平衡二叉树/AVL树)

    平衡二叉树(AVL 树) 1 看一个案例(说明二叉排序树可能的问题) 给你一个数列{1,2,3,4,5,6},要求创建一颗二叉排序树(BST), 并分析问题所在.  左边 BST 存在的问题分析: ...

  8. github文件快速下载

    目录 一,提升加载速度 二,提升下载速度 只是想快速下载文件的直接看第二部分. github加载速度慢究其原因还是伟大的墙的存在.我们需要赞美墙,但就算墙很伟大,问题还是要解决的. 有问题就解决问题, ...

  9. 力扣 - 剑指 Offer 37. 序列化二叉树

    目录 题目 思路 代码 复杂度分析 题目 剑指 Offer 37. 序列化二叉树 思路 序列化其实就是层序遍历 但是,要能反序列化的话,前.中.后.层序遍历是不够的,必须在序列化时候保存所有信息,这样 ...

  10. angular+ionic -- 启动命令

    初始angular+ionic项目,启动需ionic的启动命令: ionic serve