読者です 読者をやめる 読者になる 読者になる

ELF解析のはじめ

linuxの実行バイナリであるELFファイルの解析の仕方のメモ

IBMのスライドを参考にした。
http://www-06.ibm.com/jp/linux/tech/doc/attachments/002cb129_elf_v1_0.pdf

サンプルELFの作成

まず、サンプルとなるプログラムを用意する。

// func.c
#include <stdio.h>

int global_num = 0xaa00;

int func1( int i )
{
  return global_num + i;
}

int func2( int i )
{
  return global_num + i + i;
}
//main.c
#include<stdio.h>

extern int global_num;
extern int func1( int i );
extern int func2( int i );

int main( void )
{
  printf( "global_num = %04x\n", global_num );
  printf( "func1( 0x11 ) = %04x\n", func1( 0x11 ) );
  printf( "func2( 0x22 ) = %04x\n", func2( 0x22 ) );
}

gccコンパイルして実行バイナリを作る。

$ gcc -O0 -g -c -o func.o func.c
$ gcc -O0 -g -c -o main.o main.c
$ gcc -o main func.o main.o

解析に入る前に

まず、objdumpを使って逆アセンブルとデータを書き出しておく。

$ objdump -M intel -S main > main.txt
$ objdump -s main > main.data

解析

.textセクションの最初から実行されるということなのでそこを見てみる。

Disassembly of section .text:

08048320 <_start>:
 8048320:	31 ed                	xor    ebp,ebp
 8048322:	5e                   	pop    esi
 8048323:	89 e1                	mov    ecx,esp
 8048325:	83 e4 f0             	and    esp,0xfffffff0
 8048328:	50                   	push   eax
 8048329:	54                   	push   esp
 804832a:	52                   	push   edx
 804832b:	68 10 85 04 08       	push   0x8048510
 8048330:	68 a0 84 04 08       	push   0x80484a0
 8048335:	51                   	push   ecx
 8048336:	56                   	push   esi
 8048337:	68 42 84 04 08       	push   0x8048442
 804833c:	e8 cf ff ff ff       	call   8048310 <__libc_start_main@plt>
 8048341:	f4                   	hlt    
 8048342:	66 90                	xchg   ax,ax
 8048344:	66 90                	xchg   ax,ax
 8048346:	66 90                	xchg   ax,ax
 8048348:	66 90                	xchg   ax,ax
 804834a:	66 90                	xchg   ax,ax
 804834c:	66 90                	xchg   ax,ax
 804834e:	66 90                	xchg   ax,ax

0x8048510, 0x80484a0, 0x8048442というのは
それぞれ__libc_csu_fini, __libc_csu_init, mainの関数ポインタのようだ。

それらを適当にスタックに積んだ後で
__libc_start_main@pltという関数を呼び出している。
__libc_start_main@pltというのはどうなっているかというと、

08048310 <__libc_start_main@plt>:
 8048310:	ff 25 14 a0 04 08    	jmp    DWORD PTR ds:0x804a014
 8048316:	68 10 00 00 00       	push   0x10
 804831b:	e9 c0 ff ff ff       	jmp    80482e0 <_init+0x2c>

0x804a014のポインタ先に間接ジャンプしているらしい。
よくわからないのでIBMのスライドを見てみると、共有ライブラリの関数を呼ぶ場合は
実行時に関数ポインタが書き込まれるとのことだ。

gdbで実行してみる。

$ gdb main
(gdb) b *0x8048510
(gdb) b *0x80484a0
(gdb) b *0x8048442
(gdb) run
Breakpoint 2, 0x080484a0 in __libc_csu_init ()
(gdb) info stack
#0  0x080484a0 in __libc_csu_init ()
#1  0xb7e2d89a in __libc_start_main (main=0x8048442 <main>, argc=1,
      ubp_av=0xbfffebd4, init=0x80484a0, <__libc_csu_init>,
      fini=0x8048510 <__libc_csu_fini>, rtld_fini=0xb7fed5f0 <_dl_fini>,
      stack_end=0xbfffebcc) at libc-start.c:219
#2  0x08048341 in _start ()

__libc_csu_initが呼び出された。この関数が何をやっているのかはよくわからないので
次に進むとmainが呼び出されている。

main部分は簡単に読むことができた。