日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

64位c语言调用32位glibc,glibc fclose源代码阅读及伪造_IO_FILE利用fclose实现任意地址执行...

發布時間:2025/3/15 编程问答 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 64位c语言调用32位glibc,glibc fclose源代码阅读及伪造_IO_FILE利用fclose实现任意地址执行... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

簡介

最近學習了一下_IO_FILE的利用,剛好在pwnable.tw上碰到一道相關的題目。拿來做了一下,遇到了一些困難,不過順利解決了,順便讀了一波相關源碼,對_IO_FILE有了更深的理解。

文章分為三部分,分別是利用原理、實例和源碼閱讀。源碼部分比較無聊所以我把它放在了最后。

原理

原理

我們使用fopen打開一個文件會在堆上分配一塊內存區域用來存儲FILE結構體,存儲的結構體包含兩個部分,前一部分為_IO_FILE結構體file,后一部分是一個指向struct IO_jump_t的指針vtable, 這個結構體種存儲著一系列與文件IO相關的函數指針。

在我們調用fclose關閉一個文件時,我們最終會調用到vtable中存儲的函數指針。如果我們能夠將vtable中的指針替換為我們自己想要跳轉到的地址就可以劫持程序流程。

利用前提

本文僅考慮libc版本 <= 2.23的情況。因為大于等于2.24的libc會對vtable的位置做判斷,無法令其指向自己構造的區域

可以控制vtable指針或者fp指針指向的位置

有一塊已知地址的可控內存區域,大小需要視情況而定

利用方式1:直接覆蓋vtable指針

這個沒什么好說的,將vtable指針指向可控內存,將__finish(off=2*SIZE_T)構造為要執行的地址即可

利用方式2:覆蓋fp指針

有的時候我們無法直接控制FILE結構體的vtable指針,但是我們可以控制文件指針。因此我們需要偽造整個FILE結構體,然后控制vtable指針指向我們自己構造的函數列表,在__finish(off=2*SIZE_T)位置布置好我們想要調用的地址,最后調用fclose。

這種方式的 關鍵 在于要偽造一個合適的FILE結構體使得在fclose的過程中不會觸發異常造成程序異常終止。為了避免這種情況,一種最簡單的方式就是將FILE結構體的_flags變量的_IO_IS_FILEBUF標志位置0。例如置為0xffffdfff。這樣做的主要原因是為了繞過一些操作。

if (fp->_IO_file_flags & _IO_IS_FILEBUF)

_IO_un_link ((struct _IO_FILE_plus *) fp);

_IO_acquire_lock (fp);

if (fp->_IO_file_flags & _IO_IS_FILEBUF)

status = _IO_file_close_it (fp);

else

status = fp->_flags & _IO_ERR_SEEN ? -1 : 0;

_IO_release_lock (fp);

_IO_FINISH (fp);

相關代碼如上。

可以看到當_IO_IS_FILEBUF位為0時,函數不會執行_IO_un_link和_IO_file_close_it函數,而直接執行_IO_FINISH函數。在_IO_FINISH函數中會直接調用vtable中的__finish函數。其中_IO_IS_FILEBUF被定義為0x2000。

#define _IO_IS_FILEBUF 0x2000

利用實例

題目簡介

測試用的題目來源于pwnable.tw,題目名為seethefile。為FILE結構體利用的一道比較經典的題目。在這里只談一下解題思路,不給出exp。

逆向分析概要

openfile打開一個文件,文件名由用戶輸入,但是當文件名含flag時會退出程序

readfile將文件中的內容讀入一個全局字符數組中

writefile將全局字符數組中的內容輸出到屏幕上

closefile關閉文件

當輸入為5時程序要求輸入一個名字,然后關閉存儲文件指針fp,退出程序

main.png

以上步驟的文件指針都存放在一個全局變量中,bss對應的結構如下

bss.png

漏洞分析

由逆向結果可知。在讀取數字時存在棧溢出,但是程序開啟了棧保護,所以不可利用;

當輸入命令5,讀取用戶指令時存在一個bss段的溢出,利用這個溢出我們可以覆蓋fp的指針指向我們想要的位置,同時可以偽造FILE結構體。利用fclose來實現攻擊。

漏洞利用

leak libc

題目給出了libc的文件,為了執行libc中的system命令,還需要獲取libc加載的基址。我們可以通過打開/proc/self/mmap這個虛擬文件來獲取當前進程的地址空間情況。獲得到libc的加載基址后就可以計算出libc中system的偏移。接下來我們就可以利用_IO_FILE結構體進行攻擊。

構造FILE

構造FILE結構體只需要關注兩個變量,第一個為FILE結構體的_flags字段,只需要_flags & 0x2000為0就會直接調用_IO_FINSH(fp),_IO_FINISH(fp)相當于調用fp->vtabl->__finish(fp)。

