함수호출규약


- 함수호출규약(Function Calling Convention)이란?


함수를 호출하는 방식에 대한 일종의 약속이다. 인수 전달 방법, 인자 전달 순서, 인자 정리 방법에 따라 그 종류를 구분한다

호출하는 쪽과 호출되는 쪽의 어느 한쪽이 약속을 어길 경우 함수도 제대로 동작하지 않으며 메모리가 엉망이 되기 때문에

프로그램을 정상적인 실행을 계속할 수 없다.


- 스택프레임

스택프레임을 보면 높은 주소에서 낮은 주소로 쌓여지며, 함수를 호출 할 때 마다 새로운 스택프레임이 생성되는 것을 볼 수 있다. 


- 함수 호출시 스택의 변화순서



- 함수 호출 규약의 구분


인자 전달 방법 : 스택 / 레지스터

인자 전달 순서 : 역방향(←)

인자 정리 방법 : Caller, Callee


* Caller(호출자) : 다른 함수를 호출한 함수

* Callee(피호출자) : 호출을 당한 함수


- cdecl

C언어와 C++에서의 표준 함수 호출 규약이다.

Caller가 인자를 정리하며, printf() 함수와 같은 가변 인자 함수를 사용 가능하다.

인자 전달 방법 : 스택

인자 전달 순서 : 역방향(←)

인자 정리 방법 : Caller


소스코드


어셈블리코드


- stdcall

Window API, Visual Basic에서 사용하는 표준 함수 호출 규약이다.

코드가 간결하고 가변 인자 함수를 사용할 수 없다.

인자 전달 방법 : 스택

인자 전달 순서 : 역방향(←)

인자 정리 방법 : Callee


소스코드


어셈블리코드


- fastcall

Delphi 에서 사용되는 함수 호출 규약이다.

인자 2개까지는 레지스터(ECX, EDX)를 사용해서 인자를 전달하므로 속도가 빠르다.

인자 3개 이상일 경우 스택과 함께 사용한다.

인자 전달 방법 : 스택 + 레지스터

인자 전달 순서 : 역방향(←)

인자 정리 방법 : Callee


소스코드


어셈블리코드





'보안 > 리버싱' 카테고리의 다른 글

Reversing -2 레지스터  (0) 2017.02.01
Reversing -1 어셈블리언어  (0) 2016.05.16


레지스터


레지스터란?

중앙처리장치(CPU)가 데이터를 처리하는 동안 중간 결과를 일시적으로 저장해 두는 고속 기억장치를 말한다.

중앙처리장치 내부에는 각 용도에 맞는 다양한 레지스터가 존재하며 연산처리 및 주소지정을 도와준다. 

크기는 레지스터마다 다르지만, 보통 32bit(4byte) 이다.


범용 레지스터


EAX

 EBP

EBX

 ESP

ECX

ESI

EDX

EDI


EAX, EBX, ECX, EDX 레지스터

EAX, EBX, ECX, EDX 레지스터는 위와 같이 나누어 사용 할 수 있다.


16bit 운영체제 시절에는 AX, BX, CX, DX와 같이 레지스터에 E를 붙이지 않고 불렀지만 

32bit로 확장되면서 E(Extended)가 앞에 붙어 EAX, EBX, ECX, EDX 등으로 부른다.

 64bit 경우에는 E대신 R을 붙인다. AX 레지스터의 상위부분(High)AH라고 하고 하위부분(Low)AL이라고 한다.


EAX (Extended Accumulator Register)

: 누산기 기능

: 산술·논리연산 저장

: 함수가 호출될 시 리턴 값이 저장되는 공간


EBX (Extended Base Register)

: 간접 주소 지정 시 사용된다. (특정 주소 저장)

: 산술·논리연산 저장


ECX (Extended Counter Register)

: 명령의 반복수행이 필요로 할 때 반복횟수 지정에 주로 사용

: ECX에 넣은 값은 한번 반복될 때 마다 1씩 감소하며 ECX > 0 조건을 만족할 때까지 반복


EDX (Extended Data Register)

: EAX 보조 역할(곱셈, 나눗셈을 할 때 보조 누산기로 사용)

: EAX에 들어가는 리턴 값이 32bit를 넘을 경우 나머지를 EDX에 저장


포인터 레지스터 //스택과 관련이 있는 레지스터


EBP (Extended Base Pointer)

: 스택프레임의 기준점이 된다.

: 스택 프레임을 사용하는 동안 EBP의 값은 변하지 않고 스택 프레임이 소멸되면 이전의 스택 프레임을 가리키게 된다.


