Format String Bug (FSB)

오랜만에 글을 작성하네요.
이번에 정리할 기법은 FSB입니다.

Format String Bug (FSB)가 일어날 일은 최근에 없지만 뭐,,프로그래머의 실수로 인해 생길 수도 있으니 알아두면 나쁘지 않을 것 같습니다.
먼저 이 글을 읽기 전에 프로그래밍을 해본 적이 있고 Format String에 대해 알고 있으면 편하게 읽으실 수 있습니다.

Format String은 C프로그래밍을 해보셨다면 printf() 함수를 이용하며 포맷에 맞게 출력을 할 때 보실 수 있었을 겁니다.


printf("I'm %s\n, name);


위의 코드는 간단한 name을 출력하는 코드입니다.


Format String Attack은 역시 프로그래머의 게으른 작은 실수로 인해 발생하는 취약점입니다.



/*

case 2

*/

printf("%s", name);


/*

case 2

*/

printf(name);



일반적으로 코드를 작성할 때에는 원하는 문자열을 포맷에 맞게 변수를 넘겨주게 코드를 작성하지만,

어느 몇몇 게으른 프로그래머는 case2 로 코드를 작성을 합니다.


물론 위의 2가지의 코드는 잘못된 코드는 아닙니다.

어떻게보면 case2의 코드가 case1의 코드보다 적은 양의 코드를 작성하고 더 현명하게 보이지만,

case2의 코드로 작성하게 되면 해커들에게 프로그램의 흐름을 바꿀 수 있는 취약점을 만든다는 사실을 깨달아야됩니다.


과연 case2의 코드에서는 무엇이 잘못일까요?


case1의 코드는 printf() 함수를 사용할 경우 출력할 형식 포맷만큼 출력시킬 변수를 인자에 넘겨주게 되지만,

case2의 코드는 사용자의 입력에 따라 지시자의 수가 달라질 수 있게 되므로, 악의적인 공격자에 의해 스택의 내용이 확인될 수 있는 것이 문제입니다!


name에 '%s, %d, %n' 등의 지시자들이 있다면 그 지시자에 맞는 값들을 출력하게 되어 스택의 내용이 보여지게 되는 것입니다!


앗...? 그럼 %n 지시자는 무슨 역할을 하는 지시자일까요?


%n 지시자는 출력된 갯수를 카운트하여 그 값을 주소에 저장을 시키는 지시자입니다!

말로 해봤자 이해가 어려울 수도 있으니 코드를 디버깅하며 이해를 해보도록 하겠습니다!



#include <stdio.h>


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

{

int vlun = 100;


printf("now vlun is %d\n", vlun);

        printf("1234567890%n\n",&vlun);

printf("but, now vlun is %d\n", vlun);

return 0;

}



위의 코드는 간단한 코드이기에 이해를 하실 수 있으실 것입니다!

vlun 변수에는 100이라는 값이 들어있기에 처음 printf 출력에서는 now vlun is 100 라는 문자열이 출력이 될 것이라는 것은 알 수 있습니다.

이제 8번째 라인 코드에서부터가 중요합니다.

위에서 말 했듯이 %n 지시자는 출력된 갯수를 카운트를 하여 그 값을  주소에 저장을 시키는 지시자라고 하였습니다.

앞에 출력되는 "1234567890" 은 총 10개라는 것은 초등학생도 알 수 있습니다.

%n 지시자에 &vlun 주소가 넘겨지며 이 주소에 10이라는 값이 저장이 되게 되는 겁니다!

그렇다면 마지막 출력에서는 but, now vlun is 10 라는 문자열이 출력이 될 것이라 알 수 있습니다.

실제로 그렇게 출력이 되는지 확인을 해보도록 하겠습니다.



실제로 그렇게 출력이 되는 것을 디버깅을 통해 알 수 있었으며, %n 지시자가 무슨 역할을 하는지 디버깅을 통해 이해할 수 있었습니다!


이제 %n 지시자 하나만 알고 있다면 여러분은 fsb attack을 할 수 있습니다!

간단한 2가지 문제를 풀며 이해를 하도록 하겠습니다.


이 2가지의 문제들은 root-me.org 라는 워게임 사이트의 문제입니다.



#include <stdio.h>

#include <unistd.h>


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

        FILE *secret = fopen("/challenge/app-systeme/ch5/.passwd", "rt");

        char buffer[32];

        fgets(buffer, sizeof(buffer), secret);

        printf(argv[1]);

        fclose(secret);

        return 0;

}



지금까지의 글을 읽어보시고 이해를 하셨다면 위의 문제에서의 취약점이 어디에 존재하는지 한번에 아실 수 있으실겁니다.

printf(argv[1]); 

argv[1] 인자 넘겨주는 값을 그냥 printf 함수로 출력해주는 것을 알 수 있었습니다.

그리고 secret 값이 buffer에 들어가는 것도 볼 수 있네요 (웃음)


그렇다면 매우 많이 문자열을 출력해준다면 시크릿 값을 볼 수 있게 됩니다.



이제 보시다보면 스택 구조의 주소들 중 이상한 문자열이 중간에 껴있음을 알 수 있습니다.


리틀엔디안, 빅엔디안 잘 보시고 플래그를 뽑아내시면 됩니다!


39617044 -> 44706139


2번째 문제는 %n 지시자를 이용하는 문제입니다!!!



#include <stdio.h>

#include <stdlib.h>


int main( int argc, char ** argv )


{


int var;

int check  = 0x04030201;


char fmt[128];


if (argc <2)

exit(0);


memset( fmt, 0, sizeof(fmt) );


printf( "check at 0x%x\n", &check );

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


snprintf( fmt, sizeof(fmt), argv[1] );


if ((check != 0x04030201) && (check != 0xdeadbeef))

printf ("\nYou are on the right way !\n");


printf( "fmt=[%s]\n", fmt );

printf( "check=0x%x\n", check );


if (check==0xdeadbeef)

    {

printf("Yeah dude ! You win !\n");

      system("/bin/dash");

    }

}



이 문제의 취약점은 어디에 일어날까요?

printf( "fmt=[%s]\n", fmt );


여기서 일어날 것이라는 걸 알 수 있습니다!

이 문제는 매우 친절하게 check의 주소를 알려주고 check를 deadbeef로 값을 바꾸면 되는 걸 알 수 있습니다!! 빠빰!!!

왜 check 값을 0xdeadbeef 로 바꿔야되냐구요? (웃음)


if (check==0xdeadbeef)

{

printf("Yeah dude ! You win !\n");

system("/bin/dash");

}


바로 위의 코드때문입니다!


그렇다면 문제를 직접 풀면서 이해를 해보도록 하겠습니다.



check 의 주소는 0xbffffb78이며 저희가 넘겨준 1이 잘 출력되는 것과 check의 값은 0x4030201이라고 아주 자세하게 알려주고 있습니다.

그렇다면 이 문제를 풀기위해서는 0xbffffb78 주소를 넘겨주고 0xdeadbeef를 넘겨주면 되겠군요?!


앗 하지만 여기서 매우 크나 큰 관문이 생겼습니다.


0xbffffb78의 값은 3221224312 입니다.

int의 범위의 값을 넘어서버렸습니다..

