小型elf Hello,World程序
參考鏈接:http://timelessname.com/elfbin/
?
環境要求:linux gcc nasm hexcurse(用來修改elf文件內容)
?
先嘗試用C語言寫"Hello,World"程序(名為chello.c):
?
#include <stdio.h> int main(void) {printf("Hello,World\n");return 0; } ?使用下面命令編譯并運行:
?
[host@myhost linker]$ gcc -o chello chello.c [host@myhost linker]$ ./chello?輸出結果:
?
Hello,World?可以用下面命令查看chello的ELF頭部分:
?
readelf -h chello?輸出結果:
?
ELF Header:Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32Data: 2's complement, little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: EXEC (Executable file)Machine: Intel 80386Version: 0x1Entry point address: 0x8048310Start of program headers: 52 (bytes into file)Start of section headers: 1932 (bytes into file)Flags: 0x0Size of this header: 52 (bytes)Size of program headers: 32 (bytes)Number of program headers: 8Size of section headers: 40 (bytes)Number of section headers: 30Section header string table index: 27?使用下面命令查看chello鏈接的動態鏈接庫:
?
ldd chello? 輸出結果為:
?
linux-gate.so.1 => (0xb7857000)libc.so.6 => /lib/libc.so.6 (0xb76d2000)/lib/ld-linux.so.2 (0xb7858000)? 使用下面命令查看文件類型:
?
file chello? 輸出結果:
?
chello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.27, not stripped? 使用下面命令查看文件大小,并使用strip取出符號表,然后查看文件大小:
?
[host@myhost linker]$ ls -l chello [host@myhost linker]$ strip -s chello [host@myhost linker]$ ls -l chello ??輸出結果為:
?
-rwxr-xr-x 1 host users 4746 11月 6 23:07 chello -rwxr-xr-x 1 host users 3036 11月 6 23:15 chello?
下面使用匯編代碼編寫該程序(hello.asm),調用linux中斷來實現:
?
SECTION .data msg: db "Hello,World",10 len: equ $-msgSECTION .textglobal main main:mov edx,lenmov ecx,msgmov ebx,1mov eax,4int 0x80mov ebx,0mov eax,1int 0x80?使用下面的命令編譯鏈接并取出生成文件的符號表:
?
[host@myhost linker]$ nasm -f elf hello.asm [host@myhost linker]$ gcc -o hello hello.o -nostartfiles -nostdlib -nodefaultlibs [host@myhost linker]$ strip -s hello [host@myhost linker]$ ./hello?輸出結果為:
?
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 08048080 Hello,World?再用gcc命令時會產生一個警告,但該文件仍然能夠執行。此時文件大小為360個字節。
此處鏈接命令“gcc -o hello hello.o -nostartfiles -nostdlib -nodefaultlibs"中幾個選項英文注解如下(鏈接 ):
?
-nostartfilesDo not use the standard system startup files when linking. The standard system libraries are used normally, unless -nostdlib or -nodefaultlibs is used. -nodefaultlibsDo not use the standard system libraries when linking. Only the libraries you specify will be passed to the linker, options specifying linkage of the system libraries, such as -static-libgcc or -shared-libgcc, will be ignored. The standard startup files are used normally, unless -nostartfiles is used. The compiler may generate calls to memcmp, memset, memcpy and memmove. These entries are usually resolved by entries in libc. These entry points should be supplied through some other mechanism when this option is specified. -nostdlibDo not use the standard system startup files or libraries when linking. No startup files and only the libraries you specify will be passed to the linker, options specifying linkage of the system libraries, such as -static-libgcc or -shared-libgcc, will be ignored. The compiler may generate calls to memcmp, memset, memcpy and memmove. These entries are usually resolved by entries in libc. These entry points should be supplied through some other mechanism when this option is specified.One of the standard libraries bypassed by -nostdlib and -nodefaultlibs is libgcc.a, a library of internal subroutines that GCC uses to overcome shortcomings of particular machines, or special needs for some languages. (See Interfacing to GCC Output, for more discussion of libgcc.a.) In most cases, you need libgcc.a even when you want to avoid other standard libraries. In other words, when you specify -nostdlib or -nodefaultlibs you should usually specify -lgcc as well. This ensures that you have no unresolved references to internal GCC library subroutines. (For example, `__main', used to ensure C++ constructors will be called;? 此處startupfiles指的是crt0.o(/lib/crt0.o),crtbegin.o,crtend.o。據說crt0.o包含調用main函數(windows下調用WinMainCRTStartup (參考鏈接 ))的代碼,這里使用匯編代碼(如果是c程序,則需要鏈接crt0.o),所以不用鏈接crt0.o.crtbegin.o和crtend.o據說是用來對c++構造和析構函數進行處理。有一個帖子(Is main required for a c program? also see Why are DJGPP .exe files so large? )說明了不用main函數來編寫c程序(其實只是把入口名稱換換而已,就像Window應用程序使用WinMain來作為入口函數,只不過使用自己的入口函數,相關的一些處理都需要自己來解決)
使用下面命令得到hello的16進制描述:
?
hexdump -x hello?輸出結果:
?
0000000 457f 464c 0101 0001 0000 0000 0000 0000 0000010 0002 0003 0001 0000 8080 0804 0034 0000 0000020 00c8 0000 0000 0000 0034 0020 0002 0028 0000030 0004 0003 0001 0000 0000 0000 8000 0804 0000040 8000 0804 00a2 0000 00a2 0000 0005 0000 0000050 1000 0000 0001 0000 00a4 0000 90a4 0804 0000060 90a4 0804 000c 0000 000c 0000 0006 0000 0000070 1000 0000 0000 0000 0000 0000 0000 0000 0000080 0cba 0000 b900 90a4 0804 01bb 0000 b800 0000090 0004 0000 80cd 00bb 0000 b800 0001 0000 00000a0 80cd 0000 6548 6c6c 2c6f 6f57 6c72 0a64 00000b0 2e00 6873 7473 7472 6261 2e00 6574 7478 00000c0 2e00 6164 6174 0000 0000 0000 0000 0000 00000d0 0000 0000 0000 0000 0000 0000 0000 0000 * 00000f0 000b 0000 0001 0000 0006 0000 8080 0804 0000100 0080 0000 0022 0000 0000 0000 0000 0000 0000110 0010 0000 0000 0000 0011 0000 0001 0000 0000120 0003 0000 90a4 0804 00a4 0000 000c 0000 0000130 0000 0000 0000 0000 0004 0000 0000 0000 0000140 0001 0000 0003 0000 0000 0000 0000 0000 0000150 00b0 0000 0017 0000 0000 0000 0000 0000 0000160 0001 0000 0000 0000 ??分析該文件頭(前52個字節)可以知道兩個比較重要表的內容,第一個是程序頭表(54(0x34)個字節開始,大小為2*32(0x20)個字節),另一個是段頭表(200(0xc8)個字節開始,大小為2*40(0x28)個字節),然后根據段頭表可以知道代碼段(0x80開始34個字節內容)和數據段(0xa4開始12個字節內容)相關信息。比較重要的是前176個字節內容,這部分內容可以分為文件頭(52個字節),程序頭表(64個字節),空白內容(12個字節),代碼段(34個字節),數據段(12個字節,"Hello,World\n")。
先使用下面命令提取hello中前176個字節內容修改權限為可執行:
?
dd if=hello of=hello.new bs=176 count=1 chmod u+x hello.new?得到文件hello.new,執行該文件可以得到"Hello,World".該文件二進制內容:
?
0000000 457f 464c 0101 0001 0000 0000 0000 0000 0000010 0002 0003 0001 0000 8080 0804 0034 0000 0000020 00c8 0000 0000 0000 0034 0020 0002 0028 0000030 0004 0003 0001 0000 0000 0000 8000 0804 0000040 8000 0804 00a2 0000 00a2 0000 0005 0000 0000050 1000 0000 0001 0000 00a4 0000 90a4 0804 0000060 90a4 0804 000c 0000 000c 0000 0006 0000 0000070 1000 0000 0000 0000 0000 0000 0000 0000 0000080 0cba 0000 b900 90a4 0804 01bb 0000 b800 0000090 0004 0000 80cd 00bb 0000 b800 0001 0000 00000a0 80cd 0000 6548 6c6c 2c6f 6f57 6c72 0a64 ??由于數據段大小剛好為12,而文件中有剛好有12個字節空白,可以將數據段(0xa4開始的12個字解)遷移到空白處(0x74開始的12個字節),并將0x86地址的0xa4改為0x74。可以使用hexcurse hello.new修改文件內容,然后用快捷鍵crtl+s保存,并改名為hello.res.此時執行hello.res可以得到"Hello,World"。
然后我們可以刪除最后的12個字節(使用命令dd if=hello.res of=hello.out bs=164 count=1),得到hello.out即為最后的結果,其大小為164個字節。按照原文中描述,該文件應該可以進一步的壓縮,不過這需要對代碼段中部分做一些改動,有空時再詳細研究)
?
linux進程內存布局
而文件頭中還有一個e_entry(0x18地址開始的4個字節,值為0x08048080(0x08048000+0x80(代碼段偏移地址))表示程序入口,即從這個地址開始執行指令。代碼段中(0x86開始的4個字節(小端法表示,9074 0804)即0x08049074,這個地址是0x08048000+0x1000(代碼段虛擬地址所占的空間大小,因為段對齊為0x1000,所以最小為4k大小(分頁機制中每個頁面的大小))+0x74,這個地址是從原來的地址0x080490a4(這個地址也分別存在于原來的0x0000005c和0x00000060開始的4個字節。但是好像0x0000005c和0x00000060中的值不改業能正常運行,但為了使得數據保持一致,最好還是改掉。)。
另外,hello.out只包含ELF頭,程序頭,代碼段和數據段,并且仍然能正常運行,這也證明了可執行文件中段頭表(section Header table)是可選項。
?
?
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的小型elf Hello,World程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 单片机Proteus7.8仿真和Prot
- 下一篇: Qt6 在线安装图文步骤