程序破解及ELF文件格式分析

程序破解

NOP、JNE、JE、JMP、CMP匯編指令的機(jī)器碼

NOP:NOP指令即“空指令”。執(zhí)行到NOP指令時(shí),CPU什么也不做,僅僅當(dāng)做一個(gè)指令執(zhí)行過(guò)去并繼續(xù)執(zhí)行NOP后面的一條指令。(機(jī)器碼:90)

JNE:條件轉(zhuǎn)移指令,如果不相等則跳轉(zhuǎn)。(機(jī)器碼:75)

JE:條件轉(zhuǎn)移指令,如果相等則跳轉(zhuǎn)。(機(jī)器碼:74)

JMP:無(wú)條件轉(zhuǎn)移指令。段內(nèi)直接短轉(zhuǎn)Jmp short(機(jī)器碼:EB)段內(nèi)直接近轉(zhuǎn)移Jmp near(機(jī)器碼:E9)段內(nèi)間接轉(zhuǎn)移Jmp word(機(jī)器碼:FF)段間直接(遠(yuǎn))轉(zhuǎn)移Jmp far(機(jī)器碼:EA)

CMP:比較指令,功能相當(dāng)于減法指令,只是對(duì)操作數(shù)之間運(yùn)算比較,不保存結(jié)果。cmp指令執(zhí)行后,將對(duì)標(biāo)志寄存器產(chǎn)生影響。其他相關(guān)指令通過(guò)識(shí)別這些被影響的標(biāo)志寄存器位來(lái)得知比較結(jié)果。

反匯編與十六進(jìn)制編程器

實(shí)驗(yàn)源碼:

login.c

#include<stdio.h>
void main()
{
        int pass=123;
        int enter;
        printf("please enter the password:\n");
        scanf("%d",&enter);
        if(enter==pass)
                printf("right\n");
        else
                printf("wrong\n");
}

運(yùn)行效果如下:

輸入命令:

objdump -d login

查看main函數(shù)

現(xiàn)在的需求是:修改可執(zhí)行文件另其無(wú)論輸入什么密碼的是正確的。

修改思路:將jne 跳轉(zhuǎn)偏移量改為0,這樣40063a位置的指令就相當(dāng)于繼續(xù)執(zhí)行輸出right。

實(shí)現(xiàn)方法:

輸入指令:

vim login

打開(kāi)可執(zhí)行文件

輸入:

%!xxd

進(jìn)入十六進(jìn)制編輯模式,如下圖所示:

然后輸入

/750c

找到j(luò)ne指令的位置,如下圖所示:

將750c改成7500表示跳轉(zhuǎn)到下一條指令繼續(xù)執(zhí)行,相當(dāng)于NOP。

輸入

%!xxd -r

切換回原模式

輸入

wq

保存后退出。

運(yùn)行修改后的文件效果如下:

由圖可見(jiàn)無(wú)論輸入什么密碼都是正確的,修改成功。

如果想輸入什么密碼都是錯(cuò)誤的,只需要將750c改為eb0c即可,eb為JMP,0c為輸出"wrong"的代碼相對(duì)偏移量。

效果如下:

如果想輸入原先正確的密碼為"wrong",輸出其他密碼為"right",只需把750c改為740c即可,74相當(dāng)于JE,相等則跳轉(zhuǎn)。

效果如下:

可執(zhí)行文件的基本格式

Linux可執(zhí)行文件格式為ELF即Executable and Linkable Format。

格式:

ELF header :ELF文件頭,在文件的開(kāi)始,保存了路線圖,描述了該文件的組織情況。

program header table :程序文件頭表,告訴系統(tǒng)如何創(chuàng)建進(jìn)程映像。用來(lái)構(gòu)造進(jìn)程映像的目標(biāo)文件必須具有程序頭部表,可重定位文件不需要這個(gè)表。

.text段:存儲(chǔ)只讀程序

.data段:存儲(chǔ)已經(jīng)初始化的全局變量和靜態(tài)變量

.bss段:存儲(chǔ)未初始化的全局變量和靜態(tài)變量,因?yàn)檫@些變量的值為0,所以這個(gè)段在文件當(dāng)中不占據(jù)空間

.rodata段:存儲(chǔ)只讀數(shù)據(jù),比如字符串常量

...(各種(節(jié)))

Section header table:節(jié)頭部表,包含了描述文件節(jié)區(qū)的信息,每個(gè)節(jié)區(qū)在表中都有一項(xiàng),每一項(xiàng)給出諸如節(jié)區(qū)名稱、節(jié)區(qū)大小這類信息。用于鏈接的目標(biāo)文件必須包含節(jié)區(qū)頭部表,其他目標(biāo)文件可以有,也可以沒(méi)有這個(gè)表。

具體分析見(jiàn)ELF文件格式分析。

ELF文件格式分析

ELF全稱Executable and Linkable Format,可執(zhí)行連接格式,ELF格式的文件用于存儲(chǔ)Linux程序。ELF文件(目標(biāo)文件)格式主要三種:

1)可重定向文件:文件保存著代碼和適當(dāng)?shù)臄?shù)據(jù),用來(lái)和其他的目標(biāo)文件一起來(lái)創(chuàng)建一個(gè)可執(zhí)行文件或者是一個(gè)共享目標(biāo)文件。(目標(biāo)文件或者靜態(tài)庫(kù)文件,即linux通常后綴為.a和.o的文件)

2)可執(zhí)行文件:文件保存著一個(gè)用來(lái)執(zhí)行的程序。(例如bash,gcc等)

3)共享目標(biāo)文件:共享庫(kù)。文件保存著代碼和合適的數(shù)據(jù),用來(lái)被下連接編輯器和動(dòng)態(tài)鏈接器鏈接。(linux下后綴為.so的文件。)

ELF文件頭

查看/usr/include/elf.h中的ELF頭文件數(shù)據(jù)結(jié)構(gòu)。

#define EI_NIDENT (16)

typedef struct
{
  unsigned char e_ident[EI_NIDENT];     /* Magic number and other info */
  Elf32_Half    e_type;                 /* Object file type */
  Elf32_Half    e_machine;              /* Architecture */
  Elf32_Word    e_version;              /* Object file version */
  Elf32_Addr    e_entry;                /* Entry point virtual address */
  Elf32_Off     e_phoff;                /* Program header table file offset */
  Elf32_Off     e_shoff;                /* Section header table file offset */
  Elf32_Word    e_flags;                /* Processor-specific flags */
  Elf32_Half    e_ehsize;               /* ELF header size in bytes */
  Elf32_Half    e_phentsize;            /* Program header table entry size */
  Elf32_Half    e_phnum;                /* Program header table entry count */
  Elf32_Half    e_shentsize;            /* Section header table entry size */
  Elf32_Half    e_shnum;                /* Section header table entry count */
  Elf32_Half    e_shstrndx;             /* Section header string table index */
} Elf32_Ehdr;

typedef struct
{
  unsigned char e_ident[EI_NIDENT];     /* Magic number and other info */
  Elf64_Half    e_type;                 /* Object file type */
  Elf64_Half    e_machine;              /* Architecture */
  Elf64_Word    e_version;              /* Object file version */
  Elf64_Addr    e_entry;                /* Entry point virtual address */
  Elf64_Off     e_phoff;                /* Program header table file offset */
  Elf64_Off     e_shoff;                /* Section header table file offset */
  Elf64_Word    e_flags;                /* Processor-specific flags */
  Elf64_Half    e_ehsize;               /* ELF header size in bytes */
  Elf64_Half    e_phentsize;            /* Program header table entry size */
  Elf64_Half    e_phnum;                /* Program header table entry count */
  Elf64_Half    e_shentsize;            /* Section header table entry size */
  Elf64_Half    e_shnum;                /* Section header table entry count */
  Elf64_Half    e_shstrndx;             /* Section header string table index */
} Elf64_Ehdr;

                                        

取一段簡(jiǎn)單的代碼進(jìn)行分析:

hello.c

#include<stdio.h>
void main()
{
        printf("hello");
}

輸入指令

readelf -h hello

得到ELF文件頭信息:

由圖可看出ELF文件頭大小為64字節(jié)。

輸入命令:

hexdump -x hello -n 64

對(duì)ELF頭的16進(jìn)制表進(jìn)行分析:

第一行對(duì)應(yīng)e_ident[EI_NIDENT],實(shí)際內(nèi)容為:7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 ,前四個(gè)字節(jié)7f454c46(0x45,0x4c,0x46是'e','l','f'對(duì)應(yīng)的ascii編碼)是一個(gè)魔數(shù),表示這是一個(gè)ELF對(duì)象。接下來(lái)02字節(jié)表示是一個(gè)64位對(duì)象,接下來(lái)01字節(jié)表示是小端法表示,再接下來(lái)的01字節(jié)表示文件頭的版本。剩下的默認(rèn)設(shè)置為0。

第二行,e_type值為0x0002,表示是一個(gè)可執(zhí)行文件。e_machine的值為0x003e表示目標(biāo)文件所期待的系統(tǒng)架構(gòu)為Advanced Micro Devices X86-64。e_version值為0x00000001,表示是當(dāng)前版本。e_entry的值為0x00400430表示程序入口地址。

第三行,e_phoff的值為0x0040,表示程序頭部表的起始位置在磁盤(pán)文件中的偏移量為64字節(jié)。e_shoff的值為0x19d8,表示節(jié)頭部表(Section Header Table)的起始位置在磁盤(pán)文件中的偏移量為6616字節(jié)。