ESP (Extended Stack Pointer)

: 스택프레임의 최상단의 주소를 가지고 있다.

: 현재까지 사용된 스택의 위치를 저장하는데 사용된다.


EIP (Extended Instruction Pointer)

: 다음에 실행할 명령어의 주소를 가지고 있다.

: CS 세그먼트 레지스터와 한 쌍이 되어 실행주소를 참조한다.

: 버퍼 오버플로우 공격에 사용되는 레지스터


인덱스 레지스터 //문자열과 관련이 있는 레지스터


ESI (Extended Source Index)

: 복사 혹은 비교를 하는데 사용되는 출발지의 문자열 주소가 들어간다.

EDI (Extended Destination Index)

: 복사 혹은 비교를 하는데 사용되는 도착지의 문자열 주소가 들어간다.


플래그 레지스터 //연산 결과 및 시스템 상태와 관련된 여러 가지 플래그 값을 저장하는 레지스터


EFLAGS (Extended FLAGS) 상태 값이 0이면 Clear 1이면 Set이라고 한다.


상태 플래그(Status Flag)

AF(Auxiliary carry Flag) : 10진수 연산에서 자리 올림/내림이 발생할 때 Set(1) 된다.

CF(Carry Flag) : 연산 결과, 자리 올림/내림이 발생할 때 Set(1) 된다.

OF(Overflow Flag) : 정수형 결과 값이 너무 큰 양수이거나 너무 작은 음수여서 피연산자 데이터 타입에 들어가지 않을 경우 Set(1)된다.

PF(Parity Flag) : 연산 결과, 1(binary)의 개수가 짝수 개일 때 Set(1) 홀수 개일 때 Clear(0)이다.

SF(Sign Flag) : 연산 결과, 최상위비트가 1일 때(음수) Set(1)되고, 양수이면 Clear(0)이다.

ZF(Zero Flag) : 연산 결과, 0이면 Set(1)되고 아니면 Clear(0)된다.


세그먼트 레지스터 //세그먼트 영역의 메모리에 주소를 지정하며 크기는 16bit


CS (Code Segment) : 코드 세그먼트의 시작 주소를 가리키며, Instruction Pointer(IP) 레지스터가 가진 offset 값과 합쳐서 실행을 위한 명령어의 주소를 참조

DS (Data Segment) : 데이터 세그먼트의 시작 주소를 가리키며, AX, CX, DX, SI, DI 레지스터와 합쳐서 데이터 영역의 주소를 참조하게 된다.

SS (Stack Segment) : 스택 세그먼트의 시작 주소를 가리키며, SP 혹은 BP 레지스터와 합쳐서 스택 영역의 주소를 참조하게 된다.

ES(Extra Segment) : 데이터 관련 확장 레지스터로 주로 문자 데이터의 주소를 지정하는데 사용한다.

FS : 데이터 관련 확장 레지스터로 TIB(Thread Information Block)의 정보를 가지고 있다.

GS : 데이터 관련 확장 레지스터

'보안 > 리버싱' 카테고리의 다른 글

Reversing -3 함수호출규약  (0) 2017.02.01
Reversing -1 어셈블리언어  (0) 2016.05.16

어셈블리언어


어셈블리언어란?

기계어를 사람이 알아보기 쉬운 니모닉 기호(mnemonic symbol)를 사용해 1:1 대응이 되는 컴퓨터 프로그래밍의 저급 언어이다. 

* 기계어: 컴퓨터가 읽을 수 있는 2 진 숫자(Binary digit, 0 과 1)로 이루어진 언어. 

* 니모닉 기호: 기계어로 된 일련의 숫자를 알아보기 쉽게 언어(단어)로 바꿔준 것. 


- IA-32 레지스터(32bit) // IA -64 레지스터 (64bit) 는 E 대신 R이 붙음

EAX : 누산기 , 산술·연산저장 , 리턴값

EBX : 간접주소지정, 산술·연산저장 

ECX : 반복카운터(차감) , ECX > 0

EDX : EAX 보조역할 //리턴값이 32bit수를 넘어갈 때 나머지를 EDX에 저장

EBP : 스택프레임 기준

ESP : 스택프레임 최상단

ESI I: 출발지(Source) 주소를 담는 공간 

EDI I: 목적지(Destination) 주소를 담는 공간 

EIP I: 다음 실행될 명령어의 주소를 담고 있는 레지스터

EFLAGS : [상태값 : 0(clear), 1(set)], AF,CF,OF,PF,SF,ZF 

  ZF: 산술연산결과가 0일 때 1로 set 되는 flag 

  ex) 1+1 = 2 ZF : 0 

      1-1 = 0 ZF : 1


