歡迎您光臨本站 註冊首頁

如何獲取應用程序的絕對路徑

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  經常看到有人提問在linux中如何獲取當前應用程序的絕對路徑, 卻很少有人能比較好的解答. 現轉貼www.linuxforum.net上的一篇文章, 希望能對受這個問題困擾的人有幫助.



13.12 如何獲取當前進程對應之靜態映像文件的絕對路徑

A: hushui110@水木清華

這是一個x86/Linux Kernel 2.4.7-10系統中利用proc獲取絕對路徑的例子

--------------------------------------------------------------------------

/*

* gcc -Wall -pipe -g -static -o myprog_2 myprog_2.c

*/

#include

#include

#include



#define MAXBUFSIZE 1024



int main ( int argc, char * argv[] )

{

char buf[ MAXBUFSIZE ];

int count;



count = readlink( "/proc/self/exe", buf, MAXBUFSIZE );

if ( count < 0 || count >= MAXBUFSIZE )

{

printf( "Failed\n" );

return( EXIT_FAILURE );

}

buf[ count ] = '\0';

printf( "/proc/self/exe -> [%s]\n", buf );

return( EXIT_SUCCESS );

} /* end of main */


--------------------------------------------------------------------------

[scz@ /home/scz/src]> echo $PATH
/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:.
[scz@ /home/scz/src]> ./myprog_2
/proc/self/exe -> [/home/scz/src/myprog_2]
[scz@ /home/scz/src]> ../src/myprog_2
/proc/self/exe -> [/home/scz/src/myprog_2]
[scz@ /home/scz/src]> myprog_2
/proc/self/exe -> [/home/scz/src/myprog_2]
[scz@ /home/scz/src]>

顯然這裡直接給出了最期待的結果,沒有冗餘信息。

A: scz & microcat
2000-03-18

下面在x86/Linux Kernel 2.4.7-10上演示、討論

--------------------------------------------------------------------------

*

* gcc -Wall -pipe -g -static -o myprog myprog.c

*/

#include

#include



int main ( int argc, char * argv[] )

{

return( EXIT_SUCCESS );

} /* end of main */


--------------------------------------------------------------------------

[scz@ /home/scz/src]> gcc -Wall -pipe -g -static -o myprog myprog.c
[scz@ /home/scz/src]> echo $PATH
/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:.
[scz@ /home/scz/src]> gdb ./myprog
(gdb) b main
(gdb) r
(gdb) x/17s 0xbfffff00
0xbfffff00: "SHLVL=1"
0xbfffff08: "_=/bin/bash"
0xbfffff14: "SHELL=/bin/bash"
0xbfffff24: "HOSTTYPE=i386"
0xbfffff32: "OSTYPE=linux-gnu"
0xbfffff43: "HISTSIZE=1000"
0xbfffff51: "TERM=vt100"
0xbfffff5c: "HOME=/home/scz"
0xbfffff6b: "SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass"
0xbfffff9e: "PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:."
0xbfffffd2: "LESSCHARSET=latin1"
0xbfffffe5: "/home/scz/src/./myprog" <-- 注意這個輸出
0xbffffffc: ""
0xbffffffd: ""
0xbffffffe: ""
0xbfffffff: ""
0xc0000000:

[scz@ /home/scz/src]> gdb myprog
(gdb) b main
(gdb) r
(gdb) x/17s 0xbfffff00
0xbfffff00: "z"
0xbfffff02: "SHLVL=1"
0xbfffff0a: "_=/bin/bash"
0xbfffff16: "SHELL=/bin/bash"
0xbfffff26: "HOSTTYPE=i386"
0xbfffff34: "OSTYPE=linux-gnu"
0xbfffff45: "HISTSIZE=1000"
0xbfffff53: "TERM=vt100"
0xbfffff5e: "HOME=/home/scz"
0xbfffff6d: "SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass"
0xbfffffa0: "PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:."
0xbfffffd4: "LESSCHARSET=latin1"
0xbfffffe7: "/home/scz/src/myprog" <-- 注意這個輸出
0xbffffffc: ""
0xbffffffd: ""
0xbffffffe: ""
0xbfffffff: ""
[scz@ /home/scz/src]> gdb ../src/myprog
(gdb) b main
(gdb) r
(gdb) x/17s 0xbfffff00
0xbfffff00: "=1"
0xbfffff03: "_=/bin/bash"
0xbfffff0f: "SHELL=/bin/bash"
0xbfffff1f: "HOSTTYPE=i386"
0xbfffff2d: "OSTYPE=linux-gnu"
0xbfffff3e: "HISTSIZE=1000"
0xbfffff4c: "TERM=vt100"
0xbfffff57: "HOME=/home/scz"
0xbfffff66: "SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass"
0xbfffff99: "PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:."
0xbfffffcd: "LESSCHARSET=latin1"
0xbfffffe0: "/home/scz/src/../src/myprog" <-- 注意這個輸出
0xbffffffc: ""
0xbffffffd: ""
0xbffffffe: ""
0xbfffffff: ""
0xc0000000:

