본문 바로가기
프로그래밍/SYSTEM HACKING

[SYSTEM HACKING] main 함수의 인자 / 시스템 프로그래밍( 시스템 콜 )

by B T Y 2017. 11. 1.
반응형

main 함수의 인자 / 시스템 프로그래밍( 시스템 콜 )에 대해서 정리한다.




1. main 함수의 인자


  #> ./a.out hello

  hello

      * 위 내용은 hello라는 인자를 받아서 그 인자의 내용인 hello를 출력하는 프로그램이다.


  - 포인터 배열 사용


  - 포인터 배열: 배열인데 주소를 원소로 하는

  - 배열 포인터: 포인터인데 배열을 나타내는



c 표준에 정의되어있는 main 함수 원형 2가지

int main( int argc, char *argv[] );

int maint();


     * main 함수를 사용할때 인자를 생략할 수 있지만 생략해도 int argc, char *argv[] 인자는 존재한다.

         ( 다만 인자를 사용하려면 명시해줘야 한다 )



#> ./a.out hello 프로그램 코드

        hello


int main( int argc, char *argv[] );

{

  printf("%s \n", argc[1]);

  return 0;

}


   * int argc 인자의 경우 argument의 갯수를 나타낸다.

      ( int argc 인자의 경우 어셈블리에서 가져오려면 [ebp+8]을 하게되면 주소를 가져올 수 있다 )


   * 실행 프로그램의 이름도 argument로 친다. ( 첫번째 인자 )


   * char *argv[]의 시작 주소를 가져올때는 [ebp+12] 위치에서 가져온다.

      ( 가변 배열이기 때문에 인자가 들어가는 경우에 주소는 변할 수 있다 )



char *argv[] -> 0xbffffbc4  // [ebp+12]



 0xbfffffbc4   0xbfffffbc8    0xbfffffbcc

[0xbfffffcb9] [0x0000000 ] [                ] ...


   * 위 상황에서 두번째 인자가 존재한다면 아래와 같이 두번째 메모리값이 채워지고 그다음 메모리가 

    널(0x00)로 채워지게 되는 상황이 될 것이다. ( 아래와 박스와 같은 상황이 된다 )


 0xbfffffbc4   0xbfffffbc8    0xbfffffbcc

[0xbfffffcb9] [0x???????? ]  [0x00000000] ...




( main 함수의 첫번째 인자인 int argc의 경우 함수가 실행되고 [ebp+8] 주소에 위치하기 때문에

어셈블리 코드로 해당 인자의 주소에 접근해 메모리 값을 가져와서 출력 해봤다.. )



( 실행 프로그램의 이름도 인자로 치기 때문에 argc의 값이 1이 나오게 된다 )



( 인자의 갯수가 늘어날 때마다 argc의 출력 값 또한 늘어나게 된다 )



( char *argv[]는 배열 포인터로 인자들의 주소를 가지고 있다..

첫번째 인자의 경우 [ebp+12]에 위치하고 있기 때문에 해당 주소에

접근하게 되면 실행 프로그램의 이름을 가져올 수 있다. )



( 첫번째 인자는 실행 프로그램의 이름이기 때문에 ./arg가 출력 결과로 나오는걸 볼 수 있다. )





[실습]

  #> ./a.out hello 프로그램 어셈블리로 작성

   - 출력 결과: hello


0xbfffffbc4: ./arg

0x???????? : hello


 0xbfffffbc4   0xbfffffbc8  0xbfffffbcc

[0xbfffffcb9] [0x???????? ] [0x0000000] ...


    * a.out 프로그램에 hello라는 두번째 인자를 주게 되면 위와 같이 두번째 인자는 메모리값으로 채워지고 3번째 인자가

     0x00000000 상태로 널값이 채워지게 된다.

       ( 가변 배열이기 때문에 인자 입력의 끝을 알리기 위해 다음 인자의 메모리값을 널값으로 채운다 )




( 실행 프로그램의 이름이 첫번째 인자이고 

프로그램 실행시 넘겨주는 인자는 두번째 인자이기 때문에 [ebp+16]의 주소에 접근해야

두번째 인자에 주소에 접근해 해당 메모리 값을 가져올 수 있다. )





시스템 프로그래밍


  - 리눅스: 시스템 콜

  - 윈도우즈: WIN API


  !! shell을 이용해서 하드웨어에 접근하려면 항상 커널을 통해서 접근해야 한다.


     * 시스템 콜은 C언어와 별개로 함수 형태로 제공된다.

     * 커널에 자원을 사용할수 있는 명령어

     * 시스템 콜은 번호를 이용해서 관리된다 ( 예를 들어서 write systemcall의 경우에는 4 )


  !! printf의 경우에도 결과적으로는내부적으로  write systemcall을 호출해서 출력하고 있다.


     * 어셈블리에서는 eax에 시스템 콜 번호를 넣어주고 첫번째 인자부터 ebx, ecx, edx에 넣어주고 3개가 넘을시에는 

       stack을 이용한다.

     * int(인터럽트) 명령어를 이용해서 시스템 콜을 호출한다.  ( 80h에서 80은 0x80 )

        ( call 명령을 이용해서는 시스템 콜을 할수 없다 )



리눅스 시스템 콜 참조 사이트

  https://syscalls.kernelgrok.com/

   * 검색 해보면 이 외에도 많은 참조 사이트가 존재한다.




( 시스템 콜을 C언어에서 사용할 때는 write system call의 경우 <unistd.h> 헤더파일을 포함시켜줘야 한다.. 

C언어의 경우 시스템 콜을 직접 실행하는게 아니라 mapping 되어있기 때문에 사용이 가능한 것이므로 

헤더파일을 포함시켜줘야 되는것 같다.. )





( 시스템 콜은 번호로 관리가 되고 있기 때문에 어셈블리에서는 시스템 콜을 사용할때 eax에 해당 되는

시스템 콜 번호를 넣어준다.. )




( 프로그램 실행시 segment default가 나는 부분은 exit system call을 이용해서 잡아줄 수 있고 

두번째 인자로는 int error_code가 들어가게 되는데 표준 에러인 0을 넣어주면 된다 )





!! 유용한 명령어


1. ltrace


  - 실행 프로그램에서 사용중인 C 라이브러리를 추적


2. strace


  - 실행 프로그램에서 사용중인 시스템 콜을 추적


     !!! 해당 명령어들만 잘 이용해도 직접 바이너리를 열어보지 않고 프로그램의 구조를 파악할 수 있다.







반응형

댓글