함수이름은 어떻게 정의 될까?
#include <stdio.h>
int asm_main(); // 어셈블리 파일로 만들예정
int main()
{
int n = asm_main();
printf("result : %d\n", n);
}
; asm1.asm
.model flat
public _asm_main
.code
_asm_main:
mov eax, 100 ; return 100과 동일
ret
end ; 파일의 끝에는 항상 end가 있어야 한다.
- cpp에서는 함수의 이름을
asm_main()
로 선언했는데 어셈블리에선_asm_main
라 정의한 이유??
cl컴파일러는 함수의 이름 앞에_
를 붙여서 쓴다. cl컴파일러의 규칙을 따르기 위해서 추가함.
.model flat 이란
.model
에서는 세 가지를 정의할 수 있다.- memory model
- language-type
- stack-option
memory model
의 경우 32bit 응용프로그램의 경우 flat으로 정의 하고 16bit는 여러가지가 있는데 16는 쓰일일이 없으니 설명은 생략한다.language-type
의 경우 c, stdcall로 정의가능한데 이후에 별도로 설명한다stack-option
의 경우 32bit에서는 사용이 안되고 16bit에서는 여러 설정이 있지만 역시 설명은 생략
대략적 사용은 아래와 같이 한다.
.model flat ;
.model flat, c ; language-type을 c로
.model float, stdcall ; language-type을 stdcall로
PE Format이란?
C++로 실행파일(exe)을 만들시 내부메모리 구조는 아래와 같다
Header
-------
Section .text (함수가 들어있다)
Section .data (전역 변수가 들어있다)
...
C++에서는 Section .text, .data를 알아서 컴파일러가 정리해줬다고하지만…
어셈블리에서는 어떻게 정리할까?
.model flat
public _asm_main ; 다른 함수에서도 부르게 해달라는 명령(c에서 부를수 있게 된다.)
.data
; Section .data를 의미한다
; 전역 변수이다
.code ; .text 를 의미한다.
_asm_main:
mov eax, 100 ; eax레지스터는 보통 반환값을 넣는 용도로 사용됨
ret ; 함수 종료(return)를 의미
end
PE에 관해서는 이후 DLL설명에서 추가설명을 넣음.
일단 PE Format이라는게 바이너리가 Header Section .text(함수), Section .data(변수)로 구분된다고 기억하자
또 다른 어셈블리 표기법
함수를 또 다른 방법으로 표기
.model flat
public _asm_main
.code
_asm_main proc ; 콜론(:)대신 proc표기가능
mov eax, 100
ret
_asm_main endp ; proc가 끝났다고 알린다
end
Section을 또 다른 방법으로 표기
.model flat
public _asm_main
_DATA SEGMENT ; .data와 동일
_DATA ends
_TEXT SEGMENT ; .code 대신에 _TEXT SEGMENT 표기가능
_asm_main proc
mov eax, 100
ret
_asm_main endp
_TEXT ends
end
C소스를 어셈블리 파일로 생성하기
# asm1.c를 어셈블리 언어로 표현해주세요
$ cl asm1.c /FAs /c
/FAs
: 어셈블리를 만들어 달라/c
: 컴파일만 해달라
Data section
.model flat
public _asm_main
.data
L1 DWORD 100 ; 4바이트 할당 100을 넣어라
L2 DD 200 ; DD = DWORD와 동일어
L3 DD ? ; 초기값을 정의하고 싶지 않다면 ?를 넣자
.code
_asm_main:
mov eax, L1 ; 100이 리턴
; 메모리 복사
; mov L1, L2 ; 컴파일 에러 : 2개의 오퍼랜드가 모두 메모리일 수 없다.
mov ebx, L2
mov L1, ebx
; 이런식으로 해야함
; 메모리 주소값 구하기
; ebx = &L1
mov ebx, offset L1 ; L1의 메모리주소를 넣어라
mov eax, [ebx] ; []는 C의 *와 동일 *ebx(ebx가 가리키는 값 = L1값)
; ebx = 300
; mov [ebx], 300 ; 에러 : 300을 몇 바이트(int, short)로 넣을지 미정
mov dword ptr[ebx], 300 ; dword(2바이트)로 넣어주세요
ret
end
; 배열 만들어 보기
.model flat
public _asm_main
.data
; L1 DD 100
L1 DD 100, 200, 300, 400
L2 DD 4 dup (100) ; 100, 100, 100, 100
; 4개의 100을 넣어달라
.code
_asm_main:
; mov eax, L1
mov esi, offset L1
mov eax, dword ptr[esi+4]
ret
end
여기서 궁금한 점 레지스터는 어디까지 있나??
eax
, ebx
… 어디까지 있는거지??
- 참고사이트 : 알파벳 순이 아니라 용도에 따라 사용되는 레지스터가 다르다
함수 호출의 원리
jump 함수호출
.model flat
public _asm_main
public _foo
.code
_asm_main:
mov eax, 100
; 여기서 _foo를 호출하고 싶다면?
ret
_foo: ; 참고로 c에서도 호출하게 하고 싶어서 _를 앞에 붙임
end
- 함수를 호출하는 방법
jmp
call
jmp
_asm_main:
mov eax, 100
mov ebx, POS_A ; 함수를 호출 후 POS_A로 돌아오게 해주세요
; mov ebx, 돌아올 주소
jmp _add ; 함수 호출
POS_A:
ret
_add:
mov eax, 300
jmp ebx ; ebx에 리턴 후 돌아갈 주소가 남겨져 있기에 jmp하면 돌아감
ebx
에 돌아갈 주소를 넣어둘 경우 ebx
는 함수가 종료될때까지 사용하지 못하게된다.
ebx
를 써야할 필요가 발생한다면???
해결해보자
_asm_main:
mov eax, 100
; mov ebx, 돌아올 주소
push POS_A
jmp _add ; 함수 호출
POS_A:
ret
_add:
mov eax, 300
pop ebx ; ebx에 pop해달라
jmp ebx
call 함수호출
_asm_main:
mov eax, 100
call _add ; 스택에 넣고 돌아올 주소까지 할당
ret
_add:
mov eax, 300
ret ; 스택에 꼭대기에 돌아갈 주소가 있다고 가정
- 결론 :
call, ret
을 쓰자
C를 어셈블리로 만들어서 call을 어떻게 사용하나 보자
int add()
{
return 300;
}
int main()
{
add();
}
이걸 어셈블리로 만들어 보자.
cl call.c /FAs
결론은 call, ret을 VS에서도 그대로 쓴다는 것을 확인하는 것이다.
; Listing generated by Microsoft (R) Optimizing Compiler Version 19.28.29914.0
include listing.inc
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
PUBLIC add
PUBLIC main
pdata SEGMENT
$pdata$main DD imagerel $LN3
DD imagerel $LN3+16
DD imagerel $unwind$main
pdata ENDS
xdata SEGMENT
$unwind$main DD 010401H
DD 04204H
xdata ENDS
; Function compile flags: /Odtp
; File C:\Git\Example\Example\call.c
_TEXT SEGMENT
main PROC
; 7 : {
$LN3:
sub rsp, 40 ; 00000028H
; 8 : add();
call add
; 9 : }
xor eax, eax
add rsp, 40 ; 00000028H
ret 0
main ENDP
_TEXT ENDS
; Function compile flags: /Odtp
; File C:\Git\Example\Example\call.c
_TEXT SEGMENT
add PROC
; 3 : return 300;
mov eax, 300 ; 0000012cH
; 4 : }
ret 0
add ENDP
_TEXT ENDS
END