- 데이터 형식

 BYTE 

 부호 없는 1 byte 

 WORD

 부호 없는 2 byte 

 DWORD

 부호 없는 4 byte 


- INC //operand의 값 1 증가

#include<stdio.h>

int main(){

int a=0;


_asm{ //_asm{} -> 인라인 어셈블러

INC a //or INC [EBP-0x04]

}


printf("%d\n",a);


return 0;

}


- DEC //operand의 값 1 감소

#include<stdio.h>

int main(){

int a=1;


_asm{

DEC a //or DEC [EBP-0x04]

}

printf("%d\n",a);


return 0;

}


- ADD //덧셈

#include<stdio.h>

int main(){

int a=0;


_asm{

ADD a, 0x05

}

printf("%d\n",a);


return 0;

}


- SUB //뺄셈

#include<stdio.h>

int main(){

int a=1;


_asm{

SUB a, 0x01

}

printf("%d\n",a);


return 0;

}


- MOV //복사,대입

#include<stdio.h>

int main(){

int a=0;


_asm{

MOV a, 0x02

}

printf("%d\n",a);


return 0;

}


ex) 예제문제

#include<stdio.h>

int main(){

int a,b,c,d;

a=b=c=0;


_asm{

MOV a , 0x03 // a주소에 3 대입

MOV b , 0x05 // b주소에 5 대입

MOV c , 0x0A // c주소에 10 대입

INC [EBP-0x08] // b주소에 1 증가

DEC [EBP-0x0C] // c주소에 1 감소

MOV EAX,[EBP-0x08] // EAX에 b주소에 있는 값 대입 

ADD EAX,[EBP-0x0C] // EAX(b) , c주소에 있는 값 두 개 더해서 EAX에 대입

MOV [EBP-0x04],EAX // a주소에 EAX값 대입

MOV EBX,[EBP-0x04]

SUB EBX,[EBP-0x0C]

MOV [EBP-0x08], EBX

}

printf("%d\n",a);

printf("%d\n",b);

printf("%d\n",c);

return 0;

}



- AND 

#include<stdio.h>

int main(){

int a=10;


_asm{

AND a, 0x06 

}

printf("%d\n",a);


return 0;

}


- OR

#include<stdio.h>

int main(){

int a=20;


_asm{

OR a, 0x0C

}

printf("%d\n",a);


return 0;

}


- XOR

#include<stdio.h>

int main(){

int a=12;


_asm{

XOR a, 0x08

}

printf("%d\n",a);


return 0;

}


- SHL & SHR //bit 이동

#include<stdio.h>

int main(){

int a=4;

printf("%d\n",a);


_asm{

SHL a, 0x01 //×2

}

printf("%d\n",a);

_asm{

SHR a, 0x02 //÷2

}

printf("%d\n",a);

return 0;

}

- PUSH & POP //push 한 만큼 pop 해야함

#include<stdio.h>

int main(){

int a=0;

_asm{

 push a

 push 0x10

 push eax

 

 pop eax

 pop a

 pop ebx

}

printf("%d\n",a);

return 0;

}


- PUSHAD //EAX->ECX->EDX->EBX->ESP->EBP->ESI->EDI , PUSH만 했기 때문에 에러남!

#include<stdio.h>

int main(){

int a=0;

_asm{

PUSHAD //범용 레지스터의 값들을 스택에 저장 = 백업하는 용도 

}

printf("%d\n",a);

return 0;

}

- POPAD 

#include<stdio.h>

int main(){

int a=0;

_asm{

POPAD //스택의 데이터를 범용 레지스터에 채움 

}

printf("%d\n",a);

return 0;

}


- PTR //데이터 타입 재정의

#include<stdio.h>

int main(){

int a = 1;

printf("%x\n",a); //1

_asm{

MOV WORD PTR a, 0x11111111

}

printf("%x\n",a); //1111

_asm{

MOV DWORD PTR a, 0x11111111

}

printf("%x\n",a); //11111111


return 0;

}


- OFFSET //전역변수 주소값 구하기

#include<stdio.h>

int g=10;

int main(){

int addr = 0;

int val = 0;

_asm{

MOV EAX, OFFSET g //데이터 세그먼트(영역) 시작부터의 상대적 거리값

MOV addr , EAX


MOV EBX, [EAX]

MOV val, EBX

}

printf("%x\n",addr);

printf("%x\n",val);

return 0;

}


- LEA //주소값 구하기

