어셈블리 핸드레이에 대해 공부하고 싶어서 구글링한 결과
출처 : https://shayete.tistory.com
3. 핸드레이 & 기본 어셈블리 명령어
Shayete입니다. 3번째 강의는 어셈블리 핸드레이에 대해 포스팅하도록 하겠습니다. 어셈블리 핸드레이는 어셈블리어를 C언어로 복원시키는 걸 의미합니다. C에서 어셈블리어로도 복원시킬 수 있
shayete.tistory.com
여기에 자세하게 설명되어 있어 많이 참고했다.
1. 어셈블리 핸드레이 (Assembly handray) 란 무엇인가?
어셈블리 핸드레이는 어셈블리어를 c언어로 복원시키는 것을 의미합니다.
즉, 핸드레이를 하려면, 기본적인 어셈블리어 명령어를 알아야 합니다
2. 기본적인 어셈블리어 명령어 (intel 형식)
push와 pop은 스택은 위에서 아래로 자라기 때문에 push로 값을 밀어넣으면 위에서 아래로, 즉 -r가 되어 esp -4가 됩니다. ( 메모리 구조 글 참고 ) esp는 스택포인트의 끝을 가르키고 있어야 하기 때문에 값을 밀어넣게 되면 밀어넣은 만큼 4를 빼서 계산해 줍니다.
마찬가지로 pop은 스택에서 값을 빼오기 때문에 값을 가져오고 가져온만큼 +4를해서 esp를 계산합니다!
nop이란 명령어는 아무것도 하지 않는데 대체 왜 존재하는가?
바이너리 내의 어셈블리 명령어들은 각각 몇 byte씩 크기가 할당되어 있습니다.
컴퓨터 내에서는 이 바이너리를 메모리에 적재하고 처리해야 하는데 32비트 시스템인 경우 4바이트씩 끊어서 명령어들을 처리합니다
컴파일러는 특정 소스를 컴파일할 때에 함수들이 차지하는 byte를 다 계산해서 메모리에 빈 공간이 없도록 nop ( 1 byte ) 명령어들을 중간중간에 넣어줍니다. nop이 없이 빈 공간이 생길 경우 시스템에서 바이너리를 읽을 때 오류가 나서 읽지 못합니다.
재밌는 예시를 하나 들자면... 우리가 테트리스를 하고있다고 가정합시다
테트리스를 하는 도중에 한줄을 다 못채우고 구멍이 난 상태로 위로 쌓아 올리게 되면 한줄씩 제거되지 않고 계속 쌓이다 게임오버가 되게 됩니다.
이건 시스템이 바이너리를 제대로 못 읽었다는 것을 의미합니다. 빈 구멍 없이 공간을 채우게 되면 블록들이 터지고 점수가 오르게 되지요? ( 명령어들을 처리함 ) 시스템도 비슷하게, 빈 공간을 채우면 ( 테트리스 블럭을 일렬로 다 쌓았을 때, 즉 메모리에 빈 공간간 없이 명령어들을 적재하였을 때 ) 그 부분이 실행된다고 생각하시면 됩니다.
이와 마찬가지로 반대의 경우로, 이미 할당된 메모리값보다 더 크게 메모리값을 쓸 수도 없습니다. 따라서 Code injection 이라거나 특정 함수를 변경할 때에 그 함수의 크기보다 더 큰 함수를 사용할 수 없습니다. 사용하게 된다면 밑의 코드들도 영향을 받기 때문에 밑의 함수들까지 다 처리해주어야 합니다.
이런 기능을 하는 nop을 시스템 해커들은 nop sled이라는 기법을 연구하여 사용하고 있습니다. 아무런 기능도 하지 않기 때문에 레지스터 값이 변경될 일 없고 아무 하는 일 없이 다음 명령어들을 부르다가 ( nop을 쭉 타고 따라내려가다가 ) nop sled 뒤에 심어 놓은 shellcode를 실행하게 되는 것이지요.
3. 핸드래이를 시작하기 전 알아두면 좋은 것!
andray를 시작할 때 알아두면 좋은 점
ebp + 8 이상의 값은 함수의 인자를 뜻합니다. ebp = save frame pointer ( sfp ) 와 연관지어 생각해보시면 됩니다. sfp 다음에 return address가 있고 그 밑에는 함수 인자들이 존재합니다. 즉, ebp = sfp // ebp + 4 = return address // ebp + 8 ~ = 함수 인자들이 되는 것입니다.
4. Handray
C 코드를 핸드레이하는 과정입니다. 각각 v1, v2 지역변수를 가지고 있으며 이 변수들을 eax, edx 레지스터들에 넣어서 같이 더하고 있습니다. ( v2 += v1 ) 그리고 이 더한 값 (eax)를 mov DWORD PTR [ebp-0xc]에 넣고 있는데 v3 = v2 + v1 임을 알 수 있습니다. ( 지역변수 시작점에서 12byte 떨어져 있으므로 )
이전에 call 명령 함수 위에는 인자들이 존재한다고 하였습니다. 보시면 call 0x80482f0 <printf@plt> 위에
DWORD PTR [esp+0x4], eax
DWORD PTR [esp], 0x80484f0 이렇게 두 개의 인자가 있습니다.
여러 명령들을 지나오며 현재 eax는 30입니다. 그래서 이 두 개를 출력하게 되면 printf("plus : %d\n", v3); 문이 실행이 되고,
결과는 plus : 30이 출력되는 것을 볼 수 있는 간단한 핸드레이 입니다.
처음 프롤로그를 거치고 mov [ebp+8] 이상의 값이 출력이 된다면 이것은 main 함수 내에 인자가 있다는 것을 볼 수 있습니다.
그러므로 ebp + 0xc는 main 함수 내의 두 번째 인자로 볼 수 있습니다. argv로 볼 수 있겠네요.
그 다음 구문을 보시면 eax에 4를 더하는데, 이것은 argv[1]로 볼 수 있습니다. ( eax = argv[1] )
각각의 포인터 변수는 공간을 4bytes씩 할당 받기 때문에 argv[1]을 가르키게 되는 것입니다.
call strcmp 바로 위(main+22)에 [esp] 즉, 함수 첫 번째 인자에 eax를 move 합니다. 그 바로 위에는strcmp의 두 번째 인자에다가 0x8048530 즉, "5678"을 집어넣게 됨으로써 strcmp(argv[1], "5678"); 이렇게 볼 수 있습니다. 함수가 call 되고 나면 함수의 retrun 값이 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 툴을 쓰시면 더욱 좋다고 합니다.
'seKUrity_Study : System & Reversing' 카테고리의 다른 글
[system 실습] CrackMe2.exe (0) | 2023.03.19 |
---|---|
[system 실습] abex crackme #1.exe (0) | 2023.03.19 |
[ system ] 여러가지 용어 정리 (0) | 2023.03.10 |
[system] 메모리구조란? (0) | 2023.03.10 |
[ System ] 어셈블리어에 대하여 (0) | 2023.03.09 |