main 함수 이전에 일어나는 일에 대한 간단한 추적

pynoos의 이미지

이 글의 원문은 http://coolengineer.com/135 입니다. 지난 기사에 이어지는 내용입니다.


지난 기사에서 .ctor 섹션에 있는 코드들이 어떻게 실행되는지를 설명하였고, 이번에는 그 하부가

어떻게 수행되는지를 살펴볼까한다.



지난번의 코드를 다시 인용해 보자면,

$ more c.c
#ifdef STRIP_ATTR
#define __attribute__(x)
#endif
void __attribute__((constructor)) before_main( void )
{
printf("I miss you Lorthlorien ever beauty.\n");
}

void __attribute__((constructor)) before_main_2nd( void )
{
printf("Bombadil, where have you been in the morning?\n");
}

void __attribute__((destructor)) after_main( void )
{
printf("Mithlandir, help me!\n");
}

int main()
{
printf("I am working, no touch!\n");
return 0;
}

$ gcc -o c1 -save-temps c.c
$ ./c1
Bombadil, where have you been in the morning?
I miss you Lorthlorien ever beauty.
I am working, no touch!
Mithlandir, help me!



그리고 그 때 만들었던 심볼 덤프는 다음과 같다.

$ more c1.nm
w _Jv_RegisterClasses
w __deregister_frame_info_bases
w __gmon_start__
U __libc_start_main@@GLIBC_2.0
w __register_frame_info_bases
U printf@@GLIBC_2.0
08048230 T _init
08048280 T _start
080482a4 t call_gmon_start
080482a4 t gcc2_compiled.
080482d0 t __do_global_dtors_aux
08048330 t frame_dummy
08048394 T before_main
080483a8 T before_main_2nd
080483bc T after_main
080483d0 T main
08048400 t __do_global_ctors_aux
08048430 T _fini
08048430 t gcc2_compiled.
08048460 R _fp_hw
08048464 R _IO_stdin_used
08049520 D __data_start
08049520 W data_start
08049524 d __dso_handle
08049528 d p.0
0804952c r __EH_FRAME_BEGIN__
0804952c r __FRAME_END__
08049530 D _DYNAMIC
080495f8 d __CTOR_LIST__
08049604 d __CTOR_END__
08049608 d __DTOR_LIST__
08049610 d __DTOR_END__
08049614 d __JCR_END__
08049614 d __JCR_LIST__
08049618 D _GLOBAL_OFFSET_TABLE_
08049630 A __bss_start
08049630 A _edata
08049630 b completed.1
08049634 b object.2
0804964c A _end


저 코드 중에서 함수들만 추려서 gdb의 브레이크 포인트 구문으로 만들어 보자.

$ cat c1.nm | grep -i " t " | grep -v gcc2 | awk '{print "b", $NF}'
b _init
b _start
b call_gmon_start
b __do_global_dtors_aux
b frame_dummy
b before_main
b before_main_2nd
b after_main
b main
b __do_global_ctors_aux
b _fini


그리고, 클립보드에 복사한 뒤 다음 명령을 수행한다.

$ gdb c1
GNU gdb 6.1.1
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".

(gdb) b _init
Breakpoint 1 at 0x8048236
(gdb) b _start
Breakpoint 2 at 0x8048280
(gdb) b call_gmon_start
Breakpoint 3 at 0x80482b5
(gdb) b __do_global_dtors_aux
.
.
.
(gdb) b _fini
Breakpoint 11 at 0x8048441
(gdb) r
Starting program: /home/pynoos/test/crt0/c1
Breakpoint 11 at 0x4013cab0: file soinit.c, line 66.

Breakpoint 2, 0x08048280 in _start ()
(gdb) continue
Continuing.

Breakpoint 1, 0x08048236 in _init ()
(gdb) continue
Continuing.
(이하 continue 명령으로 계속...)
Breakpoint 3, 0x080482b5 in call_gmon_start ()
Breakpoint 5, 0x08048330 in frame_dummy ()
Breakpoint 10, 0x08048404 in __do_global_ctors_aux ()
Breakpoint 7, 0x080483ae in before_main_2nd ()

Bombadil, where have you been in the morning?