將fp指向一塊內存P,P偏移0的前4字節設置為0xffffdfff,P偏移4位置放上要執行的字符串指令(字符串以';'開頭即可),P偏移sizeof(_IO_FILE)大小位置(vtable)覆蓋為內存區域Q,Q偏移2*4字節處(vtable->__finish)覆蓋為system函數地址即可。

glibc fclose源碼學習

glibc的版本為2.23.90,復制粘貼比較多,主要是為了方便查閱。以下內容對大部分的fclose函數進行了層層解剖,很多部分與漏洞利用無太大關系,按需取用。

_IO_FILE與_IO_FILE_plus結構體

在閱讀fclose前先來了解一些有關于FILE結構體的知識。

在C語言中,成功調用fopen函數后會在堆上分配一塊空間用于存放_IO_FILE_plus結構體,并且返回結構體的首地址。閱讀源碼可以發現_IO_FILE_plus結構體只是在_IO_FILE結構體后添加了一個虛表指針 vtable。

/* _IO_FILE_plus結構體 */

/* in libio/libioP.h */

struct _IO_FILE_plus

{

_IO_FILE file;

const struct _IO_jump_t *vtable;

};

虛表指針指向了如下的一個結構體。JUMP_FIELD是一個接收兩個參數的宏,前一個參數為類型名,后一個為變量名。結構體的前兩個變量實際上不會被使用到,所以默認為0,其余的變量存儲著不同的函數指針,在使用FILE結構體進行IO操作的過程中會通過這些函數指針調用到對應的函數。

/*_IO_jump_t虛表結構體*/

/* in libio/libioP.h */

struct _IO_jump_t

{

JUMP_FIELD(size_t, __dummy);

JUMP_FIELD(size_t, __dummy2);

JUMP_FIELD(_IO_finish_t, __finish);

JUMP_FIELD(_IO_overflow_t, __overflow);

JUMP_FIELD(_IO_underflow_t, __underflow);

JUMP_FIELD(_IO_underflow_t, __uflow);

JUMP_FIELD(_IO_pbackfail_t, __pbackfail);

/* showmany */

JUMP_FIELD(_IO_xsputn_t, __xsputn);

JUMP_FIELD(_IO_xsgetn_t, __xsgetn);

JUMP_FIELD(_IO_seekoff_t, __seekoff);

JUMP_FIELD(_IO_seekpos_t, __seekpos);

JUMP_FIELD(_IO_setbuf_t, __setbuf);

JUMP_FIELD(_IO_sync_t, __sync);

JUMP_FIELD(_IO_doallocate_t, __doallocate);

JUMP_FIELD(_IO_read_t, __read);

JUMP_FIELD(_IO_write_t, __write);

JUMP_FIELD(_IO_seek_t, __seek);

JUMP_FIELD(_IO_close_t, __close);

JUMP_FIELD(_IO_stat_t, __stat);

JUMP_FIELD(_IO_showmanyc_t, __showmanyc);

JUMP_FIELD(_IO_imbue_t, __imbue);

#if 0

get_column;

set_column;

#endif

};

_IO_FILE結構體的定義如下。__flags FILE結構體的一些狀態;_markers為指向markers結構體的指針變量,為一個單向鏈表結構,存放流的位置;_chain變量為一個鏈表的指針,進程中創建的FILE結構體會通過這個變量連成一個單向鏈表;

另一點需要注意的是在新版本中,_IO_FILE_complete結構體被刪除,其中的字段被添加到_IO_FILE結構體中

/*_IO_FILE結構體*/

/* libio/libio.h */

struct _IO_FILE {

int _flags; /* High-order word is _IO_MAGIC; rest is flags. */

#define _IO_file_flags _flags

/* The following pointers correspond to the C++ streambuf protocol. */

/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */

char* _IO_read_ptr; /* Current read pointer */

char* _IO_read_end; /* End of get area. */

char* _IO_read_base; /* Start of putback+get area. */

char* _IO_write_base; /* Start of put area. */

char* _IO_write_ptr; /* Current put pointer. */

char* _IO_write_end; /* End of put area. */

char* _IO_buf_base; /* Start of reserve area. */

char* _IO_buf_end; /* End of reserve area. */

/* The following fields are used to support backing up and undo. */

char *_IO_save_base; /* Pointer to start of non-current get area. */

char *_IO_backup_base; /* Pointer to first valid character of backup area */

char *_IO_save_end; /* Pointer to end of non-current get area. */

struct _IO_marker *_markers;

struct _IO_FILE *_chain;

int _fileno;

#if 0

int _blksize;

#else

int _flags2;

#endif

_IO_off_t _old_offset; /* This used to be _offset but it's too small. */

#define __HAVE_COLUMN /* temporary */

/* 1+column number of pbase(); 0 is unknown. */

unsigned short _cur_column;

signed char _vtable_offset;

char _shortbuf[1];

/* char* _save_gptr; char* _save_egptr; */

_IO_lock_t *_lock;

#ifdef _IO_USE_OLD_IO_FILE

};