第四行,e_flags的值為e_flags值為0x00000000,表示未知處理器特定標(biāo)志。e_ehsize值為0x0040表示ELF文件頭部的大小為64字節(jié)。e_phentsize的值為0x0038,表示程序頭部表(Program Header Table)中每一個(gè)表項(xiàng)的大小56字節(jié)。e_shnum值為0x0009,表示程序頭部表(Program Header Table)中總共有9個(gè)表項(xiàng)。e_shentsize的值為0x0040,表示節(jié)頭部表(Section Header Table)中每一個(gè)表項(xiàng)的大小為64字節(jié)。e_shnum值為0x001f,表示節(jié)頭部表(Section Header Table)中總共有31個(gè)表項(xiàng)。e_shstrndx的值為:0x001c表示節(jié)頭部表(Section Header Table)中與節(jié)名字表相對(duì)應(yīng)的表項(xiàng)的索引。

通過(guò)文件頭找到section header table,理解其內(nèi)容

在/usr/include/elf.h中section header table的數(shù)據(jù)結(jié)構(gòu)定義如下:

typedef struct
{
  Elf32_Word    sh_name;                /* Section name (string tbl index) */
  Elf32_Word    sh_type;                /* Section type */
  Elf32_Word    sh_flags;               /* Section flags */
  Elf32_Addr    sh_addr;                /* Section virtual addr at execution */
  Elf32_Off     sh_offset;              /* Section file offset */
  Elf32_Word    sh_size;                /* Section size in bytes */
  Elf32_Word    sh_link;                /* Link to another section */
  Elf32_Word    sh_info;                /* Additional section information */
  Elf32_Word    sh_addralign;           /* Section alignment */
  Elf32_Word    sh_entsize;             /* Entry size if section holds table */
} Elf32_Shdr;

typedef struct
{
  Elf64_Word    sh_name;                /* Section name (string tbl index) */
  Elf64_Word    sh_type;                /* Section type */
  Elf64_Xword   sh_flags;               /* Section flags */
  Elf64_Addr    sh_addr;                /* Section virtual addr at execution */
  Elf64_Off     sh_offset;              /* Section file offset */
  Elf64_Xword   sh_size;                /* Section size in bytes */
  Elf64_Word    sh_link;                /* Link to another section */
  Elf64_Word    sh_info;                /* Additional section information */
  Elf64_Xword   sh_addralign;           /* Section alignment */
  Elf64_Xword   sh_entsize;             /* Entry size if section holds table */
} Elf64_Shdr;

由上述分析可知節(jié)頭部表(Section Header Table)的起始位置文件頭的偏移量為0x19d8字節(jié)。

輸入命令:

xxd hello

找到0x19d8即Section Header Table的起始點(diǎn)如下圖所示:

從起始點(diǎn)開(kāi)始的Section Header Table的數(shù)據(jù)結(jié)構(gòu)如Elf64_Shdr結(jié)構(gòu)體所示。

通過(guò)輸入命令

readelf -S hello

可以查看Section Header Table,如下圖所示:

通過(guò)section header table找到各section

由前面的分析可知節(jié)頭部表(Section Header Table)中每一個(gè)表項(xiàng)的大小為64字節(jié)。

在第一節(jié)中,內(nèi)容全部為0不表示任何段。

在第二節(jié)中,為.interp段,段偏移sh_offset為0X238(紅線),段大小sh_size為0X1c(藍(lán)線)。

在第三節(jié)中,為.note.ABI-tag節(jié),節(jié)偏移sh_offset為0X 254(紅線),節(jié)大小sh_size為0X 20(藍(lán)線)。

第四個(gè)節(jié),為.note.gnu.build-i段,節(jié)偏移sh_offset為0X 274(紅線), 節(jié)大小sh_size為0X 24(藍(lán)線)。

第五個(gè)節(jié),為.gnu.hash節(jié),節(jié)偏移sh_offset為0X 298(紅線), 節(jié)大小sh_size為0X 1c(藍(lán)線)。

............中間節(jié)省略...........

第14節(jié).text節(jié)的表項(xiàng)起始地址=0x19d8+14*64=0x1d58

第14節(jié).text節(jié)的節(jié)偏移為0x430字節(jié),節(jié)大小為0x182字節(jié)。

其他節(jié)同理可推出,其他節(jié)起始地址的表項(xiàng)起始地址=0x19d8+節(jié)序號(hào)*64。
然后通過(guò)相對(duì)文件頭的偏移地址和節(jié)大小可以找到各section。

理解常見(jiàn).text .strtab .symtab .rodata的section。

1.text section是可執(zhí)行指令的集合,.data和.text都是屬于PROGBITS類型的section,這是將來(lái)要運(yùn)行的程序與代碼。

2.strtab section是屬于STRTAB類型的section,可以在文件中看到,它存著字符串,儲(chǔ)存著符號(hào)的名字。

3.symtab section存放所有section中定義的符號(hào)名字,比如“data_items”,“start_loop”。 .symtab section是屬于SYMTAB類型的section,它描述了.strtab中的符號(hào)在“內(nèi)存”中對(duì)應(yīng)的“內(nèi)存地址”。

4.rodata section,ro代表read only,即只讀數(shù)據(jù)(const)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容