Breakpoint 6, 0x0804839a in before_main ()

I miss you Lorthlorien ever beauty.

Breakpoint 9, 0x080483d6 in main ()

I am working, no touch!

Breakpoint 4, 0x080482d6 in __do_global_dtors_aux ()
Breakpoint 8, 0x080483c2 in after_main ()

Mithlandir, help me!

Breakpoint 11, _fini () at soinit.c:66

(gdb) c
Continuing.

Program exited normally.
(gdb)


약간 호출 순서대로 살펴보면

_start ()
_init ()
call_gmon_start ()
frame_dummy ()
__do_global_ctors_aux ()
before_main_2nd ()
before_main ()
main ()
__do_global_dtors_aux ()
after_main ()
_fini ()


main 앞에 실행되는 것들이 상당히 많다. 실험용으로 제작한 함수를 제외하면, _start, _init, call_gmon_start, frame_dummy, __do_global_ctors_aux 가 실행된다.
그리고 대칭으로 main 다음에 __do_global_dtors_aux 가 먼저 실행되고, _fini를 끝으로 실행한다.

예상컨데, __do_global_ctors_aux가 실험용으로 제작한 main 함수 이전 함수들을, __do_global_dtors_aux가 실험용으로 제작한 main 함수 이후 함수들을 실행하는 것임을 알 수 있다.

이 놈들이 어디서 흘러왔는지 알아보자. gcc에 -v 옵션을 주어 link에 참여하는 모든 파일들을 나열한 다음 절대 경로로 주어지는 파일들만 골라서 nm 으로 속내를 들여다 보자.

$ gcc -v -o c3 c.c 2>&1 | xargs -n1 echo | grep "^/" | xargs nm -A 2>/dev/null | grep __do_global_ctors_aux
No -c option found for c.c
/usr/local/bin/cc1:083c2c70 t __do_global_ctors_aux
/usr/local/bin/as:08123410 t __do_global_ctors_aux
/usr/local/bin/collect2:080a48f0 t __do_global_ctors_aux
/usr/lib/gcc-lib/i386-redhat-linux/2.96/crtend.o:00000000 t __do_global_ctors_aux


아싸.. crtend.o 에서 발견되는 놈이 정답인거 같다. 나머지는 모두 빌드에 필요한 실행파일들이니까.

자세히 보니 gcc와 같이 딸려오는 라이브러리에 있는것 같다.
그럼 gcc 소스를 봐야하는군.

$ grep -lr __do_global_ctors_aux /usr/src/gcc-2.95.3
/usr/src/gcc-2.95.3/gcc/FSFChangeLog
/usr/src/gcc-2.95.3/gcc/FSFChangeLog.11
/usr/src/gcc-2.95.3/gcc/crtstuff.c
/usr/src/gcc-2.95.3/gcc/config/alpha/crtend.asm
/usr/src/gcc-2.95.3/gcc/tags


대층 보니까 crtstuff.c 에 있는 것 같다. 그 파일에서 인용하자면,

328 #ifdef CTOR_LIST_BEGIN
329 CTOR_LIST_BEGIN;
330 #else
331 asm (CTORS_SECTION_ASM_OP); /* cc1 doesn't know that we are switching! */
332 STATIC func_ptr __CTOR_LIST__[1] __attribute__ ((__unused__))
333 = { (func_ptr) (-1) };
334 #endif
335
336 #ifdef DTOR_LIST_BEGIN
337 DTOR_LIST_BEGIN;
338 #else
339 asm (DTORS_SECTION_ASM_OP); /* cc1 doesn't know that we are switching! */
340 STATIC func_ptr __DTOR_LIST__[1] = { (func_ptr) (-1) };
341 #endif
342
343 #ifdef EH_FRAME_SECTION_ASM_OP
344 /* Stick a label at the beginning of the frame unwind info so we can register
345 and deregister it with the exception handling library code. */
346
347 asm (EH_FRAME_SECTION_ASM_OP);
348 #ifdef INIT_SECTION_ASM_OP
349 STATIC
350 #endif
351 char __EH_FRAME_BEGIN__[] = { };
352 #endif /* EH_FRAME_SECTION_ASM_OP */
353
354 #endif /* defined(CRT_BEGIN) */
355
356 #ifdef CRT_END
357
358 #ifdef INIT_SECTION_ASM_OP
359
360 #ifdef OBJECT_FORMAT_ELF
361
362 static func_ptr __CTOR_END__[];
363 static void
364 __do_global_ctors_aux (void)
365 {
366 func_ptr *p;
367 for (p = __CTOR_END__ - 1; *p != (func_ptr) -1; p--)
368 (*p) ();
369 }
471 #ifdef CTOR_LIST_END
472 CTOR_LIST_END;
473 #else
474 asm (CTORS_SECTION_ASM_OP); /* cc1 doesn't know that we are switching! */
475 STATIC func_ptr __CTOR_END__[1] = { (func_ptr) 0 };
476 #endif



