본문 바로가기

Let's Study/Basic System Hacking Technique

3. 핸드레이 & 기본 어셈블리 명령어


system_hacking3.pdf





Shayete입니다. 3번째 강의는 어셈블리 핸드레이에 대해 포스팅하도록 하겠습니다.



 어셈블리 핸드레이는 어셈블리어를 C언어로 복원시키는 걸 의미합니다. C에서 어셈블리어로도 복원시킬 수 있다면 베리 나이스구요. C에서 어셈으로 복원하는 건 우선 바이너리를 많이 뜯어보고 어셈이 눈에 익은 다음에 시도하도록 합니다.


핸드레이를 하려면 기본적인 어셈블리어 명령어를 알아야 합니다. 



1. Basic Assembly Instruction

































































# cmp a ,b 부분의 assembly가 at&t형식일 때에 위처럼 zf, cf가 셋팅됩니다. intel 형식일 때는 반대로 봐주세요. 

혹시 flag 확인이 헷갈리시다면 info reg 에서 eflags 부분을 계산기에 넣고 이진수로 바꾸신 다음

 cf 와 zf 부분이 맞게 세팅되어있는지 확인해주시면 됩니다.

그냥 헷갈리시면, cf == 이진수 2의 0승부분, zf == 이진수 2의 6승 부분입니다.


eflags 참고사이트 : http://netstat.tistory.com/27 




위의 어셈블리어들은 가장 많이 나오고 반드시 알아야되는 기본 명령어들입니다. push와 pop은 2번째 강의해서도 말했듯이 스택은 위에서 아래로 자라기때문에 push로 값을 밀어넣으면 위에서 아래로, 즉 -가 되어 esp - 4가 됩니다. esp는 스택포인트의 끝을 가르켜야 하기 때문에 값을 밀어넣으면(push) 밀어넣은 만큼 -4 해서 계산해줍니다. 마찬가지로 pop은 스택에서 값을 빼오기 때문에 값을 가져오고(pop) 가져온 만큼 +4해서 esp를 계산해줍니다.


cmp, je, jne에 나온 ZF (zero flag)는 지금 깊게 아실 필요는 없고 cmp는 비교하는 명령어, je, jne는 점프 명령어 정도로만 알아두시면 되겠습니다.




# 알고가면 재밌는 지식

 nop 이라는 명령어는 아무것도 하지 않는 명령어입니다. 근데 저게 왜 있을까요? 아무것도 하지않는데 존재이유가 궁금하지요?


바이너리 내의 어셈블리 명령어들은 각각 몇 byte씩 크기가 할당되어 있습니다. 컴퓨터에서는 이 바이너리를 메모리에 적재하고, 처리해야하는데  32bit 시스템의 경우 4바이트씩 끊어서 명령어들을 처리합니다. (64bit도 그런가? 제가 64bit는 tiny pe로 어셈코딩 안해봐서 모르겠네요 ㅠ 아마 64bit는 8바이트일 겁니다. 시스템의 Program Counter에 영향을 받는 걸로 알고있는데 시스템마다 다르니 주의해주세요.)


 컴파일러는 특정 소스를 컴파일할 때에 함수들이 차지하는 byte를 다 계산해서 메모리에 빈공간이 없도록 nop ( 1byte ) 명령어들을 중간중간에 넣어주는데, 빈공간이 생길 경우 시스템에서 바이너리를 읽을 때 오류가 나서 읽지 못하기 때문에 nop으로 메모리를 메꿔줍니다. 


비유를 들자면 테트리스를 하는데 중간 중간에 구멍이 하나씩 존재하면 블럭들이 다 터지지 않고 쌓이다 죽게되죠. 이건 시스템이 바이너리를 제대로 못읽었음을 의미하구요, 빈 구멍 없이 공간을 메꾸게 되면 블럭들이 터지고 점수가 오르죠?(명령어들을 처리함) 시스템도 비슷하게, 빈 공간을 채우면 (테트리스 블럭을 일렬로 다 쌓았을 때, 즉 메모리에 빈공간없이 명령어들을 적재했을 때) 그 부분이 실행된다고 생각하시면 됩니다.


마찬가지로, 이미 할당된 메모리값보다 더 크게 메모리값을 쓸 수 없기 때문에 Code injection이라거나 특정 함수를 변경할 때에 그 함수의 크기보다 더 큰 함수를 사용할 수 없습니다. 사용하게 된다면 밑의 코드들도 영향을 받기 때문에 밑의 함수들까지 다 처리해줘야되요.


 이런 기능을 하는 nop을 시스템 해커들은 nop sled라는 기법으로 연구하여 사용하고 있습니다. 아무 기능도 하지 않기 때문에 레지스터 값이 변경될 일이 없고 아무 하는 일 없이 다음 명령어들을 부르다가 (nop들을 쭉 따라내려가다가) nop sled 뒤에 심어놓은 shellcode를 실행하게 되는 거지요.






2. Handray를 시작할 때 알아두면 좋은 것