[scz@ /home/scz/src]>

這是ELF文件在Linux系統中載入進內存之後的布局簡圖

--------------------------------------------------------------------------
0x08048000 code .text,代碼,只讀
data .data,包含已經初始化的數據,只讀
bss .bss,未初始化數據,初始化成0,讀/寫
... 堆區,動態分配獲取的內存從.bss往內存高端增長
... (heap),讀/寫
...
stack 棧區,起始地址大於0xBFFF0000
arguments main()的形參
environment 環境變數區域
program name execve()第一形參,不是argv[0]
0xBFFFFFFC null(dword) 最後四個位元組固定為零
0xC0000000
--------------------------------------------------------------------------

通常動態鏈接庫被映射到0x40000000往高端的地址。對於Linux,如果打了不可執行
堆棧內核補丁,動態鏈接庫被映射到0x40000000往低端的地址。tt說就是以前的映射
地址減去0x40000000。打了補丁后使得通過字元串拷貝(strcpy)傳遞shellcode相對
複雜化,需要更多技巧。

program name處不一定是絕對路徑,實際對應了execve()第一形參。一般從shell上
啟動進程,shell根據PATH環境變數自動搜索匹配出一個路徑,未必是絕對路徑。假
設PATH環境變數中有當前目錄(.),所執行的程序也只在當前目錄下有,直接指定程
序名(myprog)執行時,shell會向execve()第一形參傳遞"./myprog"。注意,execve
第一形參未必是絕對路徑。用gdb載入后再執行,情況有所不同。如果gdb命令行上指
定的程序名非絕對路徑,gdb在調用execve()之前會調用getcwd()拼接在程序名之前,

此時不依賴PATH環境變數。所以用gdb調試溢出程序時應該在命令行上指定絕對路徑,

避免不必要的偏移調整。

現在來看這樣一個演示程序

--------------------------------------------------------------------------

/*

* gcc -Wall -pipe -g -static -o myprog_1 myprog_1.c

*/

#include

#include



extern char **environ;



int main ( int argc, char * argv[] )

{

char *path = ( char * )( 0xc0000000 - 5 );



while ( *--path );

++path;

printf( "path --> %08x [%s]\n", ( unsigned int )path, path );

printf( "argv --> %08x\n", ( unsigned int )argv );

printf( "argv[0] --> %08x [%s]\n", ( unsigned int )argv[0],
argv[0] );

printf( "environ --> %08x\n", ( unsigned int )environ );

printf( "environ[0] --> %08x [%s]\n", ( unsigned int )environ[0],
environ[0] );

return( EXIT_SUCCESS );

} /* end of main */


--------------------------------------------------------------------------

直接執行,然後用gdb載入后執行,對比輸出上的不同。

[scz@ /home/scz/src]> myprog_1
path --> bffffff1 [./myprog_1] <-- 注意這裡前面多了一個".>argv --> bffffb24
argv[0] --> bffffc29 [myprog_1]
environ --> bffffb2c
environ[0] --> bffffc32 [PWD=/home/scz/src]
[scz@ /home/scz/src]> ./myprog_1
path --> bffffff1 [./myprog_1]
argv --> bffffb24
argv[0] --> bffffc27 [./myprog_1]
environ --> bffffb2c
environ[0] --> bffffc32 [PWD=/home/scz/src]
[scz@ /home/scz/src]> ../src/myprog_1
path --> bfffffec [../src/myprog_1]
argv --> bffffb14
argv[0] --> bffffc18 [../src/myprog_1]
environ --> bffffb1c
environ[0] --> bffffc28 [PWD=/home/scz/src]
[scz@ /home/scz/src]>

