0x01 fseek 函数
函数原型:int fseek(FILE *stream, long int offset, int whence)
函数功能:设置流 stream
的文件位置为给定的偏移 offset
,参数 offset
意味着从给定的 whence
位置查找的字节数
动态链接库:ucrtbased.dll
C\C++ 实现:
#define _CRT_SECURE_NO_WARNINGS // 抛弃安全警告
#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
函数
fseek
(一级函数)中调用了两个子函数:ucrtbased.sub_625FF510
负责将传入的参数一(文件句柄)赋值到局部变量 [ebp-4]
中,之后压入文件句柄调用 ucrtbased.sub_6261C940
ucrtbased.sub_6261C940
(二级函数)函数内部首先判断传入的第一个参数(文件句柄)是否存在,不存在的话就调用 _CrtDbgReportW
和 _error
进行错误处理
之后判断传入的第四个参数(SEEK_SET
)是否为 0、1、2
中的一个
SEEK_SET: 文件开头 0
SEEK_CUR: 当前位置 1
SEEK_END: 文件结尾 2
然后调用 ucrtbased.sub_ 625EC550
获取文件句柄,文件句柄的类型为 FILE
。紧接着调用 _lock_file
锁住文件。既然锁住了文件,那么肯定要解锁文件,如图所示:解锁文件的函数 _unlock_file
在结尾处调用
最后压入参数调用 ucrtbased.sub_6261CCE0
(三级函数),参数如下图注释所示
进入 ucrtbased.sub_6261CCE0
(三级函数) 函数单步调试:首先会调用 ucrtbased.sub_62616B10
和 ucrtbased.sub_626168F0
对 _flag
进行判断
注:
_flag
是_iobuf
结构体的成员,而FILE
结构体指针指向的原型就是_iobuf
。结构体示意图如下图所示:
ucrtbased.sub_62616B10
:判断(_flag & 2000
)是否大于0
,若等于0
,则函数直接返回。因为查阅不到_flag
成员的有关资料,所以暂时不知道这一步操作是什么目的
ucrtbased.sub_626168F0
判断方式:先循环比较_flag
和(_flag & FFFFFFF7)
是否相等,然后比较_flag & 8
是否等于0
。这里有两个bug
,假如_flag
为204e
的话,就变成死循环了。而且通过观察ucrtbased.sub_626168F0
的返回值,发现没有对返回值做任何处理,也就是说这个判断_flag
的函数没有任何作用
然后压入 4 个参数后,调用 ucrtbased.sub_6261CB00
(四级函数)
进入 ucrtbased.sub_6261CB00
(四级函数)函数进行分析:首先这个函数判断传入的第四个参数(SEEK_SET
)是否为指向文件结尾,如果指向文件结尾此函数返回 0
,之后调用 ucrtbased.sub_62615130
和 ucrtbased.sub_62615150
对 _flag 进行判断
注
ucrtbased.sub_62615130
的判断方式:(_flag & 4c0
) 是否等于0
,若不等于0
,返回1
ucrtbased.sub_62615150
的判断方式:在 (_flag & 4c0
) 是不等于0
的前提下判断 (_flag & 6
) 是否等于0
,若不等于0
,返回1
ucrtbased.sub_62615150
判断完成之后直接清空 eax
,ucrtbased.sub_6261CB00
(四级函数)返回
返回之后判断传入的第四个参数(SEEK_SET
)是否为当前文件位置,由于不是当前文件位置,故发生了跳转
之后调用 ucrtbased.sub_625EC550
获取文件句柄,把文件句柄做为参数压入,接着调用 ucrtbased.sub_62619070
ucrtbased.sub_62619070
(四级函数) 这个函数主要是对 _iobuf->cnt
和 _iobuf->*_ptr
进行操作,进入这个函数看看:首先会调用 ucrtbased.sub_625FF510
将文件句柄赋值到局部变量 [ebp-4]
当中,接着调用 ucrtbased.sub_62615070
将取出 _iobuf->_flag
取出
注:
ucrtbased.sub_62619010
是对_iobuf->_flag
进行判断判断方式:
if((_flag & 3 == 2) && (_flag & C0 == 0))
{
return 1;
}
else
{
return 0;
}
判断完成之后,将 _iobuf->*_ptr - _iobuf->_cnt
,
接着调用 ucrtbased.sub_62618EA0
,该函数的主要功能:_iobuf->*ptr = iobuf->cnt
,并将 _iobuf->*_base
赋值为 0
之后在 _iobuf->*ptr - _iobuf->_cnt
大于 0
的前提下调用 _fileno
和 _write
函数
之后再判断写入的字节和_iobuf->*ptr - _iobuf->_cnt
相等的前提下调用 ucrtbased.sub_62618FA0
,该函数的主要功能就是判断 _iobuf->_flag & 4
和 4
是否相等,eax
返回 1
表示相等
最后调用 ucrtbased.sub_62618FA0
判断 _flag & 8
是否等于 0
,ucrtbased.sub_62619070
(四级函数)返回 0
接着向下调试,发现会将 _iobuf->*ptr
赋值为 iobuf->cnt
, _iobuf->*_base
设置为 0
,并且判断 _iobuf->flag & 4
和 4
是否相等, 再相等的前提下判断 _flag & 8
是否为 0
最后调用 ucrtbased.sub_62619070
(四级函数)
进入这个函数看看:首先会使用 _get_osfhandle
检索与指定文件描述符关联的操作系统文件句柄,如果函数调用失败则进入错误处理
之后调用 ucrtbased.sub_62619070
这个函数的功能是利用 SetFilePointerEx
函数移动指定文件的文件指针,并配合 GetLastError
函数进行错误处理
接着将返回值放入局部变量中
最后返回
在 ucrtbased.sub_6261ccE0
的结尾对这两个返回值进行比较,如图所示跳转实现,故返回值为 0
最后使用 _unlock_file
对文件进行解锁,fseek
函数调用结束
逆向 stdio.h 库的
fseek
函数到此结束,如有错误,欢迎指正