이제 핸드레이를 시작할 텐데 알아두면 좋은 지식들을 올려봅니다.

















































ebp + 8 이상의 값은 함수 인자를 뜻하는데 ebp == save frame pointer 와 연관지어 생각해보시면 간단합니다. sfp 다음 return address가 있고 그 밑에는 함수 인자들이 존재합니다. 즉, ebp = sfp, ebp + 4 = return address, ebp + 8 ~ = 함수 인자들이 되는 거지요.

ebp - 4, esp + 8은 지역변수를 나타내는데 컴파일러마다 처리해주는 게 달라서 그런갑다 하고 알아두시면 됩니다.

for 문의 전형적인 문장은 인자를 초기화하고 for (int i = 0;) 인자를 비교하며 for (; a <= b) 인자에 대한 연산을 수행합니다. for (;;i++)


 딱 이정도만 알아두셔도 핸드레이 할 때에 매우 유용합니다. 어셈블리어는 단순해서 명령어들을 쫒아가면서 분석하다보면 이게, 핸드레이하는게 어려워서 시간이 오래걸리는 게 아니라 그냥 노가다구나 싶습니다.







3. Handray




간단한 c 코드를 핸드레이하는 과정입니다. 위의 두 줄 push ebp, mov ebp, esp는 함수 프롤로그로서 이전 함수의 ebp를 스택에 저장하고 main 함수 스택포인트를 따로 설정합니다. 


 뒤의 mov DWORD PTR [ebp - 0x4], 0xa,

        mov DWORD PTR [ebp - 0x8], 0x14

각각 첫번째 지역변수 v1 = 10, 두번째 지역변수 v2 = 20 을 담고 있고 이 변수들을 eax, edx 레지스터들에 넣어서 더하고 있습니다. (v2 += v1)

그 후 더한 값을 mov DWORD PTR [ebp - 0xc]에 넣고 있는데 v3 = v2 + v1임을 알 수 있습니다.

esp + 0x4에 eax를 넣는걸 보니 printf의 두번째 인자는 v3이고 printf의 첫번째 인자는 0x80484f0에 있는 값인 "plus : %d\n" 즉

printf("plus : %d\n", v3)이 되어 plus : 30이 출력됩니다.


 지금 스크린 샷과 설명이 약간 다를건데 위의 스크린 샷은 편의상 v2로 했습니다. v3 = v2 + v1이나 v2 += v1, printf v2나 똑같기 때문에 간략하게 적어놨습니다.






 두번째도 간단하게 c코드로 짜봤습니다. 4번째 줄에 뜬금없이 ebp + 0xc의 값을 eax에 넣죠? 위에서 말씀드렸듯 ebp + 0x8 이상의 값은 함수 인자를 의미합니다. 즉 main에서 함수의 두번째 인자는 int main(int argc, char *argv[])인, argv[0]가 ebp + 0xc가 되는 겁니다. 그 밑 5번째 줄 add eax, 0x4는 

argv[0] + 4 --> argv[1]이 되는 과정이구요. 각각의 포인터변수는 공간을 4바이트씩 할당받기 때문에 4를 더하면 그 다음 위치인 argv[1]을 가리킵니다.

함수가 call 되고 나면 함수의 return 값이 eax에 들어가는데 call strcmp@plt 보이시죠? strcmp 로 비교를 한다음 문자열이 맞으면 0을 반환하여 eax에 저장될 겁니다.

 그 밑의 test eax, eax.   eax값이 0인지 아닌지 판별하는 명령어인데 만약 eax가 0이라면 (argv[1]의 문자열이 "5678" 이라면) zero flag가 1로 셋팅되고 jne 점프문을 거치지 않고 바로 밑의 puts() 함수를 출력하게 됩니다. ("Correct!")


이렇게 어셈블리 핸드레이는 한줄 한줄씩 보면서 C로 복원하는 과정입니다. 어셈블리어가 눈에 익을 쯤에 IDA-pro 툴을 쓰시면 더욱 좋구요.





4. Use Stack example Swap function


 이 부분은 간단하게 메인 안에 스왑함수를 구현해서 실제로 스택에 값이 어떻게 쌓이는지, 어셈블리 명령어는 어떻게 동작하는지 설명을 해드리려고 작업좀 했습니다. 아래 동영상들은 main1, main2를 실행하다가 swap함수로 넘어가고 다시 main3 으로 넘어가는걸 표현했습니다. pdf자료와 병행해서 보시면 더욱 좋습니다.



주황색 네모는 main1 , 녹색은 main2, 녹색 네모 안의 call swap은 swap, 파란색 네모는 main3입니다.

(티스토리는 파일제한이 10mb라 동영상을 파일로 올릴 수가 없네요. ㅠㅠ)













5.  Debugging with PEDA


  제가 애용하는 peda 플러그인?입니다. gdb를 아주 보기 간편하게 만들어주는데 설치방법은 제 티스토리 찾아보시면 있습니다.


  peda 설치 : http://shayete.tistory.com/30







6. HandRay 과제 동영상