#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 // EDI가 가르키는 주소에 EAX 값을 저장

#include<stdio.h>

int main(){

char Buffer[20];

      _asm{

MOV EAX, 0x00

LEA EDI, DWORD PTR [Buffer]

STOS DWORD PTR [EDI]

      }

printf("%s\n",Buffer);

      return 0;

}

- REP //Repeat , STOS,MOVS,SCAS 에서 사용가능

#include<stdio.h>

int main(){

char Buffer[20];

      _asm{

MOV EAX, 0x00

LEA EDI, DWORD PTR [Buffer]

MOV ECX, 0x05

REP STOS DWORD PTR [EDI]

      }

printf("%s\n",Buffer);

      return 0;

}

- MOVS //ESI가 가리키는 곳에 값을 EDI가 가리키는 곳에 복사(대입)

#include<stdio.h>

#include<string.h>

int main(){

char * str1 = "Hello~ world!!!";

      char str2[20];

      int Len = strlen(str1);

      _asm{ //strcpy 구현

MOV ESI, DWORD PTR [str1]

LEA EDI, DWORD PTR [str2]

MOV ECX, Len

REP MOVS BYTE PTR [EDI], BYTE PTR[ESI]

MOV BYTE PTR [EDI], 0x00

      }

printf("%s\n",str2);

      return 0;

}


- TEST //묵시적 AND 연산 , OPRAND에는 영향을 미치지 않음 , ZF:0

#include<stdio.h>

int main(){

int a = 3;

_asm{

TEST a, 0x02 

//ex) TEST EAX, EAX 리턴값이 NULL이 아닌지 확인

}

printf("%d\n",a);

return 0;

}


- CMP //내부적으로 - 연산

#include<stdio.h>

int main(){

int a = 5;

_asm{

CMP a, 0x05 //OPRAND에는 영향을 미치지 않음 

//CF:부호없는 산술연산 결과(음수 포함)가 너무 커서 담을 수 없을 때 1로 set 되는 flag , ZF:0

}

printf("%d\n",a);

return 0;

}


ex) CMP 5, 6 = -1 ZF:0 CF:1

ex) CMP 6, 5 = 1  ZF:0 CF:0

ex) CMP 5, 5 = 0  ZF:1 CF:0


- JMP//지정한 위치 이동에 사용

#include<stdio.h>

int main(){

int a = 5;

_asm{

JMP L1

MOV a, 0x09


L1: //레이블은 인라인 어셈 안이나 밖이나 사용 가능

MOV a, 0x01

}

printf("%d\n",a);

return 0;

}


- 조건 점프 명령

 JA 

 CMP a > b

 JB

 CMP a < b

 JE

 CMP a == b

 JNE

 CMP a != b

 JZ

 TEST EAX, EAX (EAX = 0), ZF = 1

 JNZ

 TEST EAX, EAX (EAX = 1), ZF = 0


 JAE

 CF = 0 and ZF = 0 

 JBE

 CF = 1 and ZF = 1 

 JC

 CF = 1 

 JCXZ

 CX = 0 

 JECXZ

 ECX = 0 

 JG

 ZF = 0 and SF == OF 


- call
 
//레이블은 인라인 어셈 안이나 밖이나 사용 가능

#include<sdtio.h>

int sum(){

int a = 0;

int b = 2;

printf("Result\n");

return a + b;


}

int main(){

int result =0;

printf("%d\n", result);

_asm{

CALL sum

MOV result, EAX

}

printf("%d\n",result);

return 0;

}


- RET (== RETN) //ESP 레지스터가 가리키는 값을 EIP 레지스터에 저장

호출한 함수가 명령을 마치고 돌아갈 주소를 지정한 공간이다. 

인자를 정리할 때 사용한다. * RET imm8 = RET 0~255 

ex) RET 8 -> ESP 를 8bit 만큼 증가 

 

- NOP //아무 일도 하지 않는 명령어 

1byte 의 크기를 가지며 16 진수로 0x90 이다. 

명령어 사이의 빈공간을 채워주는 역할을 한다. 

- 바이너리 디버거 다운로드

OllyDBG          http://www.ollydbg.de/

Immunity DBG    http://debugger.immunityinc.com/

IDA Pro          https://www.hex-rays.com/products/ida/support/download.shtml

WinDBG          https://msdn.microsoft.com/ko-kr/windows/hardware/hh852365.aspx



'보안 > 리버싱' 카테고리의 다른 글

Reversing -3 함수호출규약  (0) 2017.02.01
Reversing -2 레지스터  (0) 2017.02.01

+ Recent posts