struct _IO_FILE_complete

{

struct _IO_FILE _file;

#endif

#if defined _G_IO_IO_FILE_VERSION && _G_IO_IO_FILE_VERSION == 0x20001

_IO_off64_t _offset;

# if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T

/* Wide character stream stuff. */

struct _IO_codecvt *_codecvt;

struct _IO_wide_data *_wide_data;

struct _IO_FILE *_freeres_list;

void *_freeres_buf;

# else

void *__pad1;

void *__pad2;

void *__pad3;

void *__pad4;

# endif

size_t __pad5;

int _mode;

/* Make sure we don't get into trouble again. */

char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];

#endif

};

fclose代碼

為了精簡我刪掉了一部分關系不大的代碼,下面的函數是新版本的fclose代碼,IO_old_fclose主要代碼與新版本類似

/* libio/iofclose.c */

int

_IO_new_fclose (_IO_FILE *fp)

{

int status;

/*這里本來有個對版本進行檢測的代碼,根據FILE結構中_vtable_offset變量是否為0來判斷,不為0則執行_IO_old_fclose*/

/* First unlink the stream. */

if (fp->_IO_file_flags & _IO_IS_FILEBUF)

_IO_un_link ((struct _IO_FILE_plus *) fp);

_IO_acquire_lock (fp);

if (fp->_IO_file_flags & _IO_IS_FILEBUF)

status = _IO_file_close_it (fp);

else

status = fp->_flags & _IO_ERR_SEEN ? -1 : 0;

_IO_release_lock (fp);

_IO_FINISH (fp);

if (fp->_mode > 0)

{

#if _LIBC

/* This stream has a wide orientation. This means we have to free

the conversion functions. */

struct _IO_codecvt *cc = fp->_codecvt;

__libc_lock_lock (__gconv_lock);

__gconv_release_step (cc->__cd_in.__cd.__steps);

__gconv_release_step (cc->__cd_out.__cd.__steps);

__libc_lock_unlock (__gconv_lock);

#endif

}

else

{

if (_IO_have_backup (fp))

_IO_free_backup_area (fp);

}

if (fp != _IO_stdin && fp != _IO_stdout && fp != _IO_stderr)

{

fp->_IO_file_flags = 0;

free(fp);

}

return status;

}

該函數的流程可以粗略地進行如下表示:

fclose flowchart.png

流程圖中有幾個關鍵函數,至于加解鎖什么的我忽略了:

_IO_un_link

_IO_file_close_it

_IO_FINISH

_IO_free_backup_area

free

在檢查vtable_offset==0之后函數對fp->_flags的_IO_IS_FILEBUF位進行檢查,_IO_IS_FILEBUF定義如下

#define _IO_IS_FILEBUF 0x2000

若該位不為0則調用_IO_un_link(fp)將fp指向的FILE結構體從_IO_list_all的單向鏈表中取下,并調用_IO_file_close_it(fp)關閉fp。

然后將調用_IO_FINISH(fp),相當于執行((struct IO_FILE_plus *)fp->vtable)->__finish(fp)。

_IO_un_link

/* in libio/genops.c */

void

_IO_un_link (struct _IO_FILE_plus *fp)

{

if (fp->file._flags & _IO_LINKED)

{

struct _IO_FILE **f;

#ifdef _IO_MTSAFE_IO

_IO_cleanup_region_start_noarg (flush_cleanup);

_IO_lock_lock (list_all_lock);

run_fp = (_IO_FILE *) fp;

_IO_flockfile ((_IO_FILE *) fp);

#endif

if (_IO_list_all == NULL)

;

else if (fp == _IO_list_all)

{

_IO_list_all = (struct _IO_FILE_plus *) _IO_list_all->file._chain;

++_IO_list_all_stamp;

}

else

for (f = &_IO_list_all->file._chain; *f; f = &(*f)->_chain)

if (*f == (_IO_FILE *) fp)

{

*f = fp->file._chain;

++_IO_list_all_stamp;

break;

}

fp->file._flags &= ~_IO_LINKED;

#ifdef _IO_MTSAFE_IO

_IO_funlockfile ((_IO_FILE *) fp);

run_fp = NULL;

_IO_lock_unlock (list_all_lock);

_IO_cleanup_region_end (0);

#endif

}

}

_IO_un_link首先判斷fp的標志位中的_IO_LINKED是否置位,若置位進行下一步操作,最后將其清零

