0x01 fopen 函数

  • 函数原型FILE *fopen(const char *filename, const char *mode) 返回值为 FILE 类型
  • 函数功能:使用给定的模式 mode 打开 filename 所指向的文件
  • 动态链接库ucrtbased.dll
  • C\C++ 实现
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>
using namespace std; int main(int argc, char** argv)
{
char str[] = "AAAAAAAAAA"; // 字符串只是为了定位程序的 main 函数
FILE* fp;
fp = fopen("D:\\1.txt", "w+");
if (fp == NULL)
{
cout << "文件打开失败" << endl;
}
else
{
cout << "文件打开成功" << endl;
fclose(fp);
}
return true;
}
  • 上述程序功能主要是打开 D 盘下的 1.txt 文件,若返回值不为空,则表示打开文件成功。运行结果如下图所示:

  • 调试工具:x32dbg
  • 逆向分析:这一次分析的函数和其他函数有很大的不同,fopen 属于操作型函数,不同于算法型函数,操作型函数没有复杂的算法,但是函数调用及传参比较复杂,尤其是 fopen 函数需要对操作系统底层进行操作,下面来进行逆向分析
  • 首先利用字符串定位的老办法定位 main 函数的位置,用来绕过 main 函数之前复杂的初始化过程。如图所示可以看到 fopen 函数的调用过程,其中第一个参数是打开文件的路径,第二个参数是 w+ 符号

  • 直接进入 fopen 函数,可以看出在里面有调用了一个子函数 ucrtbased.sub_560A560,且压入的参数和 fopen 函数的参数是一样的

  • 进入 ucrtbased.sub_5A8AA560 函数,单步向下调试:首先判断传入的第一个参数和第二个参数是否为空



  • 其次判断第二个参数的第一和第二个字节是否为空,同时判断第一个参数的第一个字节是否为空(不知道为什么需要重复的判断)



  • 参数判断完成之后调用 ucrtbased.sub_5A8D6ED0 函数,函数的功能是对 w+ 符号对象的操作进行初始化

  • 进入 ucrtbased.sub_5A8D6ED0 函数看看,发现此函数会使用关键段对一些数据进行初始化操作。里面涉及了一系列的 API 函数,包括 _calloc_dbgfree_dbg_unlock_file_lock_file 等。需要注意的是在 ucrtbased.sub_5A8D6ED0 函数的最后进行了 _lock_file 锁文件操作,并且将文件的 FILE 类型的句柄储存在局部变量 [ebp-1c] 当中。由于并不是 fopen 函数的核心功能,所以不多叙述。

