官术网_书友最值得收藏!

3.8 atexit()和on_exit()函數

atexit()是C標準定義的一個通用工具函數。atexit()可以注冊無參函數,并在程序正常結束后調用該函數。C要求實現支持至少32個函數的注冊。SunOS上的on_exit()函數具有類似的功能。libc4、libc5和glibc也提供了這樣的函數[Bouchareine 2005]。

在例3.11展示的程序中,第8行(main()中)使用atexit()注冊test()函數。該程序在退出前將全局變量glob賦值為字符串“Exiting.\n”(第9行)。test()函數在程序退出后得以執行,并且打印出該字符串。

例3.11 使用atexit()的程序


01  char *glob;
02
03  void test(void) {
04    printf("%s", glob);
05  }
06
07  int main(void) {
08    atexit(test);
09    glob = "Exiting.\n";
10  }

atexit()通過向一個退出時將被調用的已有函數的數組中添加指定的函數完成工作。當exit()被調用時,數組中的每一個函數都以“后進先出”(Last-in,First-out,LIFO)的順序被調用。由于atexit()和exit()都要訪問該數組,因此它被分配為一個全局性的符號(在BSD操作系統中是__atexit,在Linux操作系統中則是__exit_funcs)。

例3.12中使用gdb調試atexit程序的會話過程,展示了atexit數組的位置和結構。在該調試會話中,在main()中調用atexit()之前設了一個斷點,然后運行程序。接下來執行atexit(),注冊test()函數。在test()函數注冊后,顯示了在__exit_funcs位置處的內存。每一個函數都保存在由4個雙字(doubleword)構成的結構中。每一個結構的最后一個雙字保存著函數的實際地址。通過檢查這些地址的內存得知,已經注冊了3個函數:

_dl_fini()、__libc_csu_fini()以及我們編寫的test()。可以通過對__exit_funcs結構采用任意內存寫或緩沖區溢出手段將程序的控制權轉移到任意的代碼。請注意,即使受攻擊的程序不顯式調用atexit()注冊_dl_fini()和__libc_csu_fini()函數,它們也會存在。

例3.12 使用gdb的atexit程序的調試會話


(gdb) b main 
Breakpoint 1 at 0x80483f6: file atexit.c, line 6. 
(gdb) r 
Starting program: /home/rcs/book/dtors/atexit 
Breakpoint 1, main (argc=1, argv=0xbfffe744) at atexit.c:6 
6 atexit(test); 
(gdb) next 
7 glob = "Exiting.\n"; 
(gdb) x/12x __exit_funcs 
0x42130ee0 <init>:    0x00000000 0x00000003 0x00000004 0x4000c660 
0x42130ef0 <init+16>: 0x00000000 0x00000000 0x00000004 0x0804844c 
0x42130f00 <init+32>: 0x00000000 0x00000000 0x00000004 0x080483c8 
(gdb) x/4x 0x4000c660 
0x4000c660 <_dl_fini>: 0x57e58955 0x5ce85356 0x81000054 0x0091c1c3 
(gdb) x/3x 0x0804844c 
0x804844c <__libc_csu_fini>: 0x53e58955 0x9510b850 x102d0804 
(gdb) x/8x 0x080483c8 
0x80483c8 <test>: 0x83e58955 0xec8308ec 0x2035ff08 0x68080496 

主站蜘蛛池模板: 天门市| 尼玛县| 登封市| 遂溪县| 永昌县| 芜湖市| 金湖县| 建平县| 敖汉旗| 白城市| 乌拉特中旗| 安化县| 高州市| 安福县| 南安市| 靖安县| 苗栗市| 盐津县| 南充市| 新宁县| 名山县| 拜泉县| 高雄县| 平乐县| 兰西县| 合江县| 新巴尔虎右旗| 秀山| 宜春市| 东安县| 县级市| 汝城县| 北碚区| 麦盖提县| 广灵县| 晋宁县| 克拉玛依市| 衢州市| 乐至县| 华坪县| 黔江区|