Linux系統中main()函數里自修改argv[0]欺騙ps有效,而FreeBSD、Solaris則無效。

execve()第二形參可以指定不同於第一形參的argv[0]。argv[0]不可信,但execve()
第一形參相對就可信得多。如果是自己寫程序,可以考慮在main()中第一時刻判斷
execve()第一形參是否為相對路徑,進而決定是否調用getcwd(),最後拼接出一個絕
對路徑。就像gdb所做的那樣。

需要考慮"/home/scz/src/./myprog"、"/home/scz/src/../src/myprog"這些情況,
消掉"./"、"../"等冗餘信息。

A: scz & microcat
2002-09-13 18:00

下面在SARPC/Solaris 8 64-bit kernel mode上演示、討論

--------------------------------------------------------------------------

/*

* gcc -Wall -pipe -g -static -o myprog_3 myprog_3.c

*/

#include

#include



extern char **environ;



static void outputBinary ( const unsigned char *byteArray, const size_t
byteArrayLen )

{

size_t offset, k, j, i;



fprintf( stderr, "byteArray [ %u bytes ] ----> \n", byteArrayLen );

if ( byteArrayLen <= 0 )

{

return;

}

i = 0;

offset = 0;

for ( k = byteArrayLen / 16; k > 0; k--, offset += 16 )

{

fprintf( stderr, "%08X ", offset );

for ( j = 0; j < 16; j++, i++ )

{

if ( j == 8 )

{

fprintf( stderr, "-%02X", byteArray[ i ] );

}

else

{

fprintf( stderr, " %02X", byteArray[ i ] );

}

}

fprintf( stderr, " " );

i -= 16;

for ( j = 0; j < 16; j++, i++ )

{

/*

* if ( isprint( (int)byteArray[ i ] ) )

*/

if ( ( byteArray[ i ] >= ' ' ) && ( byteArray[ i ]
<= 255 )

&& ( byteArray[ i ] != 0x7f ) )

{

fprintf( stderr, "%c", byteArray[ i ] );

}

else

{

fprintf( stderr, "." );

}

}

fprintf( stderr, "\n" );

} /* end of for */

k = byteArrayLen - i;

if ( k <= 0 )

{

return;

}

fprintf( stderr, "%08X ", offset );

for ( j = 0 ; j < k; j++, i++ )

{

if ( j == 8 )

{

fprintf( stderr, "-%02X", byteArray[ i ] );

}

else

{

fprintf( stderr, " %02X", byteArray[ i ] );

}

}

i -= k;

for ( j = 16 - k; j > 0; j-- )

{

fprintf( stderr, " " );

}

fprintf( stderr, " " );

for ( j = 0; j < k; j++, i++ )

{

if ( ( byteArray[ i ] >= ' ' ) && ( byteArray[ i ] <=
255 )

&& ( byteArray[ i ] != 0x7f ) )

{

fprintf( stderr, "%c", byteArray[ i ] );

}

else

{

fprintf( stderr, "." );

}

}

fprintf( stderr, "\n" );

return;

} /* end of outputBinary */



int main ( int argc, char * argv[] )

{

char *path = ( char * )( 0xffbf0000 - 5 );



while ( *path == '\0' )

{

path--;

}

while ( *path )

{

path--;

}

++path;

printf( "path --> %08x [%s]\n", ( unsigned int )path, path );

printf( "argv --> %08x\n", ( unsigned int )argv );

printf( "argv[0] --> %08x [%s]\n", ( unsigned int )argv[0],
argv[0] );

printf( "environ --> %08x\n", ( unsigned int )environ );

printf( "environ[0] --> %08x [%s]\n", ( unsigned int )environ[0],
environ[0] );

outputBinary( ( const unsigned char * )( 0xffbf0000 - 256 ), 256 );

return( EXIT_SUCCESS );

} /* end of main */


--------------------------------------------------------------------------

直接執行,然後用gdb載入后執行,對比輸出上的不同。SPARC/Solaris與x86/Linux
在此問題上有不少區別。