332, 333을 보면 __CTOR_LIST__ 가 가리키는 첫번째에는 -1 값이 들어가고, 그리고 362의 __CTOR_END__는 선언이며, 실제 정의는 475에 존재한다. 이들은 모두 앞부분에 있는 asm (CTORS_SECTION_ASM_OP); 에 의해 .ctor 섹션에 존재하도록 지시된다. Makefile을 잘보니 이 파일이 crtbegin.o와 crtend.o 를 만드는데 사용되며, 각각을 만들 때, __CTOR_LIST__와 __CTOR_END__가 두 파일에 나뉘어 들어가게 되어 있다.

전통적으로 링크할 때는 인자에 넘겨지는 순서대로 심볼들이 배치되므로 그 중간에 .ctor 섹션에 뭔가를 넣고 링커를 호출하면 __CTOR__LIST__에서 출발하여 __CTOR_END__에 이르는 배열을 만들 수 있게된다.

실제로 gcc -v 옵션을 넣어 확인해보면,

/usr/lib/gcc-lib/i386-redhat-linux/2.96/collect2 -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o c3 /usr/lib/gcc-lib/i386-redhat-linux/2.96/../../../crt1.o /usr/lib/gcc-lib/i386-redhat-linux/2.96/../../../crti.o /usr/lib/gcc-lib/i386-redhat-linux/2.96/crtbegin.o -L/usr/lib/gcc-lib/i386-redhat-linux/2.96 -L/usr/lib/gcc-lib/i386-redhat-linux/2.96/../../.. /tmp/ccfOmTjx.o -lgcc -lc -lgcc /usr/lib/gcc-lib/i386-redhat-linux/2.96/crtend.o /usr/lib/gcc-lib/i386-redhat-linux/2.96/../../../crtn.o


이렇게 되어 있다. 따라서 .ctor 섹션은 하나의 배열을 만들게 되며,
__do_global_ctors_aux 함수를 살펴보면
__CTOR_END__가 가리키는 윗번지에서부터, -1을 만나기 전까지 거꾸로 차례차례 함수를 호출하도록 되어 있다.

그러니 before_main_2nd 부터 수행되겠다!!, 에~~ 그런것이었구만.

댓글

Anonymou의 이미지

링커는 crt0.o (스타트업 코드) 와 main 함수를 정의한 사용자가
만든 object 파일을 묶어서 실행파일을 만듭니다.

스타트업 코드는 플랫폼마다 이름이 약간식 틀린데 대부분 crt0.o 을 공통적
(window는 아마 틀리겠죠) 사용합니다.

이 스타트업 코드가 하는 일이 해당 플랫폼에 맞게 정해져 있습니다.
언어에 따라 또는 컴파일러 제작자에 따라 이 스타트업 코드다 다르게 제공되고요.
언어의 레벨에 따라 해주는 일이 약간씩 틀리고 언어가 지원하는 사양에
따라 기본적으로 해줘야 하는 일이 틀리기 때문입니다.

purewell의 이미지

아름다운 후킹에 도움을 주셔서 감사합니다.

----------------------------------------------
언제나 맑고픈 샘이가...
http://yubink.com - 강아지 필요하세요?
http://purewell.biz - 헙!!

_____________________________
언제나 맑고픈 샘이가...
http://purewell.biz

pynoos의 이미지

도움이 되었다니 감사할 따름이죠.. purewell 님의 아이콘이 이젠 안보이는군요.. ;)

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.