1. 리버싱 소개
1) 연산자
▣ PUSHAD
- Stack에 범용레지스터 8개의 값을 push함. (주로 백업용도)
- 순서 : EAX -> ECX -> EDX -> EBX -> ESP -> EBP -> ESI -> EDI
#include <stdio.h> int main() { int a = 1; _asm { pushad } printf("%d\n", a); return 0; }
- 실행이 되지 않음.
☞ push&pop 처럼 pushad 한만큼 8개의 값을 pop 해줘야함. (popad)
▣ POPAD
- Stack 안에 있는 8개의 범용레지스터 값을 거꾸로 빼와서 범용레지스터에 저장. (stack -> 범용레지스터)
#include <stdio.h> int main() { int a = 1; _asm { popad } printf("%d\n", a); return 0; }
- 이 또한 실행되지 않음.
#include <stdio.h> int main() { int a = 1; _asm { pushad popad } printf("%d\n", a); return 0; }
- 이렇게 같이 사용해야 돌아감.
[주의사항]
: pushad 이후 스택을 사용하게 되면 pop할 때 그 순서가 뒤바뀔 수 있음.
☞ 순서대로 범용레지스터 값들이 저장되어있을 때 push 명령어등으로 새로운 값을 stack에 저장하게 되면
popad를 하게 됐을 때 값이 밀려서 저장이 될 수 있음.
EDI | 내가 push 한 값 |
ESI | EDI |
EBP | ESI |
... | ... |
▣ PTR
- 데이터타입 재정의 (like c언어 캐스팅 연산자)
- usage : [데이터타입] PTR ([데이터타입] : BYTE, WORD, DWORD)
#include <stdio.h> int main() { int a = 1; printf("%X\n", a); _asm { mov word ptr a, 0x11111111 } printf("%X\n", a); _asm { mov dword ptr a, 0x11111111 } printf("%X\n", a); return 0; }
- 실행 결과
- 설명
☞ 11줄 : a를 2바이트로 취급, 뒤의 2바이트만 대입이 됨. (a : 00 00 11 11)
☞ 18줄 : a를 4바이트로 취급, 4바이트 대입이 됨. (a : 11 11 11 11)
▣ OFFSET
- Data 영역에 있는 전역변수의 주소를 가져옴.
- usage : OFFSET op1 (op1은 전역변수이고 주소를 가져옴)
#include <stdio.h> int global = 30; int main() { int addr = 0; int value = 0; _asm { mov eax, offset global mov addr, eax mov ebx, [eax] mov value, ebx } printf("addr : %p\n", addr); printf("value : %d\n", value); return 0; }
- 실행 결과
- 설명
☞ 12줄 : 전역변수 global의 주소를 EAX에 저장.
☞ 15줄 : EAX를 주소로 취급하고 EAX가 가리키는 주소에 있는 값을 EBX에 저장.
▣ LEA (Load Effective Address)
- usage : LEA op1 , op2
- op2(지역변수, 전역변수)의 메모리 절대주소를 op1에 저장.
☞ mov는 op1에 레지스터 뿐만아니라 다른 지역변수나 전역변수 모두 가능
LEA reg, mem |
#include <stdio.h> int main() { int a = 4; printf("%.8X\n", a); printf("%.8X\n", &a); _asm { mov eax, [ebp - 0x04] mov a, eax } printf("%.8X\n", a); _asm { lea eax, [ebp - 0x04] mov a, eax } printf("%.8X\n", a); return 0; }
▣ STOS (Store String)
- EDI가 가리키는 주소에 EAX 값을 저장.
☞ 반드시 EDI에는 주소값, EAX에는 어떠한 값이 저장되어 있어야함.
- 복사한 크기만큼 EDI값 증가
- REP 명령어랑 같이 연속된 저장공간(=배열)을 초기화할 때 사용됨.
STOS mem |
#include <stdio.h> int main() { char buffer[20]; _asm { mov eax, 0x61 lea edi, dword ptr [buffer] stos dword ptr [edi] } printf("%s\n", buffer); return 0; }
- 실행 결과
- 설명
☞ 10줄 : buffer 변수의 주소를 4바이트 데이터타입으로 전환시킨 후 EDI에 저장.
☞ 11줄 : 현재 EDI에 저장된 주소는 buffer의 주소. EAX에 있는 값은 0x61('a')임.
▣ REP (Repeat String Operation)
- String 관련 명령어를 반복시킴. (STOS, MOVS, SCAS)
- 반복횟수 카운터로 ECX를 사용.
☞ 한 번 반복할 때 마다 ECX를 1씩 감소시키며 0이 되면 반복을 종료.
#include <stdio.h> int main() { char buffer[20]; _asm { mov eax, 0x61 lea edi, dword ptr [buffer] mov ecx, 0x05 rep stos dword ptr [edi] } printf("%s\n", buffer); return 0; }
- 반복 종료 후 EDI 값은 처음 값보다 +5가 되어있음.
i2sec 대구지점 23기 수료생.
'해킹&보안 > 리버싱' 카테고리의 다른 글
[D+6] 리버싱 소개 (6) (0) | 2017.03.28 |
---|---|
[D+5] 리버싱 소개 (5) (0) | 2017.03.27 |
[D+3] 리버싱 소개 (3) (0) | 2017.03.23 |
[D+2] 리버싱 소개 (2) (0) | 2017.03.22 |
[D+1] 리버싱 소개 (1) (0) | 2017.03.21 |