[scz@ /home/scz/src]> echo $PATH
/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:.
[scz@ /export/home/scz/src]> myprog_3
path --> ffbeffee [myprog_3]
argv --> ffbefcfc
argv[0] --> ffbefd7c [myprog_3]
environ --> ffbefd04
environ[0] --> ffbefd85 [PWD=/export/home/scz/src]
[scz@ /export/home/scz/src]> ./myprog_3
path --> ffbefff0 [myprog_3] <-- 注意這裡前面反而沒有了".>argv --> ffbefcfc
argv[0] --> ffbefd7c [./myprog_3]
environ --> ffbefd04
environ[0] --> ffbefd87 [PWD=/export/home/scz/src]
[scz@ /export/home/scz/src]> ../src/myprog_3
path --> ffbeffea [../src/myprog_3]
argv --> ffbefcec
argv[0] --> ffbefd6c [../src/myprog_3]
environ --> ffbefcf4
environ[0] --> ffbefd7c [PWD=/export/home/scz/src]
[scz@ /export/home/scz/src]>

如果我們複製myprog_3到/usr/bin下去,再次執行myprog_3,可以看到如下輸出

[scz@ /export/home/scz/src]> myprog_3
path --> ffbeffe9 [/usr/bin/myprog_3] <-- 注意這裡
argv --> ffbefcf4
argv[0] --> ffbefd74 [myprog_3]
environ --> ffbefcfc
environ[0] --> ffbefd7d [PWD=/export/home/scz/src]
byteArray [ 256 bytes ] ---->
00000000 55 6C 74 72 61 2D 35 5F-31 30 00 53 48 4C 56 4C Ultra-5_10.SHLVL
00000010 3D 31 00 5F 49 4E 49 54-5F 55 54 53 5F 4D 41 43 =1._INIT_UTS_MAC
00000020 48 49 4E 45 3D 73 75 6E-34 75 00 53 48 45 4C 4C HINE=sun4u.SHELL
00000030 3D 2F 62 69 6E 2F 62 61-73 68 00 48 4F 53 54 54 =/bin/bash.HOSTT
00000040 59 50 45 3D 73 70 61 72-63 00 4F 53 54 59 50 45 YPE=sparc.OSTYPE
00000050 3D 73 6F 6C 61 72 69 73-32 2E 38 00 48 4F 4D 45 =solaris2.8.HOME
00000060 3D 2F 65 78 70 6F 72 74-2F 68 6F 6D 65 2F 73 63 =/export/home/sc
00000070 7A 00 54 45 52 4D 3D 76-74 31 30 30 00 50 41 54 z.TERM=vt100.PAT
00000080 48 3D 2F 62 69 6E 3A 2F-75 73 72 2F 62 69 6E 3A H=/bin:/usr/bin:
00000090 2F 73 62 69 6E 3A 2F 75-73 72 2F 73 62 69 6E 3A /sbin:/usr/sbin:
000000A0 2F 75 73 72 2F 6C 6F 63-61 6C 2F 62 69 6E 3A 2E /usr/local/bin:.
000000B0 00 5F 49 4E 49 54 5F 4E-45 54 5F 53 54 52 41 54 ._INIT_NET_STRAT
000000C0 45 47 59 3D 6E 6F 6E 65-00 5F 3D 2F 62 69 6E 2F EGY=none._=/bin/
000000D0 6D 79 70 72 6F 67 5F 33-00 53 55 4E 57 2C 55 6C myprog_3.SUNW,Ul
000000E0 74 72 61 2D 35 5F 31 30-00 2F 75 73 72 2F 62 69 tra-5_10./usr/bi
000000F0 6E 2F 6D 79 70 72 6F 67-5F 33 00 00 00 00 00 00 n/myprog_3......
[scz@ /export/home/scz/src]>

前面說的getcwd()拼接技術同樣適用於SPARC/Solaris 8。

與Linux相比,有幾處重要變化,一是從0xC0000000變成0xFFBF0000,二是如果程序
在當前目錄下,沒有了前面的"./",三是SPARC晶元的一些對齊特性導致我們要找的
位置可能比想像的要向內存低端移動一些位元組。

后兩點容易理解,那我們是如何確定0xC0000000、0xFFBF0000的呢。這個問題實際上
就是

[火星人 ] 如何獲取應用程序的絕對路徑已經有592次圍觀

http://coctec.com/docs/program/show-post-71954.html