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

程序破解

NOP、JNE、JE、JMP、CMP汇编指令的机器码

NOP:NOP指令即“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。(机器码:90)

JNE:条件转移指令,如果不相等则跳转。(机器码:75)

JE:条件转移指令,如果相等则跳转。(机器码:74)

JMP:无条件转移指令。段内直接短转Jmp short(机器码:EB)段内直接近转移Jmp near(机器码:E9)段内间接转移Jmp word(机器码:FF)段间直接(远)转移Jmp far(机器码:EA)

CMP:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。

反汇编与十六进制编程器

实验源码:

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");
}

运行效果如下:

输入命令:

objdump -d login

查看main函数

现在的需求是:修改可执行文件另其无论输入什么密码的是正确的。

修改思路:将jne 跳转偏移量改为0,这样40063a位置的指令就相当于继续执行输出right。

实现方法:

输入指令:

vim login

打开可执行文件

输入:

%!xxd

进入十六进制编辑模式,如下图所示:

然后输入

/750c

找到jne指令的位置,如下图所示:

将750c改成7500表示跳转到下一条指令继续执行,相当于NOP。

输入

%!xxd -r

切换回原模式

输入

wq

保存后退出。

运行修改后的文件效果如下:

由图可见无论输入什么密码都是正确的,修改成功。

如果想输入什么密码都是错误的,只需要将750c改为eb0c即可,eb为JMP,0c为输出"wrong"的代码相对偏移量。

效果如下:

如果想输入原先正确的密码为"wrong",输出其他密码为"right",只需把750c改为740c即可,74相当于JE,相等则跳转。

效果如下:

可执行文件的基本格式

Linux可执行文件格式为ELF即Executable and Linkable Format。

格式:

ELF header :ELF文件头,在文件的开始,保存了路线图,描述了该文件的组织情况。

program header table :程序文件头表,告诉系统如何创建进程映像。用来构造进程映像的目标文件必须具有程序头部表,可重定位文件不需要这个表。

.text段:存储只读程序

.data段:存储已经初始化的全局变量和静态变量

.bss段:存储未初始化的全局变量和静态变量,因为这些变量的值为0,所以这个段在文件当中不占据空间

.rodata段:存储只读数据,比如字符串常量

...(各种(节))

Section header table:节头部表,包含了描述文件节区的信息,每个节区在表中都有一项,每一项给出诸如节区名称、节区大小这类信息。用于链接的目标文件必须包含节区头部表,其他目标文件可以有,也可以没有这个表。

具体分析见ELF文件格式分析。

ELF文件格式分析

ELF全称Executable and Linkable Format,可执行连接格式,ELF格式的文件用于存储Linux程序。ELF文件(目标文件)格式主要三种:

1)可重定向文件:文件保存着代码和适当的数据,用来和其他的目标文件一起来创建一个可执行文件或者是一个共享目标文件。(目标文件或者静态库文件,即linux通常后缀为.a和.o的文件)

2)可执行文件:文件保存着一个用来执行的程序。(例如bash,gcc等)

3)共享目标文件:共享库。文件保存着代码和合适的数据,用来被下连接编辑器和动态链接器链接。(linux下后缀为.so的文件。)

ELF文件头

查看/usr/include/elf.h中的ELF头文件数据结构。

#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;

                                        

取一段简单的代码进行分析:

hello.c

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

输入指令

readelf -h hello

得到ELF文件头信息:

由图可看出ELF文件头大小为64字节。

输入命令:

hexdump -x hello -n 64

对ELF头的16进制表进行分析:

第一行对应e_ident[EI_NIDENT],实际内容为:7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 ,前四个字节7f454c46(0x45,0x4c,0x46是'e','l','f'对应的ascii编码)是一个魔数,表示这是一个ELF对象。接下来02字节表示是一个64位对象,接下来01字节表示是小端法表示,再接下来的01字节表示文件头的版本。剩下的默认设置为0。

第二行,e_type值为0x0002,表示是一个可执行文件。e_machine的值为0x003e表示目标文件所期待的系统架构为Advanced Micro Devices X86-64。e_version值为0x00000001,表示是当前版本。e_entry的值为0x00400430表示程序入口地址。

第三行,e_phoff的值为0x0040,表示程序头部表的起始位置在磁盘文件中的偏移量为64字节。e_shoff的值为0x19d8,表示节头部表(Section Header Table)的起始位置在磁盘文件中的偏移量为6616字节。

第四行,e_flags的值为e_flags值为0x00000000,表示未知处理器特定标志。e_ehsize值为0x0040表示ELF文件头部的大小为64字节。e_phentsize的值为0x0038,表示程序头部表(Program Header Table)中每一个表项的大小56字节。e_shnum值为0x0009,表示程序头部表(Program Header Table)中总共有9个表项。e_shentsize的值为0x0040,表示节头部表(Section Header Table)中每一个表项的大小为64字节。e_shnum值为0x001f,表示节头部表(Section Header Table)中总共有31个表项。e_shstrndx的值为:0x001c表示节头部表(Section Header Table)中与节名字表相对应的表项的索引。

通过文件头找到section header table,理解其内容

在/usr/include/elf.h中section header table的数据结构定义如下:

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;

由上述分析可知节头部表(Section Header Table)的起始位置文件头的偏移量为0x19d8字节。

输入命令:

xxd hello

找到0x19d8即Section Header Table的起始点如下图所示:

从起始点开始的Section Header Table的数据结构如Elf64_Shdr结构体所示。

通过输入命令

readelf -S hello

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

通过section header table找到各section

由前面的分析可知节头部表(Section Header Table)中每一个表项的大小为64字节。

在第一节中,内容全部为0不表示任何段。

在第二节中,为.interp段,段偏移sh_offset为0X238(红线),段大小sh_size为0X1c(蓝线)。

在第三节中,为.note.ABI-tag节,节偏移sh_offset为0X 254(红线),节大小sh_size为0X 20(蓝线)。

第四个节,为.note.gnu.build-i段,节偏移sh_offset为0X 274(红线), 节大小sh_size为0X 24(蓝线)。

第五个节,为.gnu.hash节,节偏移sh_offset为0X 298(红线), 节大小sh_size为0X 1c(蓝线)。

............中间节省略...........

第14节.text节的表项起始地址=0x19d8+14*64=0x1d58

第14节.text节的节偏移为0x430字节,节大小为0x182字节。

其他节同理可推出,其他节起始地址的表项起始地址=0x19d8+节序号*64。
然后通过相对文件头的偏移地址和节大小可以找到各section。

理解常见.text .strtab .symtab .rodata的section。

1.text section是可执行指令的集合,.data和.text都是属于PROGBITS类型的section,这是将来要运行的程序与代码。

2.strtab section是属于STRTAB类型的section,可以在文件中看到,它存着字符串,储存着符号的名字。

3.symtab section存放所有section中定义的符号名字,比如“data_items”,“start_loop”。 .symtab section是属于SYMTAB类型的section,它描述了.strtab中的符号在“内存”中对应的“内存地址”。

4.rodata section,ro代表read only,即只读数据(const)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容