제 1장. 어셈블리를 알아보자! -1부
void drinking(){
open = 냉장고 문을 연다();
if(open){
물을 꺼냄();
마심();
}
}
'물을 마신다'는 소스 코드를 C언어로 구현(?) 한 것이다. (물론, 실제로 되진않겠지?)
어셈블리 어는 이 코드를 컴퓨터가 알아듣기 쉽게 또는 기계어로 번역할 때 좀 더 자세하게 나타내주었다고
보면 된다.
asm{
냉장고 앞으로간다.
팔을 뻗는다.
손을 손잡이에 둔다.
힘을 준다.
문을 연다.
물병을 찾는다.
물병을 잡는다.
뚜껑을 연다.
물을 마신다.
}
이런 식으로 매우 자세하게 표현이 된다.
일반적으로 고급언어(ex. C언어) --> 어셈블리어 --> 기계어 이런 식으로
컴파일러와 어셈블러가 우리가 코딩한 소스를 010100로 이루어진 컴퓨터(기계어)로 변역해준다고 보면된다.
*어셈블리어 기본형태
"명령어(opcode) + 인자 (operand)
명령어는 가장 기본적으로 mov나 push 이러한 어셈 명령어를 말하고,
인자는 어떤 장소에 값을 넣을지(목적지) 이거나, 명령어를 실행할 값들 등이 된다.
mov eax , 1
opcode operand
가장 기본적으로 mov라는 명령어를 예로 들자면 eax(레지스터) 라는 저장공간에 1이라는 값을 넣는다. 라는 명령어이다.
여기서 레지스터는 '변수'라고 생각하면 된다.
하지만 일반 적인 변수와는 달리 CPU가 사용하는 변수이기에, 개수가 제한되어 있으며, CPU에서 바로 연산이 되어
속도또한 매우 빠르다.
eax 레지스터
- 산술 계산을 하며, 함수의 리턴값을 전달한다.
- 가장 많이 쓰이는 변수라고 보면되고, 사칙연산에 사용될 때 eax가 자주 등장한다.
edx 레지스터
- eax와 역할이 같다. eax의 역할을 조금 분담하는 '조수'의 역할로 보면 된다.
- 하지만 리턴 값의 용도로는 사용되지 않는다. d는 Data의 약자이다.
ecx 레지스터
- c는 Count의 약자로, 주로 휫수를 카운팅 한다. 즉, 반복문(for문) 등의 루프문을 수행할 시, 휫수를 카운팅 한다.
- 카운팅할 필요가 없을 때는 변수로 사용해도 무방하다.
ebx 레지스터
- 별다를 것 없다. 그냥 레지스터가 부족할 때, 어떠한 특정한 목적으로 쓰기위해 만들어진 친절한 레지스터이다.
-여분
esi, edi 레지스터
- 이것 역시 변수의 일종이다. 하지만, 어떤 반복적인 데이터의 처리나 메모리를 옮기는 데 사용된다.
- esi는 시작(source), edi는 도착(Destination)
ex. memcpy(void *dest, void *source, size);
일 때, 두번째 인자 source를 esi로 source메모리를 읽어 edi로 복사한다. (말이 좀 어렵다. 나중에 더 쉬운 말로 고치도록 하겠다)
*가장 기본적인 어셈블리 명령어
PUSH, POP
- PUSH(넣는다), POP(꺼내다) 라는 영문해석 그대로 뭔가를 넣고 꺼낸다.
- 함수를 사용하거나 할 때. 인자를 사용하기 위해 스택에서 가져올 때 쓰는 명령어이다.
- popad, pushad 라는 명령어도 있는데, 모든 레지스터를 push하고 pop하는 명령어이다.
일반적으로, 패킹 시에 코드 변동이 있으므로 레지스터의 백업 시 사용한다.
ex. add라는 더하기 함수를 호출할 때,
push 1
push 3
call add
식으로 인자를 스택에 넣고 add라는 함수를 호출한다.
*mov 명령어
- 어떠한 레지스터에 값을 넣는다.
MOV ebx ,1
MOV eax, ebx 의 식으로
첫 번째 인자(목적지)에 두번째 인자를 넣는다.
* lea 명령어
- 앞서 mov가 값을 가져온다면 lea는 주소를 가져온다.
- LEA는 가져올 src 오퍼랜드가 주소라는 의미로 대부분 []로 이뤄져 있다.
* ADD 명령어
- src(소스)에서 dest(목적지)로 값을 더한다.
ex. ADD eax, ebx
eax와 ebx를 더해 eax에 저장한다.
* SUB 명령어
- ADD와 반대되는 명령어로, 값을 뺀다.
* INT 명령어
- 어떤 인터럽트를 일으키는 명령어이다. 리버싱을 하다보면 INT3를 가장 많이 보는데(사실 아직 아는게 이거밖에 없다 ㅜㅜ)
INT3는 쉽게 DebugBreak로 보면 된다.
#인터럽트란?
컴퓨터가 작업을 수행하는데 어떤 이상한 상황이 발생했다!
그래서 그 특수상황을 처리하고 원래의 작업으로 돌아가는 것이다.
* jmp 명령어
- jump의 약자로 어떠한 코드 주소로 점프한다.
- cmp 등의 비교 명령어와 함께 사용하여 분기점을 만들 수 있다.
* CALL 명령어
- CALL이라는 옵코드 뒤에 번지수가 붙는다. 즉, 어떠한 함수를 호출하는 것이다.
- 어떤 주소로 가는 것이면 jmp를 써도 되는데 궅이 call을 쓴다? 라는 의문이 들 수 있는데
call을 쓰면 jmp와는 달리 RET를 만나 함수가 끝나면 call다음 EIP(다음 실행될 코드)로 이동한다.
* INC,DEC 명령어
- C언어에서의 ++, -- 명령어로 보면 된다.
* AND, OR, XOR 명령어
- C언어에서 많이 봤듯이 비트 연산자이다.
- XOR 의 경우는 XOR eax, eax 처럼 사용해 변수를 초기화 하는 효과를 줄 수 있다.
* NOP 명령어
- 아무것도 하는 말라는 명령어이다.
- 해킹이나 리버싱에서 NOP Sled나 어셈을 NOP으로 임의적으로 바꿔
실행결과를 유추하는 등의 기법으로 많이 쓰이는 명령어이기도 하다.
헤헤헿헤ㅔㅎ 나머지는 나중에 쓸거야 귀차나나ㅏ앙 ><