0
点赞
收藏
分享

微信扫一扫

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

0x01 fseek

  • 函数原型:​​int fseek(FILE *stream, long int offset, int whence)​
  • 函数功能:设置流​​stream​​​ 的文件位置为给定的偏移​​offset​​​,参数​​offset​​​ 意味着从给定的​​whence​​ 位置查找的字节数
  • 动态链接库:​​ucrtbased.dll​
  • C\C++ 实现

#define// 抛弃安全警告
#include <stdio.h>
#include <string.h>

int main()
{
FILE* fp;

fp = fopen("D:\\1.txt", "w+");
fputs("This is runoob.com", fp);

fseek(fp, 7, SEEK_SET);
fputs(" C Programming Langauge", fp);
fclose(fp);

return(0);
}

  • 上述程序的功能主要是打开​​D:\1.txt​​ 文件,之后调整文件流的偏移,最后关闭文件
  • 调试工具:​​x32dbg​​ 调试器
  • 逆向分析:首先利用定位字符串的方式找到​​fseek​​ 函数
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_赋值

  • ​fseek​​​(一级函数)中调用了两个子函数:​​ucrtbased.sub_625FF510​​​ 负责将传入的参数一(文件句柄)赋值到局部变量​​[ebp-4]​​​ 中,之后压入文件句柄调用​​ucrtbased.sub_6261C940​
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_赋值_02


  • ​ucrtbased.sub_6261C940​​​(二级函数)函数内部首先判断传入的第一个参数(文件句柄)是否存在,不存在的话就调用​​_CrtDbgReportW​​​ 和​​_error​​ 进行错误处理
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_文件句柄_03

  • 之后判断传入的第四个参数(​​SEEK_SET​​​)是否为​​0、1、2​​ 中的一个
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_文件句柄_04

SEEK_SET: 文件开头 0
SEEK_CUR: 当前位置 1
SEEK_END: 文件结尾 2

  • 然后调用​​ucrtbased.sub_ 625EC550​​​ 获取文件句柄,文件句柄的类型为​​FILE​​​。紧接着调用​​_lock_file​​​ 锁住文件。既然锁住了文件,那么肯定要解锁文件,如图所示:解锁文件的函数​​_unlock_file​​ 在结尾处调用
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_赋值_05

  • 最后压入参数调用​​ucrtbased.sub_6261CCE0​​(三级函数),参数如下图注释所示
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_文件句柄_06

  • 进入​​ucrtbased.sub_6261CCE0​​​(三级函数) 函数单步调试:首先会调用​​ucrtbased.sub_62616B10​​​ 和​​ucrtbased.sub_626168F0​​​ 对​​_flag​​ 进行判断
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_赋值_07

  • 注:​​_flag​​​ 是​​_iobuf​​​ 结构体的成员,而​​FILE​​​ 结构体指针指向的原型就是​​_iobuf​​。结构体示意图如下图所示:
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_sed_08

  • ​ucrtbased.sub_62616B10​​​:判断(​​_flag & 2000​​​)是否大于​​0​​​,若等于​​0​​​,则函数直接返回。因为查阅不到​​_flag​​ 成员的有关资料,所以暂时不知道这一步操作是什么目的
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_sed_09

  • ​ucrtbased.sub_626168F0​​​ 判断方式:先循环比较​​_flag​​​ 和​​(_flag & FFFFFFF7)​​​ 是否相等,然后比较​​_flag & 8​​​ 是否等于​​0​​​ 。这里有两个​​bug​​​,假如​​_flag​​​ 为​​204e​​​ 的话,就变成死循环了。而且通过观察​​ucrtbased.sub_626168F0​​​ 的返回值,发现没有对返回值做任何处理,也就是说这个判断​​_flag​​ 的函数没有任何作用
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_sed_10

  • 然后压入 4 个参数后,调用​​ucrtbased.sub_6261CB00​​(四级函数)
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_赋值_11

  • 进入​​ucrtbased.sub_6261CB00​​​(四级函数)函数进行分析:首先这个函数判断传入的第四个参数(​​SEEK_SET​​​)是否为指向文件结尾,如果指向文件结尾此函数返回​​0​​​,之后调用​​ucrtbased.sub_62615130​​​ 和​​ucrtbased.sub_62615150​​ 对 _flag 进行判断
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_文件句柄_12

  • 注​​ucrtbased.sub_62615130​​​ 的判断方式:(​​_flag & 4c0​​​) 是否等于​​0​​​,若不等于​​0​​​,返回​​1​
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_赋值_13


  • ​ucrtbased.sub_62615150​​​ 的判断方式:在 (​​_flag & 4c0​​​) 是不等于​​0​​​ 的前提下判断 (​​_flag & 6​​​) 是否等于​​0​​​,若不等于​​0​​​,返回​​1​
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_文件句柄_14


  • ​ucrtbased.sub_62615150​​​ 判断完成之后直接清空​​eax​​​,​​ucrtbased.sub_6261CB00​​(四级函数)返回
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_文件句柄_15

  • 返回之后判断传入的第四个参数(​​SEEK_SET​​)是否为当前文件位置,由于不是当前文件位置,故发生了跳转
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_sed_16

  • 之后调用​​ucrtbased.sub_625EC550​​​ 获取文件句柄,把文件句柄做为参数压入,接着调用​​ucrtbased.sub_62619070​
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_文件句柄_17


  • ​ucrtbased.sub_62619070​​​(四级函数) 这个函数主要是对​​_iobuf->cnt​​​ 和​​_iobuf->*_ptr​​​ 进行操作,进入这个函数看看:首先会调用​​ucrtbased.sub_625FF510​​​ 将文件句柄赋值到局部变量​​[ebp-4]​​​ 当中,接着调用​​ucrtbased.sub_62615070​​​ 将取出​​_iobuf->_flag​​ 取出
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_文件句柄_18

  • 注:​​ucrtbased.sub_62619010​​​ 是对​​_iobuf->_flag​​ 进行判断
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_sed_19

  • 判断方式:

if((_flag & 3 == 2) && (_flag & C0 == 0)) { return 1; } else { return 0; }

  • 判断完成之后,将​​_iobuf->*_ptr - _iobuf->_cnt​​,
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_文件句柄_20

  • 接着调用​​ucrtbased.sub_62618EA0​​​,该函数的主要功能:​​_iobuf->*ptr = iobuf->cnt​​​,并将​​_iobuf->*_base​​​ 赋值为​​0​
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_sed_21


  • 之后在​​_iobuf->*ptr - _iobuf->_cnt​​​ 大于​​0​​​ 的前提下调用​​_fileno​​​ 和​​_write​​ 函数
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_sed_22

  • 之后再判断写入的字节和​​_iobuf->*ptr - _iobuf->_cnt​​​ 相等的前提下调用​​ucrtbased.sub_62618FA0​​​,该函数的主要功能就是判断​​_iobuf->_flag & 4​​​ 和​​4​​​ 是否相等,​​eax​​​ 返回​​1​​ 表示相等
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_赋值_23


  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_赋值_24

  • 最后调用​​ucrtbased.sub_62618FA0​​​ 判断​​_flag & 8​​​ 是否等于​​0​​​,​​ucrtbased.sub_62619070​​​(四级函数)返回​​0​
  • 接着向下调试,发现会将​​_iobuf->*ptr​​​ 赋值为​​iobuf->cnt​​​,​​_iobuf->*_base​​​ 设置为​​0​​​,并且判断​​_iobuf->flag & 4​​​ 和​​4​​​ 是否相等, 再相等的前提下判断​​_flag & 8​​​ 是否为​​0​
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_sed_25


  • 最后调用​​ucrtbased.sub_62619070​​(四级函数)
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_sed_26

  • 进入这个函数看看:首先会使用​​_get_osfhandle​​ 检索与指定文件描述符关联的操作系统文件句柄,如果函数调用失败则进入错误处理
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_sed_27

  • 之后调用​​ucrtbased.sub_62619070​
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_sed_28


  • 这个函数的功能是利用​​SetFilePointerEx​​​ 函数移动指定文件的文件指针,并配合​​GetLastError​​ 函数进行错误处理
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_文件句柄_29

  • 接着将返回值放入局部变量中
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_赋值_30

  • 最后返回
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_文件句柄_31


  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_文件句柄_32

  • 在​​ucrtbased.sub_6261ccE0​​​ 的结尾对这两个返回值进行比较,如图所示跳转实现,故返回值为​​0​
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_文件句柄_33


  • 最后使用​​_unlock_file​​​ 对文件进行解锁,​​fseek​​ 函数调用结束
  • 逆向 stdio.h 函数库 fseek 函数(调试版本)_赋值_34

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



举报

相关推荐

0 条评论