`

小型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:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048310
  Start of program headers:          52 (bytes into file)
  Start of section headers:          1932 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         8
  Size of section headers:           40 (bytes)
  Number of section headers:         30
  Section 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 $-msg

	SECTION .text
	global main
main:
	mov edx,len
	mov ecx,msg
	mov ebx,1
	mov eax,4
	int 0x80
	mov ebx,0
	mov eax,1
	int 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"中几个选项英文注解如下(链接 ):

 

-nostartfiles
    Do not use the standard system startup files when linking. The standard system libraries are used normally, unless -nostdlib or -nodefaultlibs is used.
-nodefaultlibs
    Do 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.
-nostdlib
    Do 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)是可选项。

 

 

分享到:
评论

相关推荐

    hello world程序深入详解

    在Linux环境中,"Hello World"程序被编译成一种名为**ELF**(Executable and Linking Format)的二进制文件格式。ELF格式是一种广泛使用的可执行文件格式,用于定义如何组织和存储可执行文件、目标文件和共享库。 -...

    深入浅出HelloWorld

    在深入探讨《深入浅出HelloWorld》这一主题时,我们首先需要理解HelloWorld程序在Linux环境下的运作机制。HelloWorld程序作为编程领域的入门经典,不仅仅是一行简单的代码,它背后蕴含了操作系统、编译原理、链接...

    Hello World OpenRISC

    《OpenRISC与C语言:构建“Hello World”程序初探》 OpenRISC,全称为Open Reduced Instruction Set Computing(开放精简指令集计算),是一种开放源代码的处理器架构,旨在提供一个简单、低功耗且高效能的硬件平台...

    深入淺出 Hello World

    "Hello World"程序编译后会转化为ELF格式,这涉及到编译器、链接器等工具链(GNU Toolchain)的过程。作者指出,尽管"Hello World"代码简单,但编译后的可执行文件可能达到上百KB,这是因为其中包含了运行所需的...

    zynq_hello_world

    命令行可能类似于`arm-none-eabi-gcc -o hello_world.elf hello_world.c`。 4. **生成启动代码**:除了用户编写的代码,还需要一些初始化代码,如设置堆栈指针、初始化内存管理单元(MMU)等。这部分通常由Vivado的...

    ZYNQ 矿板EBAZ4205的例helloworld

    本文将详细解析基于ZYNQ 7010的EBAZ4205矿板上实现“Hello World”程序的过程,包括Vivado工程配置、网络接口和串口设置、SD卡启动以及通过SDK进行裸机编程。 首先,ZYNQ 7010是一款高度集成的SoC(System on Chip...

    嵌入式 实验一熟悉实验环境和Hello World

    本实验旨在带领学生进入嵌入式开发的世界,通过熟悉实验环境并编写运行“Hello World”程序,让学生能够动手实践,理解嵌入式系统开发的基础流程。 实验一的核心内容包括熟悉嵌入式开发平台以及编写和运行基础程序...

    ReadELF, ELF格式分析程序源码

    ReadELF, ELF格式分析程序源码 ReadELF, ELF格式分析程序源码 ReadELF, ELF格式分析程序源码 ReadELF, ELF格式分析程序源码 ReadELF, ELF格式分析程序源码

    hello-wcet:Patmos helloworld 包括 WCET 分析步骤

    我们首先像这样编译和链接 helloworld 程序: patmos-clang -o hello.elf hello.c 使用 Patmos 模拟器pasim执行二进制文件会做我们期望它做的事情: pasim hello.elf # (make run) Hello world. 为了使 hello...

    第7题:在Linux中下修改一个现有的elf可执行程序1

    本题目的目标是在不改变原有ELF程序基本功能的前提下,向其添加新的功能——在程序运行之初创建一个指定文件并写入"hello, world"的字符串,然后继续执行原程序。 实现这个目标主要分为以下几个步骤: 1. **创建和...

    ELF格式可执行程序的代码嵌入技术

    ### ELF格式可执行程序的代码嵌入技术 #### 摘要 本文主要探讨了Linux环境中ELF(Executable and Linkable Format)格式可执行程序的结构,并深入分析了如何将额外的代码片段嵌入到这类文件中。对于那些无法获取源...

    hello world on uClinux&& skyeye

    本节将详细介绍如何在SkyEye上配置和运行uClinux,并实现“Hello World”示例程序。 1. **下载并解压缩所需文件** - 首先需要下载`uClinux-dist-20080808.tar.bz2`和`arm-linux-tools-20061213.tar.gz`两个压缩包...

    第7题-ELF文件注入1

    本题主要涉及Linux环境下的ELF文件注入技术,要求在没有源代码的情况下,对一个已有的ELF可执行程序进行修改,使其在运行时先执行一个附加功能,即创建或打开一个文件并写入“helloworld”字符串,然后继续执行原...

    ELF解析工具 v1.7(elf格式解析工具)

    支持32位/64位elf文件自适应解析、可解析elf文件头、程序头、节头、字符表、符号表、hash表、版本定义表、版本依赖表、动态信息表等。 更多详细介绍请访问:...

    ELF文件的加载和动态链接过程

    文章的作者通过分析HelloWorld程序的加载和链接过程,以及符号的动态解析过程,对ELF文件的加载和动态链接进行了详细说明。这涉及到Linux内核空间加载ELF文件的过程,以及在程序运行过程中符号的动态解析。这些过程...

    Hellow world

    在本文中,我们将深入探讨如何在Zedboard上利用Vivado软件实现“Hello World”程序。Zedboard是一款基于Xilinx Zynq-7000 All Programmable SoC的开发板,它集成了ARM Cortex-A9双核处理器和FPGA逻辑资源,非常适合...

    一个简单程序解析ELF

    通过实践解析ELF文件,你可以更深入地了解操作系统如何加载和执行程序,以及程序在内存中的表现。 在`elf.zip`中可能包含了一些实用工具或教程,用于解析和理解ELF文件。这些资源可以作为进一步学习和研究的起点,...

    程序人生-Hello’s P2P.docx

    《程序人生——Hello's P2P》这篇文档深入剖析了编程初学者的入门程序——"Hello World"在计算机系统中的生命周期,以及它如何通过P2P(对等网络)和020(线上线下融合)的角度进行理解。文章分为四个章节,详细介绍...

    csapp大作业hello的自白——hello程序的一生

    - `hello.c`程序是一个简单的C语言程序,用于输出"Hello, world!"。 - 该程序经历了一系列步骤,包括预处理、编译、汇编和链接,最终生成可执行文件`hello`。 - **环境与工具**: - 开发环境:Ubuntu操作系统。 ...

Global site tag (gtag.js) - Google Analytics