벗어난 값을 입력하게 되면 오버플로우가 떠버릴지도 모른다구요~? (웃음 wwww


그래서 2바이트 2바이트씩 나눠서 넣어주면 됩니다.

0xbffffb78 : fe eb 00 00

0xbffffb7a : 00 00 da ed

이렇게 넣게 된다면?

0xbffffb78 : fe eb da ed


가 되게 되서 0xdeadbeef가 들어가게 되겠죠?!


먼저 우리가 원하는 주소가 스택에 있는지 찾아보기위해 무작위로 원하는 개수 만큼 "-%8x-"*?? 를 넣어줍니다.



이런! 없군요! 하지만 우리가 넘겨준 argv의 값이 출력되는 것을 볼 수 있습니다.

이걸 이용해 우리가 입력한 값으로 주소로 넘겨줄 수 있겠네요?


바로 이렇게 말이죠!



보시면 우리가 입력한 AAAA가 스택에 보이는 것을 볼 수 있어요.

이렇게 되면 우리가 입력한 값이 주소가 될 수도 있다는 것을 깨달을 수 있죠 (큰 깨달음


그렇다면 주소를 넘겨주고 0xdeadbeef를 만들면되겠네요!



익스플로잇 코드는 위와 같습니다! 

이제 알아보도록 하겠습니다.

먼저 check의 주소를 먼저 넘겨주면 스택에 들어가 가젯역할을 할 수 있게 됩니다!

그 후 dummy의 값을 주고 check의 2byte 만큼 높은 주소를 줍니다!

왜 2byte 높은 주소를 주는 이유는 위에 있습니다!

그 후 이제 %8x 로 스택의 현재 넘겨준 가젯의 위치를 잡습니다!

그 후는 계산만 남아있는데요!


먼저 0xbeef가 들어갈 예정입니다.

0xbeef의 값은 48879 입니다.

다만 앞에 있는 값들을 계산해줘야되겠죠?

0xbeef - (4 [0xbffffb48] + 4 [dummy] + 4 [0xbffffb48+2] + 8*7) = 48811 입니다.

그 뒤에는 0xdead - 0xbeef 한 값을 넘겨주면 되겠습니다!

0xdead - 0xbeef = 8126


그렇게 된다면 check의 주소인 0xbffffb48에는 0xbeef이 들어가고 0xbffffb4a에는 0xdead가 들어가 0xdeadbeef가 완성되게 되는거죠!


하지만 여기서 왜 더미가 들어가야될까요?

그 이유는 아래와 같습니다.

현재 포인터는 0xbffffb48을 잡고 값을 들어가게 됬습니다.

%n 지시자를 통해 주소는 4바이트 상승해서 AAAA 인 0x41414141을 잡고 있겠죠!

하지만 %8126c 인 %c 지시자를 사용하게 되어 4바이트 상승하여 그 다음 주소를 잡고 있게 되어 그 주소에 값이 들어가게 되는 것 입니다!


이렇게 위의 2문제를 풀며 Format String Bug(FSB) 기법을 볼 수 있었습니다.

'0x20 Security > 0x21 System' 카테고리의 다른 글

Save Frame Pointer Overwrite (SFO)  (0) 2016.07.25
Buffer Overflow (BOF)  (2) 2016.07.22
About LD_PRELOAD  (0) 2016.07.20
Use After Free (UAF)  (1) 2016.07.13
How main() is executed on linux  (2) 2016.07.04



1. 터미널 창을 간지나게 꾸며보자

2. 입장하거나 입장하려고 할 때에 글씨를 간지나게 띄어보자



이런 밋밋한 터미널 창 말고 간지나게 아름답게 예술적인 터미널을 만들어보겠습니다.


Oh-My-zsh (https://github.com/robbyrussell/oh-my-zsh) 에 접속을 하여 설치를 하세요!



만약 경로가 아래와 같이 깨진다 싶으면 폰트를 설치해주어야됩니다.



폰트 (https://github.com/powerline/fonts) 에서 zip을 받아 설치를 합니다.



이러케 설치를 한 뒤에 터미널의 폰트를 설치한 폰트로 변경해줍니다.



$ sudo apt-get install zsh

$ sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

$ chsh -s `which zsh`

$ vi ~/.zshrc


그렇다면 아래와 같이 터미널이 이뻐진 것을 볼 수 있습니다.



그리고 이제는 이런거나 이런걸 해봅시다.




저는 메이드를 좋아합니다.

글씨는 아래와 같이 하면 됩니다.


$ sudo apt-get install figlet

$ figlet "문자열"



원하는 그림 같은건 아스키 아트 같은 사이트를 이용하여 위와 같이 원하는 문자열로 뽑으시면 됩니다!


그런 문자열을 이제 이 파일들에 넣으면 됩니다.


 설정파일

 설명

 상태

 /etc/issue

 로컬에서 접속 시도시 보여줄 메세지 파일

 로그인 전

 /etc/issue.net

 원격지에서 접속 시도시 보여줄 메세지 파일

 로그인 전

 /etc/motd

 로컬, 원격 모두에서 로그인 성공 후에 보여줄 메세지 파일

 로그인 후


먼저 위의 설정 파일을 사용하기 위해서는 sshd_config를 변경할 필요가 있습니다.


$ vi /etc/ssh/sshd_config 



:/PrintMotd



PrintMotd no 에서 yes로 변경해줍니다.



:/Banner



주석 처리되있는 것을 지워줍니다.



그렇다면 이제 설정 파일은 다 했습니다.


이제 원하는 문자열을 원하는 파일에 집으시면 됩니다.



/etc/issue.net or /etc/issue



/etc/motd


작성을 다 하셨으면 재시작을 해줍니다.


$ /etc/init.d/ssh restart




'0x20 Security > 0x26 Server' 카테고리의 다른 글

ssh config 파일을 설정해보자!  (62) 2017.02.26
Run ARM ELF for qemu in ubuntu  (0) 2016.08.31
Install mongodb  (0) 2016.07.26

$ sudo apt-get install gcc-arm-linux-gnueabi qemu -y


arm compiler와 qemu 설치합니다.


arm-linux-gnueabi-gcc --version; qemu-system-arm --version



설치&버전 체크합시다.


arm-linux-gnueabi-gcc -staic ./a.c -o a


arm-gcc를 이용하여 컴파일합니다.



제대로 컴파일이 ARM로 되었음을 확인합니다.


qemu-arm ./a 로 실행합니다.


당연히 ./a 로 하면 arm이므로 실행이 되지 않습니다.



잘 되는 것을 볼 수 있습니다.

'0x20 Security > 0x26 Server' 카테고리의 다른 글

ssh config 파일을 설정해보자!  (62) 2017.02.26
서버를 간지나게 꾸며보자  (0) 2016.10.14
Install mongodb  (0) 2016.07.26

https://www.mongodb.com/download-center#community


$ wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-amazon-3.2.8.tgz



$ tar -zxvf ./mongodb-linux-x86_64-amazon-3.2.8.tgz



$ mkdir /opt/nosql

$ mv mongodb-linux-x86_64-amazon-3.2.8 /opt/nosql/mongodb

$ cd /opt/nosql/mongodb

$ mkdir data

$ mkdir config

$ mkdir log


$ vi mongodb.conf



dbpath=/opt/nosql/mongodb

logpath=/opt/nosql/mongodb/log/mongodb.log

logappend=true

port=5050

verbose=true

fork=true

rest=true



$ cd ./bin

$ ./mongod --config /opt/nosql/mongodb/config/mongodb.conf



$ /opt/nosql/mongodb/bin/mongo localhost:2885


MongoDB shell version: 3.2.8

connecting to: localhost:2885/test

Welcome to the MongoDB shell.

For interactive help, type "help".

For more comprehensive documentation, see

http://docs.mongodb.org/

Questions? Try the support group

http://groups.google.com/group/mongodb-user

Server has startup warnings: 

2016-07-26T08:25:14.110-0400 I CONTROL  [main] ** WARNING: --rest is specified without --httpinterface,

2016-07-26T08:25:14.110-0400 I CONTROL  [main] **          enabling http interface

2016-07-26T08:25:14.146-0400 I CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.

2016-07-26T08:25:14.146-0400 I CONTROL  [initandlisten] 

2016-07-26T08:25:14.147-0400 I CONTROL  [initandlisten] 

2016-07-26T08:25:14.147-0400 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.

2016-07-26T08:25:14.147-0400 I CONTROL  [initandlisten] **        We suggest setting it to 'never'

2016-07-26T08:25:14.147-0400 I CONTROL  [initandlisten] 

2016-07-26T08:25:14.147-0400 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.

2016-07-26T08:25:14.147-0400 I CONTROL  [initandlisten] **        We suggest setting it to 'never'

2016-07-26T08:25:14.147-0400 I CONTROL  [initandlisten] 


끝,

'0x20 Security > 0x26 Server' 카테고리의 다른 글

ssh config 파일을 설정해보자!  (62) 2017.02.26
서버를 간지나게 꾸며보자  (0) 2016.10.14
Run ARM ELF for qemu in ubuntu  (0) 2016.08.31

Save Frame Pointer Overwrite (SFO)

이번에 작성할 기법은 SFO 입니다.
Buffer Overflow(BOF) 가 일어나는 프로그램 취약점을 이용하여 Save Frame Pointer를 주작질을 하여 원하는 주소로 이동시키게 하는 기법입니다.
이 글을 일으시려면 BOF에 대해 알아두셔야 됩니다.

Save Frame Pointer Overwrite (SFO)는 1byte만 Overflow되는 상황에서 사용되는 기법으로, 

save frame pointer(sfp) 1byte를 해커가 주작질함으로서 상위 실행 권한을 얻을 수 있지만,

main 함수에서는 save frame pointer(sfp)를 주작질을 하여도 리턴 주소가 주작되지 않습니다.



Buffer Overflow(BOF) 글에서 SFP에 대해 조금 설명을 짤막하게 적어두고 GDB를 보며 깨달음을 얻도록 하겠습니다.


Save Frame Pointer(SFP)는 이전 함수의 ebp를 저장해 두어 함수의 실행이 끝나고 이전 함수로 돌아가기 위해 저장해두는 공간입니다.

이제 그렇다면 GDB로 직접 보아가면서 깨달음을 얻도록 하겠습니다!


깨달음에 GDB를 박으면 꼼짝모테~!


Open Program in GDB!

먼저 취약점이 존재하는 프로그램을 GDB에서 열어보겠습니다.
아래는 그 프로그램의 코드입니다.


/*
        The Lord of the BOF : The Fellowship of the BOF
        - darkknight
        - FPO
*/

#include <stdio.h>
#include <stdlib.h>

void problem_child(char *src)
{
char buffer[40];
strncpy(buffer, src, 41);
printf("%s\n", buffer);
}

main(int argc, char *argv[])
{
if(argc<2){
printf("argv error\n");
exit(0);
}

problem_child(argv[1]);
}


네, 코드를 어디서 많이 보신 것 같다구요?

넵, LOB의 golem의 소스코드입니다.


딱 보아도 취약점이 존재하다는 것을 발견할 수 있었습니다.

problem_child에 인자를 넘겨주는데,

problem_child 함수안에는 buffer는 40 byte 버퍼를 생성하고,

ctrncpy 함수로 buffer에 받은 인자를 쑤셔 넣지만 41byte 만큼 복사합니다.


buffer 는 40byte만큼 생성됬는데 말이죠 (웃음)

그렇다면 저희는 sfp 1byte를 주작질이 가능하다는 것을 알 수 있습니다.



그렇다면 problem_child에 브포를 설정해놓고 41만큼 넘겨주도록 하겠습니다.



넵, 걸린 걸 볼 수 있습니다.



strncpy 함수 뒤에 걸어주고 버퍼의 값을 확인해보았습니다.



40개의 A와 B가 잘 들어간 것을 볼 수 있습니다.

고로 여기서 볼 수 있는 것은 SFP는 0xbffffa62라는 것을 볼 수 있습니다.


이제 이 SFP를 어떻게 이용하는 지는 바로 이 녀석들이 중요합니다.

leave, ret !


main에서 leave ret이 실행되면서 조작된 SFP가 사용이 되는 것인데요.

이 leave ret은 BOF 글에서 설명을 했었으므로 넘어가겠습니다.


일단 그렇다면 main의 leave에 브포를 설정해주었습니다.



ebp를 확인을 해보면 저희가 주작질을 한 0xbffffa62 임을 확인할 수 있습니다.

그리고 여기서 leave가 실행을 하게 되면.

mov esp, ebp

pop ebp 이므로


ebp 값을 esp에 옮기고,

pop ebp을 하여 ebp에 저장을 하게 됩니다.

다만, pop ebp를 하면서 +4 만큼 증가하게 됩니다.



leave를 수행하고 난 뒤의 레지스터의 상태입니다.

esp에는 0xbffffa62 + 4 값이 되어있는 것을 볼 수 있습니다.


이제 ret을 수행하게 되면,

pop eip

jmp eip


즉, 스택에 쌓여있는 값을 eip에 넣고 해당 eip로 이동을 하게 됩니다.



즉, 0x88c04002이 eip에 저장이 되고, 해당 주소로 이동한다는 것을 알 수 있습니다.



이러한 과정을 통해서 Save Frame Pointer Overwirte(SFO) 기법을 볼 수 있었습니다.

'0x20 Security > 0x21 System' 카테고리의 다른 글

Format String Bug (FSB)  (0) 2016.12.01
Buffer Overflow (BOF)  (2) 2016.07.22
About LD_PRELOAD  (0) 2016.07.20
Use After Free (UAF)  (1) 2016.07.13
How main() is executed on linux  (2) 2016.07.04

먼저 적기에 앞서 winnt.h 헤더 파일을 올려놓겠습니다.


PE File Format

이번에 정리할 것은 윈도우 리버싱에서 알아두어야할 PE 파일이다.
PE는 Portable Excutable)의 줄임말이며,
윈도우에서 사용되는 실행 가능한 파일 형식입니다.
PE 파일은 EXE 파일을 말하는 것이 아니라 SCR, DLL, OCX, SYS, OBJ 들도 포함이 됩니다.
PE 구조를 이해는 API 후킹, 압축 실행 등과 같은 고-오-급 리버싱 기법들의 기본 바탕이 되기도 합니다.
고로 리버싱을 하겠다 하면 이 PE 구조에 대해 빡삭한 지식을 가지고 있어야됩니다.

Preview


어느 실행파일의 PE 구조입니다.

이 PE 헤더에는 이 실행파일을 실행하기 위한 여러가지 정보가 담겨있으며,

PE의 내용을 가지고 DLL을 로드하거나, 리소스를 할당하고 그 외에도 많은 정보들이 담겨있습니다.

이렇게 중요한 정보들이 담겨있는 만큼 PE파일이 잘못된 데이터로 누락하게 되면 실행파일은 실행이 안될 수도 있습니다.


Basic PE File

PE 구조에 대해 알아보도록 하겠습니다.

먼저 이미지를 첨부하고... (쮸글쮸글)
아, 참고로 구글에 검색하고 괜찮은 이미지 긁어왔습니다.

[ 출처 : rednooby.tistory.com ]


PE 구조는 차례대로 DOS Header, DOS Stub, NT Header(File Header, Optional Header), Section Header로 나뉘며,

그 뒤로는 코드를 포함하는 (.text) Section, 

(전역, 정적) 변수를 포함하고 있는 (.data) Section, 

문자열이나 리소스 데이터를 포함하는 (.rsrc) Section이 등장합니다.

그리고 이러한 섹션들 끝에는 NULL padding 이라는 것이 존재합니다.


VA & RVA

섹션마다 
가상메모리에서 해당 섹션을 차지하는 크기(VirtualSize), 
가상 메모리 오프셋(VirtualOffset), 
파일에서 해당 섹션이 차지하는 크기(RawSize), 
파일 오프셋(RawOffset) 들이 있습니다.

가상 메모리의 주소(Virtual Address)

응용 프로그램의 가상 메모리의 절대 주소를 의미합니다.
즉, 이 VA는 로딩되었을 때의 가상 메모리에서의 절대주소입니다.

ImageBase에서부터의 상대주소(Relative Virtual Address)

응용 프로그램의 가상 메모리에서의 상대주소를 의미합니다.
상대주소라고 하면 ImageBase의 기준으로부터 상대주소를 의미합니다.

RVA와 VA의 관계식(?)

RVA + ImageBase = VA

RVA와 RAW의 비례식

RAW - PointerToRawData = RVA - Virtual Address
RAW = RVA - Virtual Address + PointerToRawData

이 비례식에 존재하는 VirtualAddress는 우리가 지금껏 말하고 있던 VA(Virtual Address)가 아닙니다.
VA는 프로그램의 메모리에서의 절대주소라고 적었습니다.
위에 있는 RAW를 구하기 위한 식에서 나오는 VA(Virtual Address)는 Section Header의 멤버인 Virtual Address를 의미합니다. 
Section Header 구조체의 멤버인 VirtualAddress 멤버는 메모리에서 섹션의 시작 주소(RVA)를 의미합니다. 
결론적으로 RAW 비례식에서의 VirtualAddress는 우리가 앞에서 계속 얘기하던 RVA (프로세스 메모리에서의 상대주소)를 말하는 것입니다. 

[ 주황색 말 출처 : 리버싱 핵심원리 ] 

PE 헤더 (Portable Executable Header)

이 헤더라는 것들은 여러가지 필드로 이루어진 하나의 구조체라고 보시면 됩니다.
PE에는 처음에 봤듯이 여러가지 헤더들이 앞에 자리잡고 있으므로 이 PE는 많은 구조체 덩어리라고 말 할 수 있습니다.


먼저 이 헤더들(IMAGE_DOS_HEADER, MS-DOS Stub Program, IMGAGE_NT_HEADER)을 차례대로 살펴보도록하겠습니다.


IMAGE_DOS_HEADER

가장 처음으로 등장하는 IMAGE_DOS_HEADER 입니다.
아래의 코드는 winnt.h 헤더에 있는 도스 헤더의 구조체입니다.


#define IMAGE_DOS_SIGNATURE                 0x5A4D      // MZ

typedef struct _IMAGE_DOS_HEADER {
    WORD  e_magic;      /* 00: MZ Header signature */
    WORD  e_cblp;       /* 02: Bytes on last page of file */
    WORD  e_cp;         /* 04: Pages in file */
    WORD  e_crlc;       /* 06: Relocations */
    WORD  e_cparhdr;    /* 08: Size of header in paragraphs */
    WORD  e_minalloc;   /* 0a: Minimum extra paragraphs needed */
    WORD  e_maxalloc;   /* 0c: Maximum extra paragraphs needed */
    WORD  e_ss;         /* 0e: Initial (relative) SS value */
    WORD  e_sp;         /* 10: Initial SP value */
    WORD  e_csum;       /* 12: Checksum */
    WORD  e_ip;         /* 14: Initial IP value */
    WORD  e_cs;         /* 16: Initial (relative) CS value */
    WORD  e_lfarlc;     /* 18: File address of relocation table */
    WORD  e_ovno;       /* 1a: Overlay number */
    WORD  e_res[4];     /* 1c: Reserved words */
    WORD  e_oemid;      /* 24: OEM identifier (for e_oeminfo) */
    WORD  e_oeminfo;    /* 26: OEM information; e_oemid specific */
    WORD  e_res2[10];   /* 28: Reserved words */
    DWORD e_lfanew;     /* 3c: Offset to extended header */
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;


위 구조체에서는 데이터 타입이 WORD가 16개, WORD 배열이 2개, DWORD가 1개가 존재합니다.
이 헤더에서는 2개의 필더만 유심히 보시면 됩니다.
e_magic, e_lfanew 2개 입니다.


e_magic 

e_magic 필드는 PE 파일이 맞는지 아닌지 체크할 때 사용되며,
처음부터 2byte를 보면 MZ(4D 5A)인 IMAGE_DOS_SIGNATURE로 시작되는 부분이 emagic이 차지하는 공간입니다.

e_lfanew

e_lfanew 필드는 IMAGE_NT_HEADER의 시작 오프셋을 가지고 있으며, 
이 값은 고정되어 있는 값이 아니라 파일에 따라 값이 변경됩니다.
즉 PE 헤더(IMAGE_NT_HEADER)의 주소는 IMAGE_DOS_HEADER의 e_lfanew 필드를 참조하여 알아낼 수 있다는 것입니다.
위에 있는 이 프로그램의 e_lfanew 필드의 값은 0x000000F0이라는 것을 알 수 있습니다.

DOS Stub

스텁 코드(Stub Code)


위의 영역이 DOS Stub 영역입니다.
저 영역을 자세히 보시면 "This program cannot be run in DOS mode" 라는 문자열을 볼 수 있으며,
도스 모드에서 이 파일이 실행되는 것을 막기 위한 것 입니다.
그냥 이 영역은 그다지 신경을 안써도 되는 영역이라고 생각합니다. 

IMAGE_NT_HEADER

이번에는 IMAGE_DOS_HEADER에 이어서 IMAGE_NT_HEADER를 알아보겠습니다.
아래의 코드는 winnt.h 헤더에 있는 NT 헤더의 구조체입니다.


#define IMAGE_NT_SIGNATURE                  0x00004550  // PE00

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;



먼저 적기 전에 IMAGE_DOS_HEADER의 e_lfanew 필드의 값이 IMAGE_NT_HEADER의 오프셋이 맞음을 알 수 있었습니다.


Signature

IMAGE_NT_HEADER의 필드를 살펴보시면 Signature 필드가 가장 처음으로 등장합니다.
데이터 타입이 DWORD이기 때문에 4byte를 차지하며, 이 Signature의 값을 가지고 PE 파일 구조인지 아닌지를 체크를 할 수 있습니다.
Signature의 값은 IMAGE_NT_SIGNATURE 상수 그대로 PE 00(50 45 00 00) 라는 값을 가지고 있습니다.

그리고 Signature 말고도 FileHeader와 OptionalHeader필드가 있는데,
이는 IMAGE_FILE_HEADER, IMAGE_OPTIONAL_HEADER32 구조체를 가지고 있습니다.
이들을 알아보도록 하겠습니다.

IMAGE_FILE_HEADER

이번에는 IMAGE_NT_HEADER에 이어서 IMAGE_FILE_HEADER를 알아보겠습니다.
아래의 코드는 winnt.h 헤더에 있는 FILE 헤더의 구조체입니다.


typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD    NumberOfSections;
    DWORD   TimeDateStamp;
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;
    WORD    Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;


IMAGE_NT_HEADER보다 필드가 많은 것을 볼 수 있습니다.
차례대로 알아보도록 하겠습니다.

Machine

Machine 필드는 이 파일이 어떤 CPU에서 실행될 수 있는지를 알 수 있습니다.
아래는 winnt.h 헤더에 정의되있는 Machine 상수입니다.


#define IMAGE_FILE_MACHINE_UNKNOWN           0
#define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.
#define IMAGE_FILE_MACHINE_R3000             0x0162  // MIPS little-endian, 0x160 big-endian
#define IMAGE_FILE_MACHINE_R4000             0x0166  // MIPS little-endian
#define IMAGE_FILE_MACHINE_R10000            0x0168  // MIPS little-endian
#define IMAGE_FILE_MACHINE_WCEMIPSV2         0x0169  // MIPS little-endian WCE v2
#define IMAGE_FILE_MACHINE_ALPHA             0x0184  // Alpha_AXP
#define IMAGE_FILE_MACHINE_SH3               0x01a2  // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3DSP            0x01a3
#define IMAGE_FILE_MACHINE_SH3E              0x01a4  // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4               0x01a6  // SH4 little-endian
#define IMAGE_FILE_MACHINE_SH5               0x01a8  // SH5
#define IMAGE_FILE_MACHINE_ARM               0x01c0  // ARM Little-Endian
#define IMAGE_FILE_MACHINE_THUMB             0x01c2
#define IMAGE_FILE_MACHINE_AM33              0x01d3
#define IMAGE_FILE_MACHINE_POWERPC           0x01F0  // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_POWERPCFP         0x01f1
#define IMAGE_FILE_MACHINE_IA64              0x0200  // Intel 64
#define IMAGE_FILE_MACHINE_MIPS16            0x0266  // MIPS
#define IMAGE_FILE_MACHINE_ALPHA64           0x0284  // ALPHA64
#define IMAGE_FILE_MACHINE_MIPSFPU           0x0366  // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16         0x0466  // MIPS
#define IMAGE_FILE_MACHINE_AXP64             IMAGE_FILE_MACHINE_ALPHA64
#define IMAGE_FILE_MACHINE_TRICORE           0x0520  // Infineon
#define IMAGE_FILE_MACHINE_CEF               0x0CEF
#define IMAGE_FILE_MACHINE_EBC               0x0EBC  // EFI Byte Code
#define IMAGE_FILE_MACHINE_AMD64             0x8664  // AMD64 (K8)
#define IMAGE_FILE_MACHINE_M32R              0x9041  // M32R little-endian
#define IMAGE_FILE_MACHINE_CEE               0xC0EE


이 프로그램에 Machine 필드의 값을 확인해보도록 하겠습니다.


4C 01로 되있으며,

#define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.
값과 일치합니다.
그렇다면 intel x86 CPU와 호환한다는 소리가 됩니다.

NumberOfSections

이 필드는 PE 파일을 구성하는 섹션의 수를 나타냅니다.
섹션이 추가되면 이 값은 증가하고 섹션이 없어지면 감소합니다.
이 값을 보고 섹션의 개수를 알아낼 수 있으며, 무조건 0보다는 커야됩니다.
그렇다면 직접 NumberOfSections의 값을 보도록 하겠습니다.


섹션이 9개가 있다고 합니다.



실제로 9개가 존재하는 것을 볼 수 있었습니다.

TimeDateStamp

TimeDateStamp 필드는 이 PE파일이 생성된 시간,
즉 이 파일이 생성된 날짜가 타임스탬프 형식으로 기록이 됩니다.
그러나 이는 확실히 신뢰할 수 없는 값이며, 언제든지 변조가 가능하므로 "아 그렇구나~" 로 보셔야 됩니다.
TimeDateStamp 필드의 값을 확인하도록 하겠습니다.


TimeDateStamp의 값은 0x578F684B로 알수 있으며,

0x578F684B(16)를 10진수로 바꾸면 1469016139(10) 입니다.


http://www.epochconverter.com/
의 사이트에서 표준 시간으로 바꾸게 되면 

실제 파일 시간과 동일함을 볼 수 있습니다.



PointerToSymbolTable

PointerToSymbolTable 필드는 파일의 심볼 내용을 담고 있는 테이블의 오프셋을 저장하고 있고, 없으면 0을 담고 있습니다.


NumberOfSymbols

NumberOfSymbols 필드는 심볼 테이블에 저장된 심볼의 개수를 저장하고 있고, 없으면 0을 담고 있습니다.


SizeOfOptionalHeader

SizeOfOptionalHeader 필드는 IMAGE_OPTIONAL_HEADER 필드에 있는 OPTIONAL 헤더(IMAGE_OPTIONAL_HEADER32)의 크기를 담고 있습니다.

이 필드의 크기는 정해져있는 것 같고 운영체제에 따라 값이 다르기 때문에 

PE로더는 이 필드의 값을 확인하고 IMAGE_OPTIONAL_HEADER의 크기를 처리합니다.



위 그림에서 SizeOfOptionalHeader 필드의 값을 확인할 수 있으며,

값은 0xE0(16)으로 224(10) byte 만큼 차지한다는 것을 알 수 있습니다.


Characteristics

Characteristics 필드는 현재 파일의 형식을 알려주는 역할을 하며,
이 필드의 값을 가지고 실행 가능한 파일인지,
DLL 파일인지, 시스템 파일인지 등의 정보가 들어있습니다.
아래는 winnt.h에 정의된 Characteristics 상수입니다.


#define IMAGE_FILE_RELOCS_STRIPPED           0x0001  // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002  // File is executable  (i.e. no unresolved externel references).
#define IMAGE_FILE_LINE_NUMS_STRIPPED        0x0004  // Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED       0x0008  // Local symbols stripped from file.
#define IMAGE_FILE_AGGRESIVE_WS_TRIM         0x0010  // Agressively trim working set
#define IMAGE_FILE_LARGE_ADDRESS_AWARE       0x0020  // App can handle >2gb addresses
#define IMAGE_FILE_BYTES_REVERSED_LO         0x0080  // Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE             0x0100  // 32 bit word machine.
#define IMAGE_FILE_DEBUG_STRIPPED            0x0200  // Debugging info stripped from file in .DBG file
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP   0x0400  // If Image is on removable media, copy and run from the swap file.
#define IMAGE_FILE_NET_RUN_FROM_SWAP         0x0800  // If Image is on Net, copy and run from the swap file.
#define IMAGE_FILE_SYSTEM                    0x1000  // System File.
#define IMAGE_FILE_DLL                       0x2000  // File is a DLL.
#define IMAGE_FILE_UP_SYSTEM_ONLY            0x4000  // File should only be run on a UP machine
#define IMAGE_FILE_BYTES_REVERSED_HI         0x8000  // Bytes of machine word are reversed.


위 상수의 값은 비트 플래그를 사용한 것으로, 2진수 형식으로 증가합니다.


Characteristics 필드의 값이 0x0102라는 것을 볼 수 있으며, 이는 0100과 0002를 합한 값이랑 같습니다.

#define IMAGE_FILE_32BIT_MACHINE             0x0100  // 32 bit word machine.

#define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002  // File is executable  (i.e. no unresolved externel references).


즉, 32비트 머신을 필요로 하고, 실행 가능한 파일이라는 것을 알 수 있습니다.


IMAGE_OPTIONAL_HEADER

이번에는 IMAGE_FILE_HEADER에 이어서 IMAGE_OPTIONAL_HEADER를 알아보겠습니다.
아래의 코드는 winnt.h 헤더에 있는 OPTIONAL 헤더의 구조체입니다.


typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

//
// Optional header format.
//

typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //

    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;

    //
    // NT additional fields.
    //

    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;


IMAGE_OPTIONAL_HEADER 구조체는 PE구조체 중에 가장 크기가 큰 구조체로서, 필드가 상당히 많은 것을 볼 수 있습니다.
이 구조체에서 주목해야할 필드만 쏙쏙 간략하게 보도록 하겠습니다.

Magic

이 필드는 IMAGE_OPTIONAL_HEADER32일 경우에는 0x0B01, IMAGE_OPTIONAL_HEADER64일 경우에는 0x0B02 값을 가지게 됩니다.


위 그림에서 Magic 필드의 값이 0x0B01 값을 가지고 있는 것을 볼 수 있으며,
이는 IMAGE_OPTIONAL_HEADER32 구조체임을 알 수 있습니다.

AddressOfEntryPoint

EP(Entry Point)의 RVA(Relative Virtual Address)의 값을 가지고 있습니다.


ImageBase

PE 파일이 메모리에 로드될 때의 주소를 가르킵니다.
32비트일 경우 가상 메모리의 범위는 0 ~ FFFFFFFF 범위입니다.
ImageBase는 이런 광범위한 메모리 내에서 PE 파일이 로딩되는 시작 주소를 가지고 있습니다.

EXE, DLL 파일은 user memory 영역인 0 ~ FFFFFFF7 범위에 위치하고,
SYS 파일은 kernel memory 영역인 00000008 ~ FFFFFFFF 범위에 위치합니다.

보통은 실행 파일들의 ImageBase는 0x00400000, DLL 파일의 ImageBase는 0x01000000입니다. ( 물론 무조건 이 값을 가지고 있는 것은 아닙니다. )


위의 사진에서 ImageBase의 값은 0x00040000임을 알 수 있습니다.


SectionAlignment & FileAlignment

SectionAlignment는 메모리에서의 섹션의 최소 단위츨 나타내고, FileAlignment는 파일에서의 섹션의 최소 단위를 나타냅니다.
따라서 파일/메모리의 섹션 크기는 반드시 각각 SectionAlignment, FileAlignment 들의 배수가 되어야합니다.


초랭이 : SecionAlignment

주랭이 : FileAlignment


SizeOfHeader

PE 헤더의 전체 크기를 나타냅니다.
파일 시작점에서 SizeOfHeaders Offset만큼 떨어진 위치에 첫번째 섹션이 존재합니다.  


위 사진에서 SizeOfHeader 값은 0x0001F000(16)으로, 헤더의 총 크기는 126976(10)입니다.


Subsystem

이 값을 통해 시스템 드라이버 파일인지, GUI인지 CUI인지 확인이 가능합니다.
아래의 코드는 winnt.h 헤더에 있는 SUBSYSTEM의 정의된 상수입니다.


// Subsystem Values

#define IMAGE_SUBSYSTEM_UNKNOWN              0   // Unknown subsystem.
#define IMAGE_SUBSYSTEM_NATIVE               1   // Image doesn't require a subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_GUI          2   // Image runs in the Windows GUI subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_CUI          3   // Image runs in the Windows character subsystem.
#define IMAGE_SUBSYSTEM_OS2_CUI              5   // image runs in the OS/2 character subsystem.
#define IMAGE_SUBSYSTEM_POSIX_CUI            7   // image runs in the Posix character subsystem.
#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS       8   // image is a native Win9x driver.
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI       9   // Image runs in the Windows CE subsystem.
#define IMAGE_SUBSYSTEM_EFI_APPLICATION      10  //
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER  11   //
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER   12  //
#define IMAGE_SUBSYSTEM_EFI_ROM              13
#define IMAGE_SUBSYSTEM_XBOX                 14
#define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16


직접 보게 되면.


위 사진에서는 Subsystem의 값이 0x0003으로 CUI 파일이라는 것을 알 수 있습니다.


NumberOfRvaAndSizes

마지막 필드인 DataDirectory 배열의 갯수를 가지고 있습니다.
PE 로더는 NumberOfRvaAndSizes의 값을 보고 배열의 크기를 인식합니다.


DataDirectory

IMAGE_DATA_DIRECTORY 구조체를 보면 Virtual Address와 Size라는 필드가 존재합니다.
아래의 코드는 winnt.h 헤더에 있는 DataDirectory의 정의된 상수입니다.


// Directory Entries

#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
//      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7   // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor


IMAGE_DATA_DIRECTORY 구조체의 Virtual Address를 통해 가상 주소를 알 수 있으며, Size를 통해 크기를 알 수 있습니다.

#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory

#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table

앗, 그렇다면 잠깐 IAT에 대해 알아보기 전에 IMAGE_SECTION_HEADER 먼저 알아보도록 하겠습니다.

이유는 IMAGE_IMPORT_DESCRIPTOR의 주소를 알아내기 위해서는 

RAW 비례식을 이용하여 RVA - IMAGE_SECTION_HEADER의 필드인 Virtual Address를 구하여야됩니다.


IMAGE_SECTION_HEADER

이 구조체는 섹션 테이블이라고도 불리며, 섹션에 대한 정보를 관리하는 구조체라고도 합니다.
이 구조체를 보고 ".text" 섹션, ".code" 섹션, ".rdata" 섹션 등의 대한 정보들을 알 수 있다는 겁니다.
아래의 코드는 winnt.h 헤더에 있는 IMAGE_SECTION_HEADER 구조체입니다.


#define IMAGE_SIZEOF_SHORT_NAME              8

typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
    union {
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;
    } Misc;
    DWORD   VirtualAddress;
    DWORD   SizeOfRawData;
    DWORD   PointerToRawData;
    DWORD   PointerToRelocations;
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;


위의 코드를 보면 필드는 총 11개가 있습니다.

이 중에 중요한 필드들만 간략하게 보도록 하겠습니다.


Name

필드 명 그대로 섹션의 이름을 나타냅니다.
#define IMAGE_SIZEOF_SHORT_NAME              8
이 상수의 값만큼 이름의 길이가 가능합니다.
말 그대로 최대 8byte까지 가능합니다.
이 필드는 NULL로 비워있을 수도 있으며, 8byte 모두가 차있을 수도 있습니다.

VirtualSize

PE 로더를 통해 PE 파일이 메모리에 로드되고 나서의 메모리에서 섹션이 차지하는 크기를 가지고 있습니다.

VirtualAddress

PE 로드를 통해 PE 파일이 메모리에 로드되고 나서의 해당하는 섹션의 RVA 값입니다.

SizeOfRawData

파일 상에서의 해당 섹션이 차지하는 크기를 가집니다.

PointerToRawData

파일 상에서의 해당 섹션이 시작하는 위치를 담고 있습니다.

Characteristics

섹션의 속성 정보를 플래그로 지닙니다.
아래는 winnt.h에 정의된 Characteristics 상수입니다.


//
// Section characteristics.
//
//      IMAGE_SCN_TYPE_REG                   0x00000000  // Reserved.
//      IMAGE_SCN_TYPE_DSECT                 0x00000001  // Reserved.
//      IMAGE_SCN_TYPE_NOLOAD                0x00000002  // Reserved.
//      IMAGE_SCN_TYPE_GROUP                 0x00000004  // Reserved.
#define IMAGE_SCN_TYPE_NO_PAD                0x00000008  // Reserved.
//      IMAGE_SCN_TYPE_COPY                  0x00000010  // Reserved.

#define IMAGE_SCN_CNT_CODE                   0x00000020  // Section contains code.
#define IMAGE_SCN_CNT_INITIALIZED_DATA       0x00000040  // Section contains initialized data.
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA     0x00000080  // Section contains uninitialized data.

#define IMAGE_SCN_LNK_OTHER                  0x00000100  // Reserved.
#define IMAGE_SCN_LNK_INFO                   0x00000200  // Section contains comments or some other type of information.
//      IMAGE_SCN_TYPE_OVER                  0x00000400  // Reserved.
#define IMAGE_SCN_LNK_REMOVE                 0x00000800  // Section contents will not become part of image.
#define IMAGE_SCN_LNK_COMDAT                 0x00001000  // Section contents comdat.
//                                           0x00002000  // Reserved.
//      IMAGE_SCN_MEM_PROTECTED - Obsolete   0x00004000
#define IMAGE_SCN_NO_DEFER_SPEC_EXC          0x00004000  // Reset speculative exceptions handling bits in the TLB entries for this section.
#define IMAGE_SCN_GPREL                      0x00008000  // Section content can be accessed relative to GP
#define IMAGE_SCN_MEM_FARDATA                0x00008000
//      IMAGE_SCN_MEM_SYSHEAP  - Obsolete    0x00010000
#define IMAGE_SCN_MEM_PURGEABLE              0x00020000
#define IMAGE_SCN_MEM_16BIT                  0x00020000
#define IMAGE_SCN_MEM_LOCKED                 0x00040000
#define IMAGE_SCN_MEM_PRELOAD                0x00080000

#define IMAGE_SCN_ALIGN_1BYTES               0x00100000  //
#define IMAGE_SCN_ALIGN_2BYTES               0x00200000  //
#define IMAGE_SCN_ALIGN_4BYTES               0x00300000  //
#define IMAGE_SCN_ALIGN_8BYTES               0x00400000  //
#define IMAGE_SCN_ALIGN_16BYTES              0x00500000  // Default alignment if no others are specified.
#define IMAGE_SCN_ALIGN_32BYTES              0x00600000  //
#define IMAGE_SCN_ALIGN_64BYTES              0x00700000  //
#define IMAGE_SCN_ALIGN_128BYTES             0x00800000  //
#define IMAGE_SCN_ALIGN_256BYTES             0x00900000  //
#define IMAGE_SCN_ALIGN_512BYTES             0x00A00000  //
#define IMAGE_SCN_ALIGN_1024BYTES            0x00B00000  //
#define IMAGE_SCN_ALIGN_2048BYTES            0x00C00000  //
#define IMAGE_SCN_ALIGN_4096BYTES            0x00D00000  //
#define IMAGE_SCN_ALIGN_8192BYTES            0x00E00000  //
// Unused                                    0x00F00000
#define IMAGE_SCN_ALIGN_MASK                 0x00F00000

#define IMAGE_SCN_LNK_NRELOC_OVFL            0x01000000  // Section contains extended relocations.
#define IMAGE_SCN_MEM_DISCARDABLE            0x02000000  // Section can be discarded.
#define IMAGE_SCN_MEM_NOT_CACHED             0x04000000  // Section is not cachable.
#define IMAGE_SCN_MEM_NOT_PAGED              0x08000000  // Section is not pageable.
#define IMAGE_SCN_MEM_SHARED                 0x10000000  // Section is shareable.
#define IMAGE_SCN_MEM_EXECUTE                0x20000000  // Section is executable.
#define IMAGE_SCN_MEM_READ                   0x40000000  // Section is readable.
#define IMAGE_SCN_MEM_WRITE                  0x80000000  // Section is writeable.


그렇다면 이제 IAT를 ... !


IAT (Import Address Table)

PE 헤더의 최대의 벽은 IAT(Import Address Table)이라고 생각합니다.


두둥!!!


이 IAT는 윈도우 운영체제의 핵심 개념인 프로세스, 메모리, DLL 구조 등에 대한 내용이 함축되어있습니다.

고로 DLL(Dynamic Linked Library)에 대한 지식이 필요합니다.

쉽게 IAT에 말하자면 어떤 라이브러리에서 어떠한 함수를 사용하고 있는지를 기술한 테이블입니다.

[ 말 출처 : 리버싱 핵심 원리 ]

그렇다면 DLL에 대해 먼저 알아보도록 하겠습니다.


DLL (Dynamic Linked Library)

IAT를 알려면 DLL 개념을 짚고 가야됩니다.

동적 링크 라이브러리(영어: dynamic-link library, DLL)는 마이크로소프트 윈도우에서 구현된 동적 라이브러리이다. 
내부에는 다른 프로그램이 불러서 쓸 수 있는 다양한 함수들을 가지고 있는데, 확장DLL인 경우는 클래스를 가지고 있기도 한다. 
DLL은 COM을 담는 그릇의 역할도 한다.

사용하는 방법에는 두 가지가 있는데,

묵시적 링킹(Implicit linking) 

실행 파일 자체에 어떤 DLL의 어떤 함수를 사용하겠다는 정보를 포함시키고 운영체제가 프로그램 실행 시 해당 함수들을 초기화한 후 그것을 이용하는 방법

명시적 링킹(Explicit linking) 

프로그램이 실행 중일 때 API를 이용하여 DLL 파일이 있는지 검사하고 동적으로 원하는 함수만 불러와서 쓰는 방법이 있다.
전자의 경우는 컴파일러가 자동으로 해주는 경우가 많으며, 후자의 경우는 사용하고자 하는 DLL이나 함수가 실행 환경에 있을지 없을지 잘 모르는 경우에 사용된다. (때때로 메모리 절약을 위해 쓰이기도 한다.)

[ 출처 : https://ko.wikipedia.org/wiki/%EB%8F%99%EC%A0%81_%EB%A7%81%ED%81%AC_%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC ]



여기서 IAT는 묵시적 링킹에 대한 매카니즘을 제공하는 역할을 합니다.

아! DLL은 넘어가고!!


Hell o IAT


어서와~!


이 IAT 설명에서 사용할 코드는 아래와 같습니다.



#include <stdio.h>

#include <Windows.h>


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

{

HWND hWnd = FindWindow(NULL, TEXT("Untitled - Notepad"));


if (hWnd) {

printf("있음\n");

}

else

{

printf("없음\n");

}


return 0;

}

 


그렇다면 이제 진짜 확인을 위해 올리디버거로 프로그램을 열어보겠습니다.



0x011A17C7 번지를 보면 FindWindowA를 직접 호출하는 것이 아니라



0x011AA098 번지에 있는 0x7787FFE6값을 가져와 호출을 하는 간접적인 호출 방식이라는 것을 볼 수 있습니다.

(모든 API 호출은 이런 방식으로 되어있습니다.)


0x011AA98 번지는 이 프로그램의 ".text" 섹션 메모리 영역입니다.

0x011AA98 번지의 값은 0x7787FFE6이라는 것을 위에서 볼 수 있었으며,

0x7787FFE6 주소는 바로 이 프로그램의 프로세스 메모리에 로딩된 user32.dll 내에 있는 FindWindowA 함수 주소입니다.


그렇다면 여기서 의문점이 하나가 생기게 됩니다.

엥 이거 완전 바로 0x7787FFE6를 Call하면 좋지 않냐~?!


이 방법은 DOS 시대의 방법이였습니다.

이 프로그램이 생성되는 순간에 이 프로그램이 어떤 윈도우 환경에 실행되는지 알 수가 없으며,

이런 환경에 따라 kernel32.dll, user32.dll 등의 버전이 틀려지게 되고,

FindWindowA함수의 주소가 달라지게 됩니다.


그렇기 때문에 어떠한 환경에서도 FindWindowA 함수의 호출을 보장하기 위해 

컴파일러가 0x011AA98 번지에 미리 공간을 마련해두고 파일이 실행이 된 직후에 PE 로더가 이 공간에 FindWindowA의 주소를 넣어주게 됩니다.

이러한 DLL 내의 함수 주소를 모아놓은 테이블을 만들어놓고 코드 섹션에서 만들어 놓은 테이블을 쓰는 방식으로 관리를 하여 이 테이블을 IAT라고 합니다.


이정도만 알아두면 IAT는 절반은 알았다! 라고 할 수 있을 것 같습니다.


아! PE 파일은 자신이 어떤 라이브러리를 import하고 있는 지 어떠한 구조체에 명시하고 있습니다.

이 아래에서는 그 구조체에 대해 알아보도록 하겠습니다!


IMAGE_IMPORT_DESCRIPTOR

아래의 코드는 winnt.h 헤더에 있는 IMAGE_IMPORT_DESCRIPTOR의 구조체입니다.


typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;
    BYTE    Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;


이 구조체는 6개의 필드 중 중요한 3개의 필드만 보도록 하겠습니다.


앗, 먼저 필드를 보기 전에 IMAGE_IMPORT_DESCRIPTOR의 위치를 찾아야됩니다.

위에서 IMAGE_OPTIONAL_HEADER의 DataDirectory가 기억나시나요?


어이쿠 안나신다고요??!

올리기 귀찮으실테니 이미지를 첨부해드리겠습니다!

(저는 압니다... 올리다가 다 까먹으실 것을요!)



2번째 요소가 Import Directory을 볼 수 있습니다.



초랭이 : VirtualAddress(RVA)

주랭이 : VirtualSize


VirtualAddress(RVA) : 0x0001A000

VirtualSize : 0x000001B4


나머지 Virtual Address를 구하려면 현재 RVA가 어디 섹션에 해당하는지를 알아야됩니다.



위에서 알려드린 IMAGE_SECTION_HEADER 기억나시죠?

앗... RVA가 이 섹션에 숨어있었습니다!



핫핑꾸 색깔로 표시해두었습니다.

위와 같이 0x0001A000이 실제적으로 ".idata" 섹션에 위치하는 옵셋이 됩니다.


".idata" 섹션의 이름은 0x6164692E 이며,

VirtualSize : 0x00000A6A

RVA : 0x0001A000

Size of Raw Data : 0x00000C00

Pointer to Raw Data : 0x00007600


앗, Raw 옵셋을 찾았습니다.



위의 영역이 모두 IMAGE_IMPORT_DESCRIPTOR 구조체 배열이며, 

이 영역 처음부터 20byte까지는 구조체 배열의 첫번째 요소입니다.


DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)

를 알아보기 전에 IMAGE_THUNK_DATA라는 구조체를 보도록 하겠습니다.


IMAGE_THUNK_DATA

아래는 winnt.h 헤더파일의 IMAGE_THUNK_DATA 구조체입니다.



typedef struct _IMAGE_THUNK_DATA64 {

    union {

        ULONGLONG ForwarderString;  // PBYTE 

        ULONGLONG Function;         // PDWORD

        ULONGLONG Ordinal;

        ULONGLONG AddressOfData;    // PIMAGE_IMPORT_BY_NAME

    } u1;

} IMAGE_THUNK_DATA64;

typedef IMAGE_THUNK_DATA64 * PIMAGE_THUNK_DATA64;


#include "poppack.h"                        // Back to 4 byte packing


typedef struct _IMAGE_THUNK_DATA32 {

    union {

        DWORD ForwarderString;      // PBYTE 

        DWORD Function;             // PDWORD

        DWORD Ordinal;

        DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME

    } u1;

} IMAGE_THUNK_DATA32;

typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;



IMAGE_THUNK_DATA 필드는 총 4개로 모두 공용체의 멤버이며, 4바이트 공간을 4개의 필드가 공유합니다.


이 이상은 제 지식이 딸려서 적을 수가 없네요 :'(

덕분에 작성하면서 깨달음을 많이 얻었습니다!

생각정리하는데는 글 쓰기가 꿀이네요.

'0x20 Security > 0x22 Reversing' 카테고리의 다른 글

UPX Pakking Principle (UPP)  (1) 2017.07.07

Buffer Overflow

먼저 시작하기에 앞서 미리 알려드립니다!
내용이 요리조리로 통통 튀어다니면서 이상하고 틀린 부분도 있을 수 있습니다.

이번엔 시스템 해킹에서의 기본 중의 기본인 BOF를 정리하는걸로...

먼저 BOF는 말 그대로 Buffer (버퍼가) Overflow (넘치다)

버퍼가 넘친다는 말입니다.

프로그래머의 실수로 인해 버퍼가 할당받은 공간에 크기 제한을 하지 않고 값들을 집어넣어 할당받은 공간보다 더 값을 넣을 수 있는 취약점입니다.


쉽게 말해서 buf[10]이라는 배열을 만들었다면,

이 배열은 총 10개의 공간을 가지게 됩니다.

하지만 buf에 10개의 값을 넣는게 아닌 11개, 12개 혹은 그 이상의 값들을 크기를 확인하지 않고 마구잡이로 넣어서 BOF 취약점이 터지게 되는 것이죠 :D


먼저 BOF에 들어가기 앞서 스택 구조와 프롤로그 에필로그에 대해 알아보도록 하겠습니다.


Hello STACK!

네, 스택은 과연 무엇일까요?



이 블루스택 프로그램을 말하는 걸까요?

오...아쉽게도 아이콘은 비슷했지만 이 프로그램을 말하는 것은 아니였습니다.


음, 프로그래밍을 좀 해보셨다면 자료구조에서 스택을 분명히 들어보셨을겁니다.


  

스택은 선입후출 FILO(First In Last Out), 후입선출 LIFO(Last In First Out) 구조를 가지고 있으며, 쉽게 말해 접시와 같다고 흔히들 비교를 하십니다.



이렇게 처음에 쌓인 것이 나중에 나온다 해서 접시, 책 등등으로 쉽게 비교를 하시며 설명을 하십니다.


Open Program in GDB!

먼저 BOF 취약점이 존재하는 프로그램을 GDB에서 열어보았습니다.

아래는 그 프로그램의 코드입니다.


#include <stdio.h>

int main(int argc, char *argv[])
{
        char buf[10];
        scanf("%s", buf);

        return 0;
}



앗, 먼저 저희는 프롤로그와 에필로그에 대한 지식이 필요할 것 같습니다.

일단 여기서 뒤로 하고 프롤로그 에필로그에 대해 알아보도록 하겠습니다.


Stack Prologue&Epilogue

음...프롤로그 에필로그를 어디에서 들어봤을까요?
저는 보통 웹툰에서 들어본 것 같습니다...!

말 그대로 함수의 시작과 끝을 말해주는 녀석입니다.

Prologue

push ebp
mov ebp, esp

해당 함수가 시작된다는 것을 알려주는 녀석입니다.

Epilogue

leave
ret

이 leaveret을 풀어보게 되면 아래와 같아집니다.

leave

mov esp, ebp
pop ebp

ret

pop eip
jmp eip

말 그대로 leave에서는 프롤로그에서 push ebp 한 것을 스택에서 pop ebp로 스택에서 꺼내주는 과정입니다.

네, 그렇다면 프롤로그 에필로그를 이해했다면 아까 중간에 끊긴 것을 이어가도록 하겠습니다!

아까 보셨던 BOF 취약점이 존재하는 프로그램을 GDB에서 열은 것입니다.


먼저 프롤로그가 시작됩니다.



sub esp, 0x20은 스택을 사용하기 위해 공간을 확보하는 것입니다.


그 뒤에는 scanf가 실행되는데요.

위에 mov되는 것은 저희가 넘겨준 인자들입니다.



그렇다면 버퍼의 주소는 0xffffd6e6이겠네요.

그리고 실제로 버퍼 주소가 맞는 것을 확인할 수 있었습니다.



그렇다면 이번에는 크기를 제한하지 않고 버퍼보다 많은 값들을 넘겨줘보겠습니다.



띠용 eip가 주작이 되었습니다.


이제 여기서 알아야될 것은 SFP와 RET입니다!



위와 같이 생겨먹었습니다.

dummy는 gcc 2.9.6? 2.9.5? 음...아무튼 저 근처대의 버전에서 더미가 추가되었습니다.


SFP는 이전 함수의 EBP를 저장해두는 장소입니다.

해당 함수가 끝이 나서 함수를 빠져나와야되는데, 돌아갈 주소를 다시 찾아가기 위한 저장소(?)입니다.


RET은 SFP와 마찬가지로 이전 함수로 돌아가기 위한 주소를 가지고 있습니다.


아무튼, 위와 같은 구조를 가지고 있었기에, buf와 dummy를 넘기고 RET까지 도달하였기에 0x62626262으로 이동하게 된 것 입니다.

이러한 방법으로 RET을 의도적으로 원하는 주소로 이동시킬 수 있기 때문에

프로그램을 해커가 원하는 흐름으로 바꿀 수 있게 되는 것 입니다.


BOF는 해킹의 꽃이라고 볼 수도 있을 것 같습니다!

감사합니다.

'0x20 Security > 0x21 System' 카테고리의 다른 글

Format String Bug (FSB)  (0) 2016.12.01
Save Frame Pointer Overwrite (SFO)  (0) 2016.07.25
About LD_PRELOAD  (0) 2016.07.20
Use After Free (UAF)  (1) 2016.07.13
How main() is executed on linux  (2) 2016.07.04

그러타.

나는 미스터 로봇 시즌1을 다본 사람이다.

미스터 로봇 시즌1이 끝날때 마지막 부분에 아쉽게 끝내서 아쉬워하던 도중

어제 미스터 로봇 시즌 2가 나왔다는 소리를 들었다.


 

오.. 세상에 7월 13일에 나왔다니.


현재는 3화까지 나와있다.


아니 근데 여기서 왜 미스터 로봇이 롸이트업에 들어가있느냐~!?


바로 미스터 로봇에 숨겨진 이스터 에그가 있었다.



바로 이 장면에 나타나는 모니터에 있었다.



이 장면은 tool을 이용하여 랜섬웨어를 생성하는 장면이였는데.


수상한 아이피를 발견할 수 있었다.


192.251.68.254:80에 랜섬웨어가 배포되는 것처럼 느껴진다.


그리고 실제로 접속이 되는지 확인을 해보았다.



실제로 접속이 되고 헤더 중 Location을 발견할 수 있는데,

http://i254.bxjyb2jvda.net/에 리다이렉트가 되는 것을 발견할 수 있었다.



마지막으로는 http://i239.bxjyb2jvda.net 로 이동을 하게 된다.



실제로 접속해보면 이러한 페이지가 나타난다.

타이머로 시간이 가고 있고 요상한 페이지였다.



또로롱


그리고 web_analytics.js 라는 수상한 스크립트를 발견할 수 있었다.


web_analytics.js 코드는 아래와 같다.



/* analytics */

var AdobeTracking = new Object();

AdobeTracking.division = 'Cable';

AdobeTracking.businessUnit = 'USA Network';

AdobeTracking.contentGroup = 'Drama';

AdobeTracking.contentType = 'Home';

AdobeTracking.pageName = 'Mr. Robot : S2 Easter Egg Sites : Ransomware : Home';

AdobeTracking.showSite = 'Mr. Robot';

var q='PGRpdiBjbGFzcz0ib3ZlciI+PGRpdj4iSSBzaW5jZXJlbHkgYmVsaWV2ZSB0aGF0IGJhbmtpbmcgZXN0YWJsaXNobWVudHMgYXJlIG1vcmUgZGFuZ2Vyb3VzIHRoYW4gc3RhbmRpbmcgYXJtaWVzLCBhbmQgdGhhdCB0aGUgcHJpbmNpcGxlIG9mIHNwZW5kaW5nIG1vbmV5IHRvIGJlIHBhaWQgYnkgcG9zdGVyaXR5LCB1bmRlciB0aGUgbmFtZSBvZiBmdW5kaW5nLCBpcyBidXQgc3dpbmRsaW5nIGZ1dHVyaXR5IG9uIGEgbGFyZ2Ugc2NhbGUuIjwvZGl2PjxkaXYgY2xhc3M9ImF1dGhvciI+LSBUaG9tYXMgSmVmZmVyc29uPC9zcGFuPjwvZGl2PjwvZGl2Pg==';

AdobeTracking.showSiteFeatureII = 'Mr. Robot : S2 Easter Egg Sites : Ransomware';

function fire_beacon() { return window.atob(q); }

AdobeTracking.showSiteFeature = 'Mr. Robot : S2 Easter Egg Sites';

function fire_beacon_final(periods) { if ($('.over').length > 0) { AdobeTracking.pageName = 'Mr. Robot : S2 Easter Egg Sites : Ransomware : Countdown Ended'; _satellite.track('virtPageTrack'); }}

$(function () { var austDay = new Date(); austDay.setSeconds(austDay.getSeconds() + 86394 /*86394*/); $('#defaultCountdown').countdown({until: austDay,expiryText: fire_beacon(),onTick: fire_beacon_final, tickInterval: 1});});

_satellite.pageBottom();



뭐 그 뒤는 저 base64 인코딩된 문자열을 디코딩을 해보면.



라는 문자열을 발견하게 된다.


I sincerely believe that banking establishments are more dangerous than standing armies, and that the principle of spending money to be paid by posterity, under the name of funding, is but swindling futurity on a large scale.

- Thomas Jefferson


아마 나중에 나올 스토리와 연관이 있는 것 같다.

기대된다 ... !

'0x20 Security > 0x25 Write-Ups' 카테고리의 다른 글

[Pwnable.kr] input - 4 pt  (0) 2017.01.04
[Pwnable.kr] cmd2 - 9 pt  (0) 2016.10.05
[ReverseLab 스터디] [Abex's CM] Abex 1~5  (0) 2016.07.11
[Pwnable.kr] dragon - 75 pt  (0) 2016.06.25
[Codegate2016] Write ups  (372) 2016.03.18

LD_PRELOAD에 대해 ARABOZA

LOB 문제를 풀다 처음 알게 되어서 정리를 해봤습니다.

아, 환경마다 PRELOAD 환경 변수 이름이 다르더라구요.
뭐 사실상 AIX 빼고는 다 이름이 같습니다.

what is LD_PRELOAD ?

프로세스를 실행하는 중에 라이브러리를 로딩할 때,
이 LD_PRELOAD 환경변수가 설정되있으면 해당 변수에 지정된 라이브러리를 먼저 로딩을 하고,
이 중에 libc 함수명과 동일한 함수가 있다면 해당 함수를 먼저 호출을 해주게 됩니다.


where is LD_PRELOAD ?


LD_PRELOAD의 메모리 영역은 공유 라이브러리 영역에 존재해서 stack 영역보다 낮은 주소에 존재합니다. 


Let's compile shared library!

.so로 컴파일 하기 위해서는 아래와 같은 옵션을 넘겨주어야됩니다.


-Wall 옵션 : 모든 경고 메세지를 출력 (사실 상 안넘겨주어도 되는 옵션입니다.)
-fPIC 옵션 : Position-Independent Code의 약자이며 test.o파일을 동적라이브러리로 사용하도록 컴파일 하는 옵션이다.
-shared 옵션 : 공유 라이브러리를 만드는 옵션


$ gcc -Wall -fPIC -shared -o filename.so filesource.c


같이 공유 라이브러리를 컴파일 할 수 있습니다.


Modify LD_PRELOAD

초 간단합니다.

$ export LD_PRELOAD="./filename.so"


Fun it !

그렇다면 이 LD_PRELOAD를 이용하여 후킹을 해보도록 하겠습니다.


hostname 명령어는 말 그대로 호스트네임의 값을 출력해주는 명령어입니다.



gethostname를 만들면 되겠네요!



#include <stdlib.h>

#include <string.h>


int gethostname(char *name, size_t len)

{

    strncpy(name, "HOOKHOOK", len-1);

    name[len-1] = '\0';

    return 0;

}



$ gcc -shared -fPIC -o hook.so a.c


컴파일을 하신 뒤,



LD_PRELOAD에 직접 만든 라이브러리 경로를 넘겨주고!

실행을 하게 되면.



잘 된 것을 볼 수 있습니다!


하지만, 중요한 것은 LD_PRELOAD는 setuid가 걸려있으면 동작을 하지 않습니다.




'0x20 Security > 0x21 System' 카테고리의 다른 글

Save Frame Pointer Overwrite (SFO)  (0) 2016.07.25
Buffer Overflow (BOF)  (2) 2016.07.22
Use After Free (UAF)  (1) 2016.07.13
How main() is executed on linux  (2) 2016.07.04
System Hacking :: Memory Protection  (0) 2016.06.28

Use After Free

오늘 다뤄볼 취약점은 Use After Free입니다.

말 그대로 사용한 후 메모리를 해제했을 때에 취약점을 발견할 수도 없을 수도 있을 수도 있습니다.

이 취약점은 근래에 브라우저 취약점에서 많이 발견되었습니다.


[CVE-2012-4792 IE Use-After-Free Analysis and Exploit]

- http://pgnsc.tistory.com/348


멋쟁이 sweetchip님의 BoB 프로젝트 도중에 그 당시에 문서로 남겨놓으셨습니다!


이러한 UAF 취약점은 스택 영역에서 이루어지는 취약점이 아닌 힙 영역에서 발생하는 취약점입니다.

What is Heap?

네, 그렇다면 heap은 무엇일까요


잘 빠지고 이쁜 엉덩이를 말하는 걸까요?
아쉽게도 이런 이쁜 엉덩이를 원하셨다면, 유감

출처 : https://namu.wiki/w/%EC%98%A4%EC%A6%88%EB%9E%9C%EB%93%9C


Heap은 프로그래머가 필요에 따라 메모리 공간이 동적 할당/소멸되는 영역입니다.

하지만 스택은 힙과는 달리 정적으로 할당이 되기 때문에 컴파일 시 미리 스택에 공간이 할당이 되어있습니다.


예를 들자면 아래와 같습니다.


0x08048510   <+0>: push   ebp

0x08048511   <+1>: mov    ebp, esp

0x08048513 <+3>: sub    esp, 0x120


위의 어셈 코드와 같이 0x120(288)만큼 할당하는 코드를 볼 수 있듯이 스택은 컴파일할 때에 할당되는 영역입니다.

하지만 힙은 런타임 시 할당되는 유용하게 사용되는 공간입니다.

이 영역은 시작과 동시에 메모리에 올라고 프로그램이 종료될때까지 남아있습니다.


이러한 힙 영역을 사용하기 위해서는 동적할당에 대해 알아두어야됩니다.


info malloc !

흔히 C언어로 코딩 좀 해봤다 싶으면 봤을 듯한 함수인 malloc입니다.

이 malloc 함수는 동적으로 메모리를 할당하는 함수로서 아래와 같이 생겨먹었습니다.


void* malloc(size_t size)


함수를 호출 시 할당하고자 하는 메모리의 크기를 인자에 전달하면 그 크기만큼 메모리를 할당하게 되고 그리고 그 할당한 메모리의 주소를 리턴하게 됩니다.

메모리에 할당에 실패하면 NULL을 리턴하게 됩니다.


Eh? return type is void* ??

ㅗㅜㅑ... 반환 타입이 void*네요.
왜 그럴까요?
이 malloc 함수는 그저 메모리를 할당해주는 함수이기 때문에 이 개발자라는 녀석이 어떤 데이터형으로 할당하는지 알 수 없습니다.
그렇기 때문에 void*로 반환하여 개발자가 입맛에 맞게 변환하여 사용할 수 있게 만들어뒀습니다.

Hell o malloc

malloc은 아래와 같이 생겨먹었습니다.

struct malloc_chunk {
  INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */
  INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */

  struct malloc_chunk* fd;         /* double links -- used only if free. */
  struct malloc_chunk* bk;

  /* Only used for large blocks: pointer to next larger size.  */
  struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
  struct malloc_chunk* bk_nextsize;
};

INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */

INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */



할당을 하게 되면 이와 같은 구조를 가지고 있습니다.

prev_size 필드에는 free가 된 이전의 chunk의 사이즈를 가지고 있습니다.


size 필드에는 할당된 사이즈를 담고 있는데.

x86 아키텍처에서는 최소 4바이트 만큼의 공간이 필요합니다.

각 필드들은 8바이트 단위로 정렬이 되서 실제 크기는 더 커질 수 있습니다.


그리고 size 필드에는 3가지의 플래그가 있습니다.

P 플래그는 PREV_INUSE로 이전 chunk가 사용 여부를 나타내는 녀석입니다.

이 플래그가 지워져있으면 free chunk라는 의미가 됩니다.


N 플래그는 NON_MAIN_ARENA로 멀티 쓰레드로 돌아가는 프로그램에서 쓰레드마다 다른 힙 영역을 사용하는 경우

현재 chunk가 main 힙에 속하는지 여부를 나타냅니다.


M 플래그는 IS_MMAPPED로 해당 필드가 mmap()으로 할당된 것인지 아닌지를 나타냅니다.

mmap()으로 할당된 chunk는 malloc과는 다른 방식으로 메모리를 관리합니다.


#define PREV_INUSE       0x1

#define IS_MMAPPED       0x2

#define NON_MAIN_ARENA   0x4


#define SIZE_BITS        (PREV_INUSE|IS_MMAPPED|NON_MAIN_ARENA)

#define chunksize(p)     ((p)->size & ~(SIZE_BITS))


struct malloc_chunk* fd;         /* double links -- used only if free. */

struct malloc_chunk* bk;

struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */

struct malloc_chunk* bk_nextsize;

이 힙이 free될 때에 usable area영역에 fd와 bk가 생성이 되는데
fd는 forward pointer
bk는 backward pointer
를 의미하며 큰 chunk에서는 fd_nextsize와 bk_nextsize가 생성이 됩니다.

이는 더블 링크드 리스트의 prev, next와 비슷하다고 보시면 됩니다.

그럼 이정도로 설명을 하도록 하고 UAF로 넘어가보도록 하겠습니다.

Yeah Use After Free!

동적할당된 힙을 free하고 다시 재사용할 때에 취약점이 발견되는 것을 UAF라고 합니다.
아래의 코드는 UAF 취약점이 존재하는 코드입니다.

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
    void *one, *two;

    one = (char*) malloc(170);
    printf("[0] one : %s\n", (char*) one);

    printf("[1] input string in one : ");
    scanf("%s", (char*) one);

    printf("[2] one : %s\n", (char*) one);
    printf("one pointer : %p\n", (char*) one);
    free(one);

    two = (char*) malloc(170);
    printf("two pointer : %p\n", (char*) two);
    printf("[3] two : %s\n", (char*) two);
    return 0;
}

이 코드를 실행해보면 아래와 같은 결과를 볼 수 있습니다.


one에 입력한 내용이 two에도 출력이 되고 one 주소와 two의 주소가 같음을 볼 수 있습니다.

왜 이럴까요?


Why?

malloc의 caching 때문이다.


출처 : http://g.oswego.edu/dl/html/malloc.html


caching 기능의 Deferred Coalescing이 있는데.

free를 하더라도 chunk를 정리하는 것보다는 같은 사이즈로 요청을 받을 때에 병합또는 분할하는 시간을 절약하고자 재활용을 하게 해주는 것입니다.


출처 : 아는 동생의 중고거래 (무려 어제 있었던 일이다)


disas GDB


초록색 : one size
파란색 : 사용하지 않은 공간 사이즈

gdb로 보게 되면 이렇게 생겨져있습니다.
처음 할당된 부분이기 때문에 fd, bk가 있지 않습니다.

아직 힙에 아무 내용을 넣지 않았기때문에 깨끗합니다.


이제 값이 들어간 것을 볼 수 있습니다.


그리고 free한 뒤에 two를 malloc을 하게 됩니다.

free할 때에 재사용할 힙 공간이 처음에 사용한 0x804b008만 있기 때문에 자연스레 처음에 사용한 힙이 재사용되면서 전에 입력한 값이 출력이 되게 됩니다.



띵똥

Use After Free in sample program

아래는 문제의 코드입니다.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct q1w2e3r4{
    int id;
    char name[20];
    void (*clean) (void *);
} VULNED;

void cleanMem(void *mem){
    free(mem);
}

void hacksure(){
    system("/bin/sh");
}
    
int main(int argc, char *argv[]){
    void *vuln_tst;
    VULNED *vuln = malloc(170);

    fflush(stdin);
    printf("Enter your id number : ");
    scanf("%d",&vuln->id);
    fflush(stdin);
    printf("Enter your name : ");
    scanf("%s", vuln->name);
   
    vuln->clean = cleanMem;

    if(vuln->id > 400){
        printf("Your id is too big!\n");
        vuln->clean(vuln); 
    }                                   
 
    vuln_tst = malloc(170); 
    strcpy(vuln_tst,argv[1]); 
 
    free(vuln_tst);
    vuln->clean(vuln); 

    return 0;
}


문제의 코드이다.
이 코드의 출처는 멋지고 잘생기고 시스템 해킹 잘하는 cd80 문서에서 코드를 참조하였다.

직접 풀어보고 감을 익히는 게 좋을 것 같다.

$ gcc sample.c -o sample -z execstack -fno-stack-protector

컴파일을 하고 문제를 구경해보도록 합시다.


Scenario

먼저 사용을 하고 free를 해야 다시 할당을 할 때에 다시 그 공간을 사용하게 해야됩니다.

vuln의 id 값이 400보다 크면 id가 크다는 문자열을 띄워주면서 cleanMem을 실행하면서 vuln을 free를 해주게 됩니다.

그 다음에 vuln과 같은 크기로 vuln_tst을 할당하고 strcpy로 인자를 vuln_tst에 복사를 한 뒤에 vuln_tst를 free를 해주고 이미 free되있는 vuln을 호출하게 됩니다.


먼저 이에 대해서는 vuln과 vuln_tst은 같은 주소를 가르키고 있고, vuln_tst에 값을 크기 상관없이 조져버릴 수 있습니다.

그렇게 조져버린 메모리를 끝에서 호출을 하기 때문에 우리는 eip 주작질이 가능하게 됩니다.


Exploit

먼저 vuln의 clean이 어디에 위치해있는지를 알아봅니다.



그렇다면 clean은 24만큼 떨어진 곳에 있습니다.



이렇게 이후에 eip를 주작질을 할 수 있음을 볼 수 있습니다.


그 뒤로는 휘리릭 뾰로롱 하면 쉘이 따집니다.



넵, 안녕 !



길고 긴 글을 읽으시느라 수고하셨습니다.

감사합니다.






'0x20 Security > 0x21 System' 카테고리의 다른 글

Buffer Overflow (BOF)  (2) 2016.07.22
About LD_PRELOAD  (0) 2016.07.20
How main() is executed on linux  (2) 2016.07.04
System Hacking :: Memory Protection  (0) 2016.06.28
[SSA] 시스템 해킹 스터디 6주차  (349) 2015.11.09

Abex Crack Me 

리버싱을 공부해본 적이 있는 사람들이라면 한번은 꼭 봤을 듯한 크랙미 시리즈이다.
출처는 어디인지 모르지만 아마도 Abex라는 사람이 올린걸 유추할 수 있다.

Abex Crack Me 1

먼저 분석을 위해 이 프로그램이 어떻게 돌아가는 지를 확인해볼 필요가 있다.



나의 하드디스크를 CD-Rom으로 인식하게 만들어보란다.

하지만 당연히 내 하드디스크는 CD-Rom이 아니므로 저런 메세지는 당연하지만 ...



어셈 코드가 졸라 짧으므로 분석이 매우 쉽다.

GetDriveTypeA 함수는 지정한 disk가 어떤 형태의 저장소인지 판단을 하는 함수이다.

kernel32에 있는 함수이며, 함수가 실행되면 특정 정수 값을 리턴하는데 해당 값에 따라 디스크의 유형을 판단할 수 있다.


 상수

 값

 설명

 DRIVE_UNKNOWN

 0

 알 수 없음

 DRIVE_NO_ROOT_DIR

 1

 최상위 경로가 없음

 DRIVE_REMOVABLE

 2

 이동형 저장장치

 DRIVE_FIXED

 3

 고정형 저장장치

 DRIVE_REMOTE

 4

 네트워크 드라이브

 DRIVE_CDROM

 5

 DVD/CD-ROM 유형

 DRIVE_RAMDISK

 6

 램 디스크


그렇습니다.

아마 그렇다면 저 함수가 실행되고 eax 레지스터에는 3이 들어가있겠네요!



그리고 실제로 3이 들어간 것을 볼 수 있습니다.


하지만 저희는 CD-ROM으로 인식하게 해야되기 때문에 저 eax 값엔 원래는 6이 리턴이 되어야됩니다.


하지만 그 값들은 여기서 요상한 연산을 하고 eax랑 esi랑 비교를 하여 같을 경우에 CD-ROM으로 인식했다고 조져따 라고 메세지를 띄웁니다.

고로 저는 다 필요없이 비교 부분에 서로 같은 것을 비교하게 조져버렸습니다.



ㅅㄱ



Abex Crack Me 2



먼저 이 문제는 흔하디 흔한 키젠 문제인 것 같습니다.

분명히 이름을 통해 시리얼키가 생성이 되고 그 생성된 키와 체크하는 것이라고 예상을 할 수 있겠습니다.


디버깅 ㄱㄱ



이 부분에서 eax에 리턴 값이 들어가고 그 ax 끼리 test 연산을 하고 시리얼이 틀렷는지 맞는지 확인을 하므로

vbaVarTstEq가 서로 비교하는 함수라는 것을 알 수 있습니다.


그렇게 내부로 들어가보면.



실제로 비교하는 것을 알 수 있었습니다.



차곡차곡 쌓인 것을 볼 수 있습니다.



00000008

0018F40C

00298784  UNICODE "CCD9C6C9"

0018F3C4


00000008

0018F40C

0029886C  UNICODE "123456789"

0018F3C4



CCD9C6C9과 제가 입력한 값을 비교합니다.



ㅅㄱ링딩동


그리고 시리얼 생성하는 부분을 분석을 하고 키젠을 만들어봤다.


def keygen(name):

key = ''

for i in range(4):

key += chr(ord(name[i])+0x64).encode("hex")

return key.upper()


Abex Crack Me 3




실행을 해보면 이러하다.

아마도 그렇다면 예상 시나리오는.

키 파일이라는 걸 생성해서 그 키 파일에서 내용을 읽고 체크하는 문제일까 하고 생각을 해보았다.


뭐 이런 생각은 디버깅을 하고 난 뒤면 꼼짝 못해~!



뭐 코드도 짧으니 금방 분석이 가능하시겠다.

메세지 박스를 띄우고 abex.l2c를 읽기모드로 연다.

하지만 당연히 eax에는 이 파일이 없기때문에 FFFFFFFF을 리턴하고 저 eax 비교에서 분기되서 파일을 찾을 수 업써!! 라고 메세지를 띄우게 된다.

고로 프로그램이 있는 경로에 저 파일을 생성해주자.



ㅇㅋ


그렇다면 파일을 찾았기 때문에.

아마 파일 찾는 분기에서는 걸리지 않을 것이다.


그 다음은 파일의 사이즈를 리턴하여 그 사이즈가 12가 아닐 경우에는 틀린 키 파일이라면서 찡찡거리게 된다.

고로 사이즈를 0x12(18)로 맞추고 실행을 해주면 풀릴 것 이다.



Abex Crack Me 4



그렇다.

이 문제는 버튼이 비활성화가 되있다...


고로 디버깅 ㄱ



현재 날짜들을 구해와서 mul, add연산을 통해 시리얼이 완성된다.



이렇게 eax에 시리얼이 들어있는 것을 볼 수 있다.


이게 시리얼인지 확인하는 방법은 vbacmp 함수를 확인하면 된다.



입력한 값과 시리얼을 비교하는 것을 볼 수 있다.


그리고 같을 경우 가입이 활성화된다.


Abex Crack Me 5



L2C-57816784-ABEX가 시리얼이다. 

끝.


'0x20 Security > 0x25 Write-Ups' 카테고리의 다른 글

[Pwnable.kr] cmd2 - 9 pt  (0) 2016.10.05
Mr.Robot Season2 Easter Egg  (0) 2016.07.21
[Pwnable.kr] dragon - 75 pt  (0) 2016.06.25
[Codegate2016] Write ups  (372) 2016.03.18
[Pwnable.kr] cmd1 - 1 pt  (0) 2015.10.07

main()는 리눅스에서 어떻게 실행이 될까?



시작하기에 앞서 리눅스에서의 실행파일이 무엇인지 알아본 뒤에 main 실행이 어떻게 되는지 깊숙히 파고들 예정입니다.
먼저 리눅스에서의 실행파일이 무엇인지 알아본 뒤에 실행 포맷 구조를 알아 본 뒤에 main에 대해 들어갈 예정입니다.
아마 내용이 축구공처럼 통통 튀기고 이리저리 갈 것 같으니 언제든지 이상한 부분은 댓글로 지적해주시면 감사하겠습니다.


What is ELF?



그래서 ELF는 무엇일까요?

게임 또는 애니메이션이나 소설에 나오는 엘프를 말하는 걸까요?

네, 하지만 여러분들이 생각하는 그런 엘프는 아니라 유감,


그렇다면 ELF에 대해 알아봅시다!

1999년 86open 프로젝트에 x86기반 유닉스 계열 시스템들의 표준 바이너리 파일 형식입니다.

ELF (Executable and Linkable Format)은 실행파일, 목적파일, 공유 라이브러리 그리고 코어 덤프를 위한 표준 파일 형식인데요.


음 그냥 간단히 윈도우에서의 .exe 파일과 비슷하다고 보시면 될 것 같습니다. 


ELF 포맷 구조를 보도록 하겠습니다.



그림판으로 그려서 못생긴 기분이 들지만...


대략 이렇게 생겨먹었습니다.

ELF파일은 ELF 헤더가 맨 앞에 위치하며, 프로그램 헤더 테이블과 섹션 헤더 테이블은 그 뒤에 위치합니다.


그렇다면 이제 elf 파일 형식을 직접 봐보도록 하겠습니다.



ELF Header View



ELF 파일 형식을 보기 위해서는 readelf 명령어를 이용합니다.

readelf 명령어 옵션에 대해 알아보고 싶으신 분은 아래의 링크를 참조바랍니다.

readelf - https://sourceware.org/binutils/docs/binutils/readelf.html


저희는 ELF Header를 보려고 하는 중이기 때문에

아래와 같은 옵션으로 넘겨줍니다.


-h

--file-header

Displays the information contained in the ELF header at the start of the file. 


$ readelf -h ./hello



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 headr 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 entry count

        Elf32_Half      e_shentsize;  // Section header table entry size

        Elf32_Half      e_shnum;  // Sectino header table entry count 

        Elf32_Half      e_shstrndx;  // Sectino header string table index

} Elf32_Ehdr; // x86


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 headr 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 entry count

        Elf64_Half      e_shentsize;  // Section header table entry size

        Elf64_Half      e_shnum;  // Sectino header table entry count 

        Elf64_Half      e_shstrndx;  // Sectino header string table index

} Elf64_Ehdr;  // x86_64


참고 : http://www.sco.com/developers/gabi/2003-12-17/ch4.eheader.html


Program Header table



프로그램 헤더 테이블은 ELF헤더의 e_phoff로 지정된 오프셋에서 시작하고 e_phentsize와 e_phnum으로 정해진 크기를 갖는 테이블입니다.

프로그램 헤더 테이블의 전체 크기는 e_phnum * e_phentsize byte 입니다.


지금 현재는 프로그램 헤더 테이블을 보려는 중이니 아래와 같은 옵션으로 넘겨줍니다.


-l

--program-headers

--segments

Displays the information contained in the file's segment headers, if it has any. 


$ readelf -l ./hello



프로그램 헤더는 다음과 같은 구조를 갖고 있습니다.


typedef struct {

Elf32_Word p_type;  // Segment type 

Elf32_Off p_offset;  // Segment file offset 

Elf32_Addr p_vaddr;  // Segment virtual address 

Elf32_Addr p_paddr;  // Segment physical address

Elf32_Word p_filesz;  // Segment size in file 

Elf32_Word p_memsz;  // Segment size in memory

Elf32_Word p_flags;  // Segment flags 

Elf32_Word p_align;  // Segment alignment 

} Elf32_Phdr;  // x86


typedef struct {

Elf64_Word p_type;  // Segment type 

Elf64_Word p_flags;  // Segment flags 

Elf64_Off p_offset;  // Segment file offset 

Elf64_Addr p_vaddr;  // Segment virtual address 

Elf64_Addr p_paddr;  // Segment physical address

Elf64_Xword p_filesz;  // Segment size in file 

Elf64_Xword p_memsz;  // Segment size in memory

Elf64_Xword p_align;  // Segment alignment 

} Elf64_Phdr;  // x86_64


세그 먼트 타입에는 다음과 같이 있습니다.


PT_NULL 0

PT_LOAD 1

PT_DYNAMIC                 2

PT_INTERP 3

PT_NOTE 4

PT_SHLIB 5

PT_PHDR 6

PT_TLS 7

PT_LOOS 0x60000000

PT_HIOS 0x6fffffff

PT_LOPROC         0x70000000

PT_HIPROC         0x7fffffff


참고 : http://www.sco.com/developers/gabi/latest/ch5.pheader.html



Section header table



이 곳은 Section&Segment 오프셋이나 크기, 타입 등의 정보를 담고 있는 테이블입니다.

이 테이블은 오브젝트 파일 같이 relocation 가능한 ELF 오브젝트에는 반드시 있어야되는 녀석입니다.


지금 현재는 Section header table을 보려는 중이니 아래와 같은 옵션을 넘겨줍니다.


-S

--sections

--section-headers

Displays the information contained in the file's section headers, if it has any. 


$ readelf -S ./hello



섹션 헤더는 다음과 같은 구조를 갖고 있습니다.


typedef struct {

    uint32_t   sh_name;  // Section name

    uint32_t   sh_type;  // Section Type

    uint32_t   sh_flags;  // Section Flag

    Elf32_Addr sh_addr;  // Section virtual address 

    Elf32_Off  sh_offset;  // Section offset

    uint32_t   sh_size;  // section size

    uint32_t   sh_link;  // section index link

    uint32_t   sh_info;  // extra information

    uint32_t   sh_addralign;  // section alignment constraints

    uint32_t   sh_entsize;  // hold a table of fixed-sized

} Elf32_Shdr;  // x86


typedef struct {

    uint32_t   sh_name;  // Section name

    uint32_t   sh_type;  // Section Type

    uint64_t   sh_flags;  // Section Flag

    Elf64_Addr sh_addr;  // Section virtual address 

    Elf64_Off  sh_offset;  // Section offset

    uint64_t   sh_size;  // section size

    uint32_t   sh_link;  // section index link

    uint32_t   sh_info;  // extra information

    uint64_t   sh_addralign;  // section alignment constraints

    uint64_t   sh_entsize;  // hold a table of fixed-sized

} Elf64_Shdr;  // x86_64


참고 : http://linux.die.net/man/5/elf


ps. 오 세상에... 여기까지 귀찮게 다 작성했더니 마지막에 섹션 헤더에 대해 구글링을 하다가 엄청난 자료를 발견하였다. 무려 한글로 된 ... 고로 첨부합니다.


ELF File Format.doc


이렇게 리눅스에 ELF 포맷에 대해 알아보았습니다.

그렇다면 이제 어떻게 main을 불러오는지 알아보도록 하겠습니다!

오 할렐루야 이제야...



I want go to main() !



먼저 바이너리의 엔트리 포인트를 먼저 보도록 하겠습니다.



Entry point address는 0x8048320 이라고 합니다.

이제 이 주소에 뭐가 있는지 디버깅을 하면서 찬찬히 들어가보도록 하겠습니다.



Entry point address는 _start함수의 주소였습니다.

그렇다면 프로그램이 실행되면 _start 함수가 먼저 실행이 되겠네요.

_start 함수가 실행되면 스택 프레임은 아래와 같습니다.


Stack Top

0x804841d

 ...

esi

ecx

0x8048440

0x80484b0

edx

esp

eax


현재 이렇게 스택 프레임이 생겨먹었습니다.

여기서 그렇다면 궁금증이 몇가지가 생기셨을꺼라 생각됩니다.

뭐 저만 생긴 걸 수도 있지만요.


  1. 스택에 push된 저 값들은 무엇인가요?
  2. _start에서 0x8048310을 호출하는데 뭐지?



스택에 push된 저 값들은 무엇인가요?



먼저 스택에 push된 값들은 아래와 같습니다.

0x80484b0, 0x8048440, 0x804841d


0x804841d : main() 함수의 주소

0x8048440 : __libc_csu_init() 함수 주소

0x80484b0 : ??


들이라는 것을 알 수 있었습니다.



_start에서 0x8048310를 호출하는데 뭐지??



음 뭐 사실 뭐하는 녀석인지는 옆에 함수 명이 보이기 때문에 크게 생각하실 것은 없습니다.

0x0804833c <_start+28>: call   0x8048310 <__libc_start_main@plt>

__libc_start_main@plt 를 호출합니다.


plt를 호출하는 이유는 __libc_start_main를 처음 호출하는 것이라 바로 호출하지 않고 plt에서 got를 다이나믹 링킹하고 호출을 합니다.


아니 그러면 __libc_start_main은 뭘까요?

깊숙히 들어가보도록 하겠습니다.


  1. __libc_start_main은 뭔가요?


__libc_start_main은 뭔가요?



음...

현재 이 흐름은 libc의 손에 달려있습니다...!

__libc_start_main함수는 libc.so.6 안에 있거든요!



glibc 소스 코드에서 __libc_start_main를 발견할 수 있습니다.

코드는 아래와 같습니다!


int __cdecl __libc_start_main(int (__cdecl *main)(int, char **, char **), 

int argc, 

char **ubp_av, void (*init)(void), 

void (*fini)(void), 

void (*rtld_fini)(void), 

void *stack_end)



__libc_csu_init 호출



_init 호출



호로록



이 부분에선 gmon 프로파일링 시스템 초기화가 필요하다면 gmon_start를 호출하여 초기화합니다.




그 뒤에 다시 __libc_start_main으로 돌아오고 저희가 만든 main을 호출하게 됩니다.



이상입니다.


뭔가 덤성덤성 뛰어넘은 부분이 많지만 ...

도움이 되었으면 좋겠습니다!


길고 긴 글을 읽어주셔서 감사합니다.







'0x20 Security > 0x21 System' 카테고리의 다른 글

About LD_PRELOAD  (0) 2016.07.20
Use After Free (UAF)  (1) 2016.07.13
System Hacking :: Memory Protection  (0) 2016.06.28
[SSA] 시스템 해킹 스터디 6주차  (349) 2015.11.09
[SSA] 시스템 해킹 스터디 5주차  (155) 2015.11.05

Memory Protection Techniques

writen by hubeen




Linux 환경에서의 메모리 보호 기법에 대해 작성하려고 합니다.

/*이 글은 틀린 정보가 있을 수도 있으며 글의 맥락이 축구공처럼 뻥뻥 돌아다닐 수도 있습니다.*/


일단 메모리 보호라는 개념을 알아보도록 하겠습니다.



네, 그렇습니다.

운영체제에서 실행되고 있는 프로세스에게 메모리를 할당을 해주게 되는데.

할당되지 않은 영역의 메모리에 접근을 하는 것을 막기 위해 있는 것이 메모리 보호 기법의 주된 목적입니다.


이러한 메모리 보호 기법들은 짱짱한 분들이 해커들의 공격을 막기 위해 열심히 노력을 해서 완성이 된 녀석들입니다. 




1. ASLR (Address Space Layout Randomization)



이 ASLR은 2001년 즈음에 Linux PaX 프로젝트에서 처음으로 "ASLR" 개념을 설계하고 2002년부터 커널 스택 무작위 배치를 구현을 하였다고 합니다.

( https://en.wikipedia.org/wiki/Address_space_layout_randomization )


이 녀석은 말 그대로 주소 공간을 랜덤한다. 입니다.


흔히 Buffer Overflow 공격으로부터 보호하기 위해 Stack, Heap, Libc, 프로세스 주소 공간을 포함하는 데이터 영역 위치들을 랜덤하게 변경하여 메모리의 특정 악용 함수 주소를 랜덤하게 하여 공격을 방지를 할 수 있게 하는 녀석입니다.


실제로 이 ASLR이 작동을 하는지 눈으로 확인을 하도록 하겠습니다.


cat /proc/self/maps 명령을 입력을 하여 직접 확인해보도록 하세요! 



앙 바뀐띄~


이 ASLR이 적용이 되있는지 안되있는지 확인하는 방법은 아래와 같습니다.


cat /proc/sys/kernel/randomize_va_space 


0 : No randomization. Everything is static.

1 : Conservative randomization. Shared libraries, stack, mmap(), VDSO and heap are randomized.

2 : Full randomization. In addition to elements listed in the previous point, memory managed through brk() is also randomized.


그렇다면 이 ASLR을 해제를 해보도록 하겠습니다.


echo 0 > /proc/sys/kernel/randomize_va_space


보시는 것과 같이 주소 값이 바뀌지 않는 것을 볼 수 있습니다.


#include <stdio.h>

#include <stdlib.h>


int main()

{

char *heap = NULL;


heap = (char*) malloc(50);


printf("[Heap Address] : %p\n", heap);


return 0;

}


간단한 코드로 주소를 출력을 해보도록 하겠습니다.

gcc -o a a.c



왼쪽은 ASLR을 끈 상태이며 오른쪽은 ASLR이 적용되있는 상황입니다.


보시는 것과 같이 ASLR이 적용되있지 않을 때에는 힙의 주소 값이 일정하지만 ASLR이 켜져있을 경우에는 주소 값이 랜덤하게 바뀌는 것을 볼 수 있습니다.




2. DEP (Data Execution Prevention)



DEP라는 녀석에 대해 알아보도록 하겠습니다.



네, 그렇습니다!

흔히 Overflow로 인해 메모리 영역에 훼손이 발생하더라도, Data 영역에서 실행(Execution)을 방지함으로써 공격을 방어하는 기법입니다.


고로 해커가 Buffer Overflow 공격으로 ret을 스택영역에 놓은 쉘코드 주소로 변경했을 때에 DEP가 적용이 안되있는 경우에는 그대로 쉘코드가 실행이 되어 쉘을 딸 수 있겠지만, DEP가 적용이 되어있다면 실행 권한이 없으므로 쉘코드가 실행되지 않고 종료가 됩니다.


아래는 포스트 할 때 사용한 예제 코드 입니다.


#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <unistd.h>


pid_t getpid(void);

pid_t getppid(void);


/*

        The Lord of the BOF : The Fellowship of the BOF

        - gremlin

        - simple BOF

*/


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

{

    char buffer[256];

    pid_t pid;

    char cmd[25];

    if(argc <2){

        printf("argv error\n");

        exit(0);

    }

    strcpy(buffer, argv[1]);

    printf("%s\n", buffer);


    if ((pid=fork()) == -1){

        printf("fork failed\n");

    }

    else if(pid != 0){

        printf("getpid : %ld\n", getpid());

        sprintf(cmd, "cat /proc/%d/maps", getpid());

        system(cmd);

    }


}


이 예제 코드는 간단한 bof 취약점이 존재하는 프로그램입니다.

코드가 익숙하시다고요?

넵, 맞습니다.

LOB gate 문제 코드거든요!


그렇다면 컴파일을 하고 보게 되면?



보시는 것과 같이 rw-p 로 스택에 실행 권한이 없는 것을 볼 수 있습니다.


그리고 DEP 해제를 하고 보도록 하겠습니다.


gcc 컴파일을 하실 때에 dep를 해제를 하시려면 -z execstack 옵션을 넘겨주시면 됩니다.


DEP 해제 : gcc -z execstack a.c -o a



역시 보시는 것과 같이 rwxp로 스택에 실행권한이 있는 것을 볼 수 있습니다. 

이렇게 바이너리에 DEP가 걸려 있지 않는다면 bof를 통해 ret을 쉘코드가 있는 스택 주소로 변조하면 쉘코드가 잘 실행이 되겠죠?




3. ASCII-Armor



다음으로는 ASCII_Armor 보호 기법에 대해 알아보도록 하겠습니다.

이 녀석은 뭘 하는 녀석일까요?


이름 그대로 "아스키 철갑옷" 이름 값하는 녀석입니다.

라이브러리를 공유 라이브러리 영역에 올리지 않고 텍스트 영역의 16MB 아래의 주소에 할당하는 녀석입니다.


이 ASCII-Armor가 적용이 되게 되면 라이브러리 영역 주소의 최상위 1byte가 \x00으로 됩니다.



보시는 것과 같이 최상위 1byte가 \x00 으로 되잇는 것을 볼 수 있습니다.


이렇게 \x00 은 NULL로 인식이 되고 RTL같은 체이닝 공격을 할 수 없게 되어 익스플로잇을 하지 못하게 만드는 녀석입니다.




4. SSP (Stack Smashing Protector)



마지막으로 SSP입니다.

이 Stack Smashing Protector 는 gcc 4.1버전 부터 지원하는 Buffer Overflow 방어 기법입니다.

4.1 버전 이상인 gcc에서 아래와 같은 코드를 컴파일 한 뒤에 BOF를 발생시켜보겠습니다.


#include <stdio.h>

#include <string.h>


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

        char buffer[5];


        strcpy(buffer, "0123456789abcdef");

        return 0;

}


컴파일 하고 실행을 하게 되면 다음과 같이 *** stack smashing detected ***: ./b terminated 라는 문자열이 나타나는 것을 볼 수 있습니다.



gdb로 까보도록 해보겠습니다.



초록색 박스 안에서는 카나리 값을 스택에 저장하는 부분이며,

주황색 박스 안에서는 카나리 값이 변조되었는지 안되있는지 체크하는 루틴입니다.



esp+0x1c에 카나리가 들어간다고 합니다.


그렇다면 아마도 0x450b8200이 카나리 값이겠네요 (웃음)



어이쿠 카나리 값을 "5"로 덮어버리고 말았어요!


이 부분에서 카나리 값이 없어지고 카나리 체크하는 루틴에서 걸려서 보호를 하게 됩니다.


이 카나리는 버퍼와 sfp의 사이에 숨어있어요!


buf[5] | dummy[?] | canary[4] | sfp[4] | ret[4]


이 SSP를 해제하는 옵션은 아래와 같아요.


SSP 해제 : gcc b.c -o b -fno-stack-protector




길고 멍청한 글을 읽어주셔서 감사합니다.





'0x20 Security > 0x21 System' 카테고리의 다른 글

Use After Free (UAF)  (1) 2016.07.13
How main() is executed on linux  (2) 2016.07.04
[SSA] 시스템 해킹 스터디 6주차  (349) 2015.11.09
[SSA] 시스템 해킹 스터디 5주차  (155) 2015.11.05
[SSA] 시스템 해킹 스터디 4주차  (31) 2015.11.05


코드게이트_2016_Write_Up_문승현.pdf




Hello, 

i sense that didn't written in Writeup

hmm... but have name in the file name

it's okay 

maybe lol..

'0x20 Security > 0x25 Write-Ups' 카테고리의 다른 글

[ReverseLab 스터디] [Abex's CM] Abex 1~5  (0) 2016.07.11
[Pwnable.kr] dragon - 75 pt  (0) 2016.06.25
[Pwnable.kr] cmd1 - 1 pt  (0) 2015.10.07
[Pwnable.kr] lotto - 2 pt  (0) 2015.10.07
[Pwnable.kr] blackjack - 1 pt  (0) 2015.10.06

이번 주에는 취약점 검사 방법의 변천사와 현재 트렌드, 핀툴에 대하여 깊게 파고드는 시간을 가졌다.


사실 취약점 검사 방법의 변천사, 현재 트렌드 들은 후반 부 밖에 듣지 못하였다...


시간 계산을 잘못해서 너무 늦게 나와서 20분~30분을 늦어버렸다...


취약점 찾는 기법의 변천사


소스코드 오디팅

[Vulnerable functions]

-> strcpy , memcpy , gets , fges , printf


api usage 탐색

-> cat program.c | grep strcpy


후킹

-> Vulnerable functions에 유저 인풋이 들어가는지 체크


퍼징

-> valid한 유저인풋을 bit flipping이나 구조를 약갘씩 변경해 주면서

   다양한 code path를 탐색하면서 특정 값이 취약한 루틴에 들어갈 때

            발생하는 취약점을 찾는 확률적인 취약점 검사 기법


taint progation analysis

-> 유저인풋의 data flow를 죄다 트래킹해서

   destination, soruce, instruction pointer 등에 영향을 주는 포인트를 알아낸

   tainted destination

   tainted source

   tainted instruction pointer


-> taint == 오염시키다

-> 모든 취약점은 유저인풋으로 나온다

-> hwp.exe exploit.exe

   hwp.exe -> CreateFile | OpenFile(“exploit.exe”)

   -> ReadFile(hFile, buf, 65535); -> tainted : buf

   -> 표를 뜻하는 opcode ==> tainted

  handler_table[opcode](section_data)

  -> call handler_table(, eax, 4) ==> eax : tainted

  -> handler_table + eax*4


dynamic binary instrumentation

 -> Pintool , libdft


symbolic executionn —> KLEE &  constraint solver —> z3

          -> path explosion

 -> scalable 하지 않다는 단점 존재


concolic execution

—> concrete execution + symbolic execution

conc(rete) + (symb)olic


path analysis | 1day analysis

—> binary | source code diffing


vulnerability extrapolation —> fabian yamaguchi

코드 유사도 비교를 통한 취약점 분석

key concept:

기존에 취약하다고 알려진 코드 패턴이 다른 코드베이스에서도 발견이 될 경우 그 코드도 취약할 가능성이 높음

jaccard similarity coefficient algorithm

hausdorff similarity


나는 symbolic executionn 에서 부터 들어서 위에 부분을 자세히 모른다..


젠장 ㅠㅠ;


그나마 뒷 부분에서 알아들은 내용이라곤 코드 유사도 비교를 통한 취약점 분석 부분이였다.


예~~전에 문장 유사도 알고리즘을 발견해서 보면서 구현하고 깃허브에 올린 게 기억이 나는데.


이것은 여러 방법들이 있지만 cd80니뮤 말씀하신 방법은 이러하였다.


(잘 기억이 안나지만 이러했던걸로 기억남..)


하이레벨 코드로 변환해서 그 코드들을 함수에 임의로 지정한 숫자들을 그래프로 표시하여 


원본 소스와 비교할 소스와 그래프로 비교하여 유사도를 확인한다고 했다.


꽤 흥미로웠었다.


그 뒤에는 핀툴에 대해 jinmo123니뮤 가르쳐주셨는데...


솔직히 알아들은게 없었다고 해도 다를 께 없었다..


ㅂㄷㅂㄷ;;


뭐 일단 대충 어떤식으로 동작을 하는 지 그 부분까지는 이해했는데.


그 뒤부터는 귀로 들어오질 않았다...ㅂㄷㅂㄷ;


설치부터 막혀버린 나의 상황이 너무 안타깝다..


뭐 그래도 이번 주는 시간이 워낙 많으니 삽질할 시간이 많아서 


구글링을 통해 사용법을 터득할 예정이다.


터득한 뒤에 자세히 정리를 하여 올려봐야겠다.


일단 설치부터... [a-z] 까지 최대한 적어보자!







그러하다.


이 5주 차에서는 4주 차 과제를 제일 먼저 풀은 2명이 풀이를 발표하게 되었었다.

그러나 1분은 늦잠을 자셔서 못오셨다고..

그렇게 pork 풀이와 한 문제의 풀이를 듣고 5주 차 풀이 발표자 분이 과제를 내게 되었다.


이번 과제는 layer7 ctf 2015에서 나온 문제를 풀기 였다.



문제는 이거다.


무려 이 대회에서 4명밖에 못 풀은 문제다.


사실 이 문제는 건드린 시각은 과제 내용을 들은 일요일이였는데.

월~수가 수학여행이라 과제를 건드릴 시간이 없어서 오늘 적는다...ㅎ;


사실 월~수 중에 숙소에서 과제를 하려고 했는데...

심하게 피곤해서 숙소 도착하면 정신도 못차린 채 쓰러졌다고 ... 한다.




그러하다.

끝.

ROP 심화 부분이라고 한다.


ROP 문제들을 풀면 된다.



풀어야한다...

이번 3주차에서는 ROP를 다뤘다.


ROP(Return Oriented Programming) 

: BOf + 여러가지 ①미티게이션 우회할 수 있는 테크닉


① 미티게이션 : 취약점을 막는 것이 아니라 취약점을 통한 익스플로잇을 막는 것.

ex) strcpy(buf, argv[1])


이러한 미티게이션은 4~5개 정도 있으며 가장 대중 적으로 사용되는 것은 3개가 있습니다.


  1. ASLR
  2. NX
  3. SSP

ASLR(Address Space Layout Randomization)
: 일반적인 bof의 흐름에서는 스택에 쉘코드를 적재해두고 거기로 리턴하는데 스택의 주소를 프로그램 실행 시마다 다르게 합니다.

ASLR 확인 방법 : /proc/sys/kernel/randomize_va_space 값을 확인.

 0

 걸려있지 않음

반 걸려있음 

다 걸려있음 


해결 방법 

1. 스택, 힙, 라이브러리 그렇기 때문에 랜덤화해주지 않는 정적인 주소 값을 찾아서 이용하자.

2. x86 아키텍쳐에 한에서 ulimit -s ulimited 를 사용하면 일시적으로 ASLR이 해제된다. 왜 이러는 것인가?



NX(Non eXecutable stack)

: Write 권한, Executable 권한을 동시에 부여하지 않는다.


해결방법

1. ①RTL을 이용해서 우회를 한다.

① RTL(Return To Libc) : eip를 변조하여 쉘코드를 실행하는 것이 아닌 직접 라이브러리 주소로 점프해 system("/bin/sh") 이나 명령을 다이렉트로 실행한다.


ex) buf -> |    |  SFP  |  라이브러리에 있는 시스템 주소  |  asdf  |  /bin/sh  |


라이브러리에 binsh 문자열이 있다.

아니면 스택이나 정적 영역에 binsh 넣고 문자열을 만들어 주도록 하자.


라이브러리 영역에서 시스템 함수가 내부적으로 호출 될 때 /bin/sh -c 

system("ls"); == /bin/sh -c ls


Tip1. system 주소 알아내는 방법 : p system

Tip2. /bin/sh 주소 알아내는 방법 : find &system, -999999999, "/bin/sh"


|  'A' *68  |  system  |  asdf  | system의 first argv[]  |

   &system   asdf     &/bin/sh 


system을 포함한 다른 함수를 호출 할 때 그 바로 다음 값이 그 함수의 리턴 값이 된다. 왜 이러는 것인가?



PLT, GOT ::


PLT : 바이너리 영역에 있는 건 바이너리에서 사용되는 함수들의 정보만 따로 모아두고 사용하게 편하게 한다.

GOT :  런타임시 라이브러리 주소를 불러와서 저장을 하는데 그게 실제로 저장되는 공간이다.


PTL에서는 호출이 되면 일련의 과정을 통해 GOT에 라이브러리 주소를 불러옴.

CALL PTL :  PLT는 GOT에 저장된 값으로 JMP를 하는데, 이게 처음이면 기본적으로 GOT에는 PLT+6이 저장되있음.

PLT+6에서는 dynamic linker 라는 것을 사용해서 GOT에 라이브러리 주소를 로딩하는데.

이게 처음이 아니면 그냥 GOT로 점프를 할 때 라이브러리 주소가 이미 로드 되어 있으므로 바로 라이브러리 호출.


PLT, GOT의 유용성


1. PLT, GOT는 바이너리 영역에 저장되어 있기 때문에 정적이라 ASLR의 영향을 받지 않는다.

2. PLT를 호출하는 것만으로 바로 라이브러리를 가져다 쓸 수 있다.

3. 바이너리에서 사용하는 모든 함수들을 사용할 수 있다.

 




이 스터디에서는 가르쳐주신 걸 모든 내용을 문서화하면 이쁨을 받는다고 해서 작성했3

그리고 나중에 내가 쓴 걸 다시 돌아보면서 많은 생각을 하게 만들 것이다.


1주 차에서는 어셈블리어 공부, 어셈블리어 프로그래밍, 핸드레이를 진행하였다.


어셈블리어에는 어마어마한 레지스터들이 있다는 것을 알게 되었다.

하지만 우리가 쓰는 레지스터들은 보통 8개 정도 쓰인다고 한다.


범용 레지스터 (General Resgister)


EAX (AX, AH, AL) - 누적 연산기, 곱셈과 나눗셈 연산에서 자동으로 사용한다.

EBX (BX, BH, BL) - 베이스 레지스터, 특정 주소를 지정한다.

ECX (CX, CH, CL) - 카운터 레지스터, 반복 카운터로 사용됨.

EDX (DX, DH, DL) - 데이터 레지스터, 입 출력 연산에서 반드시 간접 주소로 지정에 사용됨.

EBP (BP) - 베이스 포인터, 스택의 데이터에 접근하기 위해 사용됨

ESP (SP) - 스택 포인터, 스택 최상부의 오프셋을 가리킨다.

ESI (SI) - 읽기 인덱스, 데이터 조작, 복사 시에 소스 데이터의 주소가 저장됨

EDI (DI) - 쓰기 인덱스, 복사 시에 목적지의 주소가 저장됨

EIP - 명령어 포인터 레지스터, 실행할 다음 명령어의 주소를 포함

EFLAGS - CPU의 동작을 제어하거나 CPU 연산의 결과를 반영



AH, AL 중 H 는 High 을 뜻하고 L는 Low를 뜻한다.


이제 어셈블리어에 레지스터에 대해서는 잘 끄적인 것 같으므로 다음으로 넘어가보자.


일단 첫 번째로 write를 구현을 해보라고 했다.


이에 대해서는 시스템 콜을 사용해야한다.


시스템 콜이란 커널의 자원을 사용자가 사용 할 수 있도록 만들어 놓은 고급 API? 같은 것이다.


사용자 프로세서가 시스템 콜을 요청하면 제어가 커널로 넘어간다.

커널은 내부적으로 각각의 시스템 콜을 구분하기 위해 기능 별로 고유 번호를 할당해 놓는다.

이것이 바로 시스템 콜 넘버라고 한다.


그에 비해 라이브러리 함수는 사용자들이 많이 사용할 것 같은 기능들을 미리 만들어놓은 것이다.

하지만 이 라이브러리 함수는 사용자 모드에서 실행된다.


그렇다면 우리는 넘겨줘야되는 것은 (system_call, fd, &buf, nbyte) 가 되시겠다.


이제 어셈블리어 코드를 작성해보자.


어셈블리어에서는 지시자들이 있다.


아직 지금까지 내가 써본 지시자들은 .globl, .string 뿐이다.


이에 대해 설명하자면 

.globl (global) <Symbol> : 심볼을 외부 참조가 가능하게 한다.

.string : 오브젝트 파일에 문자열을 복사(?) 

.data : 초기화된 데이터 섹션




지금 알려준 것은 함수에서라고 한다.


카운트를 0으로 초기화해준 뒤에


가져온 eax 를 바이트 단위로 bl 8bits 레지스터에 넣는다. (?) 맞나?


그 뒤에 바이트 단위로 %bl 과 null을 비교를 하고


같을 경우 끝으로 점프한다


아닐 경우 ecx ++, eax++ 하며 루프를 돈다.


※ 아 (%eax) 를 하면 메모리 주소의 값을 의미한다고 한다.

ex)

%eax = 0xffffe008


Memory :

0xffffe008: 0xabcdef


%eax 는 0xffffe008 

(%eax) 는 0xabcdef 

라고 한다.


그렇게 엔딩에 가고 카운트를 eax 에 넣는다.

eax 레지스터는 리턴 값으로 자주 쓰이는 것 같아 보인다.



_M#]


위에는 전에 썻던 글인데 

왜 이렇게 썻는지 모름... ㅂㄷ;;


고로 버리기에는 아까우니 요약에다가 쑤셔넣기 !


어여튼 어셈 코드는 이러하다.


입력을 받고 esi 에 넣는다.


글자 길이를 카운트를 책임질 ecx 를 0으로 초기화해줌


(esi) 글자를 1글자 씩 bl에 넣음


cmpb로 null과 1글자 씩 비교함


je 같을 경우에 .end 로 점프


아닐 경우 ecx ++ 와 다음 글자를 비교하기 위해 esi ++


그리고 loop 로 다시 점프


그리고 null일 경우 end 로 가서 

eax에 ecx를 넣고 끝


함수는 보통 eax로 리턴을 한다고 한다. (정확한 것은 아님)


_M#]





+ Recent posts