需要注意的是以上 API 具有调试方面的功能,调试版本和运行版本可能会有区别

  • 初始化完成之后,会调用 ucrtbased.sub_55EFAA0 函数用于判断局部变量 [ebp-1c] 否为 0,由于在初始化的过程中传入了 [ebp-1c] 的地址,所以该局部变量可能是为了判断初始化是否赋值成功

  • 继续向下调试,发现会调用 ucrtbased.sub_55DC550 函数获取 [ebp-1c] 地址的值作为调用 ucrtbased.sub_6256AA20 函数的第 4 个参数。其他三个参数如图中的注释所示(第一个参数和第二个参数就是文件的路径和打开的方式)。从参数中可以看出 ucrtbased.sub_560AA20 函数才是 fopen 函数的核心处理函数,下面主要看这个函数就可以了

  • F7 单步进入 ucrtbased.sub_560AA20 这个函数,发现其会对传入的参数稍作处理,之后会调用 ucrtbased.sub_5619C60 这个子函数,继续 F7 跟进 ucrtbased.sub_5619C60 这个函数

  • ucrtbased.sub_5619C60 函数中发现其会调用两个函数:ucrtbased.sub_55EF510 函数和 ucrtbased.sub_5619900 函数

  • 传入 ucrtbased.sub_55EF510 函数的参数包括局部变量 [ebp-4] 和第四个参数 [ebp+14]。函数的功能很简单,主要是将 [ebp+14] 地址的值赋值到 [ebp-4] 地址中去

  • 而传入 ucrtbased.sub_62579900 函数的参数有 4 个,如上图所示,其中第四个参数传递的局部变量 [ebp-4] 地址的值,也就是祖父函数 [ebp-1c] 的值。进入 ucrtbased.sub_62579900 函数单步调试,首先会判断传入的第一个参数是否为空,第一个参数是文件的路径;其次判断第二个参数是否为空,之后调用 ucrtbased.sub_55EFAA0 函数判断传入的第四个参数地址的值是否为 0

  • 其次判断传入的第 4 个参数是否为 0,其实这个 [ebp+14] 的值就是祖父函数的局部变量 [ebp-1c] 传进来的,通过参数溯源可以很容易的看出来

  • 判断完成之后调用 ucrtbased.sub_5606F60 函数,该函数的功能根据传入的参数 w+ 初始化一些数据,之后需要用到

  • 进入 ucrtbased.sub_5606F60 函数调试看看,首先会初始化一些变量。接着判断传入的 W+ 参数第一个字节是否为 0

  • 接下来就比较重要了,该判断会根据传入 fopen 的第二个参数来对 [ebp-10][ebp-c] 这个两个局部变量赋值。举个例子:如果传入的参数为 w+,则将 [ebp-10] 赋值为 301[ebp-c] 赋值为 2

  • 接着对该函数中的其他变量进行赋值,以位为单位。其中 w+ 的第二个字节储存在局部变量 [ebp-18]

  • 之后将 + 号对应的 Ascii 码减去 0x20,配合 0xF3E7770 做地址取值,取出来的值做为索引进行调用,jmp 就相当于 call,该功能主要是为了判别传入 fopen 函数的第二个参数后面是否带了加号跳转对应的地址

  • 紧接着将 302、4、1 放入传入的第一个参数的地址中去

  • 之后经过 GS 验证之后函数返回第一个参数的地址

  • 函数调用完成之后,将返回的值赋值到局部变量当中去,如下图所示:

  • 之后调用 ucrtbased.sub_5619c00 函数,压入的参数如图所示

  • 单步进入 ucrtbased.sub_5619c00 函数,发现其会调用底层 API 函数 _sopen_s 函数,由该函数完成对文件的共享访问

  • _fopen_s 底层函数如图所示:

  • 然后将传入的第四个参数的值做为地址,如图所示清空其地址中的值,需要注意的是在地址 +0x10 的地方赋值为 3,这个三就是传入 _sopen_s 函数的第一个参数,也就是文件的句柄

还记得第四个参数吗,就是上面说的祖父函数的 [ebp-1c] 变量,其实根据函数的功能可以判断出此变量就是用于返回 fopen 函数的返回值,类型是 FILE 类型

  • 最后进行 GS 验证后返回第四个参数的地址

  • 继续返回,返回值和上面一样不变



  • 经过两次返回之后,将返回值储存在 [ebp-20] 当中,之后调用 ucrtbased.sub_560AA20 函数

  • ucrtbased.sub_560AA20 函数的功能主要是通过底层 API 函数 _unlock_file 来解锁文件,压入的参数为父函数的 [ebp-1c] 这个局部变量

  • 最后取出 [ebp-20] 的值放入 eax 中,fopen 函数调用完毕