#define _IO_LINKED 0x80 /* Set if linked (using _chain) to streambuf::_list_all.*/

若_IO_list_all != fp則_IO_un_link函數將從_IO_list_all開始遍歷鏈表,尋找fp指針,找到后將其前一個節點指針指向后一個節點指針即指向fp->file._chain;若_IO_list_all==fp則將全局變量_IO_list_all的值更改為IO_list_all->file._chain。

_IO_file_close_it

/* in libio/fileops.c */

/* 在新版本中 _IO_file_close_it被定義為_IO_new_file_close_it */

int

_IO_new_file_close_it (_IO_FILE *fp)

{

int write_status;

if (!_IO_file_is_open (fp))

return EOF;

if ((fp->_flags & _IO_NO_WRITES) == 0

&& (fp->_flags & _IO_CURRENTLY_PUTTING) != 0)

write_status = _IO_do_flush (fp);

else

write_status = 0;

_IO_unsave_markers (fp);

int close_status = ((fp->_flags2 & _IO_FLAGS2_NOCLOSE) == 0

? _IO_SYSCLOSE (fp) : 0);

/* Free buffer. */

#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T

if (fp->_mode > 0)

{

if (_IO_have_wbackup (fp))

_IO_free_wbackup_area (fp);

_IO_wsetb (fp, NULL, NULL, 0);

_IO_wsetg (fp, NULL, NULL, NULL);

_IO_wsetp (fp, NULL, NULL);

}

#endif

_IO_setb (fp, NULL, NULL, 0);

_IO_setg (fp, NULL, NULL, NULL);

_IO_setp (fp, NULL, NULL);

_IO_un_link ((struct _IO_FILE_plus *) fp);

fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;

fp->_fileno = -1;

fp->_offset = _IO_pos_BAD;

return close_status ? close_status : write_status;

}

_IO_new_file_close_it首先根據fp->_fileno是否為0判斷文件是否打開

#define _IO_file_is_open(__fp) ((__fp)->_fileno != -1)

若文件未打開,則直接返回EOF。否則函數將繼續執行

if ((fp->_flags & _IO_NO_WRITES) == 0

&& (fp->_flags & _IO_CURRENTLY_PUTTING) != 0)

write_status = _IO_do_flush (fp);

以上代碼將fp中未輸出的部分輸出,_IO_do_flush(fp)定義如下

#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T

# define _IO_do_flush(_f) \

((_f)->_mode <= 0 \

? _IO_do_write(_f, (_f)->_IO_write_base, \

(_f)->_IO_write_ptr-(_f)->_IO_write_base) \

: _IO_wdo_write(_f, (_f)->_wide_data->_IO_write_base, \

((_f)->_wide_data->_IO_write_ptr \

- (_f)->_wide_data->_IO_write_base)))

#else

# define _IO_do_flush(_f) \

_IO_do_write(_f, (_f)->_IO_write_base, \

(_f)->_IO_write_ptr-(_f)->_IO_write_base)

#endif

不做過多解釋。

然后fclose將調用_IO_unsave_markers(fp)將保存的markers清除,在這個版本的libc代碼中,這個函數有一部分功能還沒完成,用(#define TODO圍著),唯一值得注意的是函數最后

if (_IO_have_backup (fp))

_IO_free_backup_area (fp);

void

_IO_free_backup_area (_IO_FILE *fp)

{

if (_IO_in_backup (fp))

_IO_switch_to_main_get_area (fp); /* Just in case. */

free (fp->_IO_save_base);

fp->_IO_save_base = NULL;

fp->_IO_save_end = NULL;

fp->_IO_backup_base = NULL;

}

如果fp->_IO_save_base不為空,它將被free。

之后在_IO_new_file_close_it中執行了

int close_status = ((fp->_flags2 & _IO_FLAGS2_NOCLOSE) == 0

? _IO_SYSCLOSE (fp) : 0);

當fp->_flags2的_IO_FLAGS2_NOCLOSE沒有被置位時,會調用_IO_SYSCLOSE(fp),相當于調用_IO_FILE_plus結構體中的vtable中的__close函數。這一次調用_IO_un_link好像并沒有實際作用?

最后又調用了_IO_un_link(fp)并設置了一些flags

_IO_un_link ((struct _IO_FILE_plus *) fp);

fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;

fp->_fileno = -1;

fp->_offset = _IO_pos_BAD;

_IO_have_backup

上面已經提到了,略略略

free(fp)

用戶打開的FILE結構體是分配在堆上的,在fclose中最終會被free釋放。

至此glibc的fclose源代碼分析完畢。

總結

以上是生活随笔為你收集整理的64位c语言调用32位glibc,glibc fclose源代码阅读及伪造_IO_FILE利用fclose实现任意地址执行...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。