IA32 어셈블리 주요 명령어 이론과 실습
명령어
MOV
- 두 연산자의 크기가 동일해야 한다
- Operand 대상은 메모리 공간, 데이터, 레지스터 가능
- source 피연산자 값을 destination 피연산자로 복사
mov DWORD [0x00000100], 0Ah
: A=10 이라는 값을 []안에 있는 메모리 주소로 복사한다
mov eax, DWORD [0x00000100]
: `[0x00000100]` 안에 있는 값을 eax로 복사한다
[대괄호]가 나오면 주소값으로 생각하기
LEA
Source 피연산자 참조 메모리 주소를 Destination 피연산자 저장 ⇒ 주소 값을 저장
lea eax, [edx+4] edx = 8
: edx+8 =12가 eax에 저장된다
ADD
- Source 피연산자를 동일한 크기의 Destination 피연산자에 더함
- Source 피연산자는 변화 X
결과 값은 Destination 에 저장
add eax, 0x8 eax = 3
→ eax에 B(=11)이 저장된다
SUB
- Destination 피연산자를 Source 피연산자 만큼 감소
- Source 피연산자는 변화 X
- CPU ALU에서는 실제로 덧셈을 함 → 2의 보수 이후 더하기
CALL /JUMP
공통점: 피연산자로 지정된 주소로 프로그램의 실행 흐름을 바꿈(분기)
차이점
CALL: 분기 후 돌아올 주소값/현재EIP값을 스택에 백업함, 조건 분기 불가
JMP: EIP 값을 백업하지 않음, 조건 분기 가능
RET
- 의미상 POP EIP 명령어와 동일(의미상 그렇다는거고 실제로는 eip 직접 수정 불가하므로 사용불가)
- 함수의 끝, 자신을 호출한 함수로 돌아가는 역할
스택에 있는 주소를 참조해서 EIP 레지스터에 주소값을 넣음
PUSH, POP
push: 피연산자를 스택메모리 최상단에 입력해 ESP 변화
pop: 스택 메모리의 최상단에 있는 값을 피연산자에 입력해 ESP 변화
pop eax → ebp 값을 eax에 대입
스택이 메모리에서 높은 주소에서 낮은 주소로 확장하기 때문에 push 를 하면 새로운 공간 확보를 위해 스택 포인터가 내려가고 pop을 하면 스택포인터가 올라간다
예시
esp = 100
1Ch = 16+12 =28
⇒ 128번에 5라는 값을 넣음
18h = 16+8 = 24
⇒ 124번지에 7 값을 넣음
eax, [esp+18h]
⇒ 124번지에 있는 값 7을 eax에 넣음
cmp [ebp-8], eax
jl short loc_4014D
⇒ 앞에 있는 피연산자가 더 작으면(jump if less) short loc_4014D 주소로 분기
cmp 4 2
jg 0x001A
⇒ 앞에 operand가 크면 0x001A로 점프
실습
sum 함수는 12를 출력하고 두번째 loop 함수는 1부터 9까지의합인 45를 출력하는 코드다
v브레이크 포인트: 일시정지
실행은 F9 한줄실행/함수로이동: F7
다음에 실행되는 프로세스를 볼려면 EIP 레지스터를 보면 되는데 76f31c13이다
여기 주소를 따라 가보면
ntdll로 점프하는 프로세스가 실행될 예정
루프함수를 살펴보고 0040F7에 브레이크포인트를 걸어보자
멈췄다! 그리고 IDA에서 다시 코드를 살펴보면
0A, 즉 10이 루프카운트 값에 들어가있고 .ds:_loop_count 값을 자세히 살펴보면 전역변수인데 그 이유는
.bss 데이터 섹션에 있기 때문이다. 그리고 전역변수는 00406034 주소에 위치해있고
x dbg에서도 000A=10 값을 넣는게 확인된다
F7을 눌러서 한 줄씩 실행해보면 loop count(ebp-8)가 10이 될때까지 계속 순환하다가 10이 되면 빠져나온다