总结

  • fopen 函数总体来说还是比较复杂的,这里简单说一下流程:首先会对传入的第一个和第二个参数进行过滤处理,之后使用局部变量 [ebp-1c] 做为 fopen 函数的返回值,然后对 [ebp-1c] 局部变量(FILE 结构)进行初始化操作,并使用 _lock_file 锁住文件,之后调用 _sopen_s 函数获取文件的共享权限,并且将句柄放入 [ebp-1c] 中,最后使用 _unlock_file 函数解锁文件并返回,返回值的类型为 FILE
  • 需要注意的是本次逆向分析是调试状态下的逆向分析,和真正运行状态下的程序流程可能会有一定的误差。另外就是调试器的注释中可能会有一些错误,还请谅解

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

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

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

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

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

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

  3. 逆向 stdio.h 函数库 fwrite 函数(调试版本)

    0x01 fwrite 函数 函数原型: size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) 函数功能:把 ...

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

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

  5. C函数库stdio.h概况

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

  6. C语言函数库

    C语言函数库 分类函数目录函数进程函数诊断函数接口子程序输入输出 str字符串操作函数mem操作存贮数组 数学函数 时间日期函数 转换函数 分类函数,所在函数库为ctype.h[top] int is ...

  7. C 标准库 - <stdio.h>

    一般地,在C语言或C++中,会把用来#include的文件的扩展名叫 .h,称其为头文件. #include文件的目的就是把多个编译单元(也就是c或者cpp文件)公用的内容,单独放在一个文件里减少整体 ...

  8. 【C语言入门教程】5.6 函数库和文件

    函数库是为代码复用建立的,将同一类型,需要在不同的程序里使用的函数放置在一起,就组成了一个函数库.如 C 语言的标准库,它集合了开发者常用的函数.开发者自行编写的函数也可以组成函数库,通常称之为自定义 ...

  9. 从函数式编程到Ramda函数库(一)

    函数式编程是种编程方式,它将电脑运算视为函数的计算.函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值).和指令式编程相比, ...

随机推荐

  1. C++ 中的虚函数表及虚函数执行原理

    为了实现虚函数,C++ 使用了虚函数表来达到延迟绑定的目的.虚函数表在动态/延迟绑定行为中用于查询调用的函数. 尽管要描述清楚虚函数表的机制会多费点口舌,但其实其本身还是比较简单的. 首先,每个包含虚 ...

  2. linux时间问题

    如果遇到创建时间和更新时间,不一致,先将时间调整为一致. 导致不一致的原因可能是时区不对,使用 tzselect ,将时区调整为Asia/Shanghai , cp /usr/share/zonein ...

  3. JavaScript快速上手

    引入JavaScript项目 在html中 <!DOCTYPE html> <html lang="en"> <head> <meta c ...

  4. 【linux】驱动-1-环境准备

    目录 前言 1. 开发环境搭建 1.1 环境准备 1.1.1 安装工具 1.1.2 编译内核 1.1.2.1 获取内核源码 1.1.2.2 编译内核 1.2 内核驱动模块编译和加载 1.2.1 hel ...

  5. P1085_不高兴的津津(JAVA语言)

    package 顺序与分支; /* * 题目描述 津津上初中了.妈妈认为津津应该更加用功学习,所以津津除了上学之外, 还要参加妈妈为她报名的各科复习班.另外每周妈妈还会送她去学习朗诵.舞蹈和钢琴. 但 ...

  6. 数数字(JAVA语言)

    package 第三章习题; /*  * 把前n(n<=10000)个整数顺次写在一起:  * 89101112...  * 数一数0-9各出现多少次  * (输出10个整数,分别是09出现的次 ...

  7. c/c++ switch case内括号

    如果在case语句中有定义变量,则必须要加{},否则会报错.

  8. PTA 找出不是两个数组共有的元素

    7-2 找出不是两个数组共有的元素 (20 分)   给定两个整型数组,本题要求找出不是两者共有的元素. 输入格式: 输入分别在两行中给出两个整型数组,每行先给出正整数N(≤),随后是N个整数,其间以 ...

  9. vue全局错误捕获

    1.errorHandler Vue全局配置 errorHandler可以进行全局错误收集,捕获全局错误抛出,避免前端页面挂掉   export default function errorHandl ...

  10. thinkphp 5.1框架利用及rce分析

    前言 上个学期钻研web渗透的时候接触过几个tp的框架,但那时候还没有写blog的习惯,也没有记录下来,昨天在做ctf的时候正好碰到了一个tp的框架,想起来就复现一下 正文 进入网站,标准笑脸,老tp ...