1. Linux 버전별  메모리 보호 기법




메모리 보호 기법

1. ASLR(Address Space Layout Randomization)

2. DEP / NX(Not Excutable)

3. ASCII-Armor

4. Canary




2. 메모리 보호 기법


1) ASLR(Address Space Layout Randomization)

주소공간을 랜덤하게 배치하는 기법이다. 스택, 힙 등 데이터 영역의 주소를 랜덤으로 프로세스 주소 공간에 배치함으로써 실행할 때 마다 데이터의 주소가 바껴 메모리를 보호하게 된다.







2) DEP / NX(Not Excutable)

주메모리를 보호하기 위해 코드가 Stack이나 Heap영역에서 실행할 수 없게 하는 기법이다. DEP가 적용된 상태에서는 BOF공격을 하여도 실행권한이 없어 프로그램에 대한 예외처리후 종료가 되어 메모리를 보호하게 된다.








3) ASCII-Armor

Libc 영역을 보호하기 위한 기법이다. 상위주소를 \x00으로 시작하게 만들어 공격자가 라이브러리를 호출하는 BOF공격을 하여도 NULL바이트가 삽입된 주소로 접근할 수 없게 됨으로 메모리를 보호하게 된다.





4) Canary


| BUFFER | SFP | RET |
위 처럼 되있는 메모리 구조가
             

| BUFFER | SFP | CANARY | RET  

이렇게 SFP와 RET 데이터 사이에 CANARY가 추가되어 스택의 BOF를 모니터링하는 기법이다. BOF가 발생하면 CANARY 데이터값의 변조로 오버플로우에 대한 경고를 하고 프로그램을 종료시킵니다. 

CANARY 데이터는 다음과 같이 구성되어 있다.

1.  Terminator canaries
문자열 끝문자를 이용하여 canary 를 구성하는 방법으로 
canary 값으로 NULL, CR, LF, Oxff 값의 조합이 사용됩니다. 
공격자는 공격시, 종료문자로 구성된 canary 값에 접근을 할 수 없게됩니다.

2.  Random canary
프로그램을 실행할 때마다 임의의 canary 값을 삽입을 합니다. 이에 따라 공격자는
Canary 값을 예측불가능하므로 공격에 어려움이 발생합니다.

3.  Null canary(0x00000000)
메모리상의 공격을 막기위해 canary 값을 NULL로 구성합니다. 
공격자는 공격코드상에 NULL 값을 삽입할 수 없으므고 canary 값에 접근이 불가능합니다.





참조:

http://justine100.tistory.com/15

http://younges.tistory.com/639



WRITTEN BY
Who1sth1s

,

1. 새로운 64 bit 에서의 인자전달 방식은?



 기존 방식과 다르게 인자를 전달하기 위해 레지스터를 4개만 사용한다.

정수형 - rcx, rdx, r8, r9

실수형 - xmm0, xmm1xmm2xmm3

함수 호출시 인자가 4개 미만이라도 레지스터를 위한 공간을 예약해두어야 된다.



2. 느낀점


32 bit에서 64 bit 로 바뀌면서 데이터를 한번에 더 많이 담기 위해 여러개의 레지스터를 사용하는 거같다. 덕분에 더 많은 양의 메모리를 사용할 수 있겠지만 메모리가 낭비될 수도 있을거 같다.










WRITTEN BY
Who1sth1s

,

1.함수의 호출


함수를 호출할때 어떻게 스택에 어떻게 PUSH하고 어떻게 POP할까?


함수 호출은 크게 다음과 같이 나뉘어 진다.

1.함수가 사용할 파라메터를 스택에 넣고 함수 시작지점으로 점프(함수 호출)한다.

2.함수 내에서 사용할 스택프레임을 설정한다. (프롤로그)

3.함수의 내용을 수행한다.

4.수행을 마치고 처음 호출한 지점으로 돌아가기 위해 스택을 복원한다. (에필로그)

호출한 지점의 다음 라인으로 점프한다.

이때 2번 과정을 프롤로그(prolog) 라고 부르며, 4번 과정을 에필로그(epilog) 라고 부른다. 보면 알겠지만 스택프레임의 설정과 복원과 관계가 있다.


그렇다면 함수 프롤로그와 에필로그가 어떤식으로 될까?

(by 어셈블리어)



2.함수 프롤로그 & 에필로그


함수의 프롤로그와 에필로그는 함수호출 규약에 따라 조금씩 다르다.



대표적으로 cdecl 을 보면 다음과 같다.


-cdecl 


특징

  • 스택에 파라메터 삽입 순서 : right → left
  • 스택의 정리를 호출한 함수(caller)에서 수행한다. 따라서 가변인자를 사용할 수 있다.
  • Name Mangling : 함수 이름 앞에 _ 추가 
    ex) _Foo
  • C / C++ 언어의 기본 함수 호출규약

int main() { foo(); /* foo 함수 호출 call foo (128102Dh) add esp, 8 ; (6) <<스택을 정리하는 부분>> */ return 0; }

위 코드를 보면 역순으로 파라메터를 push 하고 함수호출을 한 후, 이어지는 다음 라인에서 스택을 정리함을 알 수 있다. 즉 caller 가 직접 스택을 정리한다.


int __cdecl foo( int a, int b ) { /* caller 의 ebp 를 저장하고 callee (foo) 의 ebp 를 확보 push ebp mov ebp, esp push ecx ; local 변수 c 의 자리 확보 */  

int c;

  1. caller 의 ebp 를 저장하고 callee 의 ebp 를 새로 확보한다. 이 새로운 ebp 를 이용하여 foo 함수 내에서 파라메터 및 local 변수로의 접근을 시도할 것이다. 그리고 local 변수에 사용할 스택을 할당하는데 여기서는 4바이트 변수 하나이므로 단순히 ecx 를 push 함으로써 이를 수행한다. 하지만 local 변수들이 8바이트 (혹은 그 이상) 일 경우 “sub esp, 8” 과 같이 스택을 할당하게 된다.

} /* foo 함수 종료 후 caller 의 ebp, esp 복구 mov esp, ebp pop ebp ret */

  1. caller 의 ebp 를 저장하고 callee 의 ebp 를 새로 확보한다. 이 새로운 ebp 를 이용하여 foo 함수 내에서 파라메터 및 local 변수로의 접근을 시도할 것이다. 그리고 local 변수에 사용할 스택을 할당하는데 여기서는 4바이트 변수 하나이므로 단순히 ecx 를 push 함으로써 이를 수행한다. 하지만 local 변수들이 8바이트 (혹은 그 이상) 일 경우 “sub esp, 8” 과 같이 스택을 할당하게 된다.





함수 프롤로그


/* caller 의 ebp 를 저장하고 callee (foo) 의 ebp 를 확보 push ebp mov ebp, esp push ecx ; local 변수 c 의 자리 확보 */  


베이스포인터(ebp)를 스택에 저장하고 현재 스택포인터(esp)를 베이스포인터(ebp)에다가 저장함.




함수 에필로그


} /* foo 함수 종료 후 caller 의 ebp, esp 복구 mov esp, ebp pop ebp ret */


현재 스택 포인터(esp)를 베이스포인터(ebp)로 복구한 후 베이스 포인터(ebp)를 복구해주고 ret를 통해 다음에 가야에 adress로 점프한다. 





참고 : hhttp://z3moon.com/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/%ED%98%B8%EC%B6%9C%EA%B7%9C%EC%95%BD


WRITTEN BY
Who1sth1s

,

1. 메모리 구조

 

 프로그램을 실해시키면 로더(디스크에서 주기억장치로 가져옴)에의해 프로그램이 주기억 장치인 메모리(Random Access Memory)에 적재된다. 이렇게 적재된 프로그램은 메모리상에서 스택(First In, Last Out)의 형태로 구현된다. 




위와 같이 메모리에 프로그램이 적재되었을때는 크게 5가지 종류로 메모리가 나누어 져 있다.


1. CODE 

-프로그램의 소스코드가 저장된다. 



2-1. DATA

-전역변수(global), 정적변수(static), 배열(array), 구조체(structure) 등이 저장된다. 초기화 된 데이터는 data 영역에 저장되고


2-2. BSS

-초기화 되지 않은 데이터는 BSS (Block Stated Symbol) 영역에 저장된다.


3. HEAP 

-크가가 가변한다. 프로그래머의 필요에 따라 할당하여 사용할 수있다. 위에서 부터 채워져 내려옴.


4. STACK

-크기가 가변한다. 지역변수가 저장된다. 스택에는 여러개의 스택 프레임이 존재한다. 데이터 용량의 불확실성을 가지므로 밑에서부터 채워 올림, 또한 커널영역을 침범하지 않게 하기위해 밑에서부터 채워 올림 (스택 프레임에 대한 설명은 아래 참고)




+)HEAP과 STACK이 서로 반대로 채워 나가기 떄문에 서로의 영역을 침범할 수 있음. (이를 악용해 공격도 가능하다)


HEAP overflow

-heap이 위에서부터 주소값을 채워져 내려오다가 stack영역을 침범하는 경우.

STACK overflow

-stack영역이 heap을 침범.




2. 스택프레임


어떤 함수가 호출되었을 때 그 함수가 가지는 공간 구조이다.


예를들어

void main() {

printf("Hellow");

}

위와같은 프로그램을 만들었다면 프로그램이 실행 되었을때 main()이라는 함수가호출 되고 그 다음에 printf()라는 함수가 호출 된다.



사진으로 나타내면 위와 같다.

숫서를 나타내면

프로그램 시작

main() push

printf() push

printf() pop

main() pop

프로그램 종료

위와 같다고 볼수 있다.



 스택프레임은 코딩의 순서에 맞춰 LIFO 구조로 차곡차곡 쌓여진다. 


이 때 esp라는 포인터가 사용되게 되는데 흔히 스택 포인터라고 하며 현재 데이터의 위치를 알려준다. 


그리고 ebp라는 흔히 베이스 포인터라고 일컫는 또 다른 포인터가 있는데, 이는 스택 프레임이 시작된 위치의 주소값을 가르키며 


나중에 esp시작점으로부터 얼마나 벗어나 있는지를 가리킨다. 


스택 프레임 해당 스택 프레임의 함수가 종료될때 함께 소멸된다. 


ebp돌아와야 할 주소 값을 가리킨다.


참고 : https://prezi.com/obkhqdxaz3zx/programming-compile-loading-for-korean/


더 알아보기 : http://warmwrite.tistory.com/5





3.함수 호출 규약


.함수 호출 규약이란 함수가 호출 되었얼때 전달 되었던 피라미터들에 대하여 함수 호출이 끝난 후 스택을 저리하는 방법에 대한 약속이다.

즉, ESP를 정리하는 방법이다.


위 처럼 비주얼베이직에서 속성을 통해 함수 호출 규칙을 직접 정할 수도 있다. 그러면 각 방법은 무엇이 다를까?


*)용어

caller : 함수를 호출한 쪽

callee : 호출 당한 함수


1. cdcel

- 주로 C언어에서 사용 된다.

- caller(함수를 호출한 쪽)에서 스택을 정리한다.

- 장점 : 가변 길이 파라미터를 전달할 수 있다.


2. stdcall

- Win32 API에서 사용됨

- callee(호출 당한 함수)에서 스택을 정리 -> 가변인자 불가능

-함수의 호출이 끝나고 존재하는 스택 정리 코드가 없어서 비교적 빠름.



3. fastcall

-이름에서 알 수있듯이 좀 더 빠른 함수 호출을 위해 고안된 방식이다. CPU가 주기억 장치에서 인자를 불러다 쓸경우보다 레지스터에서 바로 떙겨 쓰는 것이 훨씬 빠르기 때문에 피라미터의 마지막 2개를 레지스터(ECX, EDX)를 이용해 전달한다. (나머지는 스택에 전달) 

- 장점 : 좀더 빠른 함수 호출 가능

- 단점 : ECX, EDX에 대하여 백업이 필요하거나, 함수에서 ECX, EDX를 다른 용도로 써야할 경우, 파라미터를 따로 저장해야함

   


참고 : http://orang.tistory.com/entry/%ED%95%A8%EC%88%98-%ED%98%B8%EC%B6%9C-%EA%B7%9C%EC%95%BDCalling-Convention


http://girtowin.tistory.com/89







WRITTEN BY
Who1sth1s

,

1.어셈블리어 기초

 

기계어아 1:1 대응이 가능(어셈블러를 통해) cup마다 지원하는 타입과 개수는 제각각이다.


데이터 타입

타입설명
BYTE8비트 부호 없는 정수
SBYTE8비트 부호 있는 정수
WORD16비트 부호 없는 정수
SWORD16비트 부호 있는 정수
DWORD32비트 부호 없는 정수
SDWORD32비트 부호 있는 정수
FWORD48비트 정수
QWORD64비트 정수
TBYTE80비트 정수

 

피연산자(operand) 타입

피연산자설명
r88비트 범용 레지스터
r1616비트 범용 레지스터
r3232비트 범용 레지스터
Reg임의의 범용 레지스터
Sreg16비트 세그먼트 레지스터
Imm8, 16, 32비트 즉시 값
imm88비트 즉시 값
imm1616비트 즉시 값
imm3232비트 즉시 값
r/m88비트 범용 레지스터, 메모리
r/m1616비트 범용 레지스터, 메모리
r/m3232비트 범용 레지스터, 메모리
mem8, 16, 32비트 메모리

 

어셈블리 명령어

□ INC(increase)

피연산자에 1을 더한다. 연산 결과에 따라 ZF나 OF가 세트 될 수 있다.

INC reg

INC mem

 

□ DEC(Decrease)

피연산자에서 1을 빼는 명령이다. 연산 결과에 따라 ZF나 OF가 세트 될 수 있다.

DEC reg

DEC mem

 

□ ADD(Add)

Destination에 Source의 값을 더해서 Destination에 저장하는 명령이다. 연산결과에 따라서 ZF, OF, CF가 세트 될 수 있다.

ADD destination, source

ADD reg, reg

ADD reg, imm

ADD mem, reg

ADD mem, imm

ADD reg, mem

 

ADD eax, 123

위 명령은 eax 레지스터에 123을 더해서 eax 레지스터에 저장한다.

 

□ SUB(Subtract)

Destination에 Source의 값을 빼서 Destination에 저장하는 명령이다. 연산 결과에 따라서 ZF, OF, CF가 세트(1)될 수 있다.

SUB destination, source

SUB reg, reg

SUB reg, imm

SUB mem, reg

SUB mem, imm

 

SUB eax, 123

위 명령은 eax 레지스터에 123을 빼서 eax 레지스터에 저장한다.

 

□ MUL(Multiply Unsigned Integer)

부호 없는 al, ax, eax의 값을 피연산자와 곱한다. 피연산자가 8비트이면 al과 곱해서 ax에 저장되고, 16비트이면 ax와 곱하고 dx:ax에 저장된다.

MUL reg

MUL mem

결과에 따라 OF, ZF가 세트(1) 될 수 있다.

 

□ IMUL(Integer Multiplication)

부호 있는 al, ax, eax의 값을 피연산자와 곱한다. 결과에 따라 CF, OF가 세트(1) 될 수 있다.

IMUL r/m8

IMUL r/m16

IMUL r/m32

단일 피연산자이고, 피연산자를 al, ax, eax에 곱한다.

 

IMUL destination, value

IMUL r16, r/m16            IMUL r16, imm8

IMUL r32, r/m32            IMUL r32, imm8

IMUL r16, imm16           IMUL r32, imm32

value를 al, ax, eax와 곱해서 destination에 저장한다.

 

IMUL destination, value, value

IMUL r16, r/m16, imm8               IMUL r16, r/m16, imm16

IMUL r32, r/m32, imm8               IMUL r32, r/m32, imm32

value끼리 곱해서 destination에 저장한다.

연산결과가 destination 레지스터의 크기보다 크다면 OF, CF가 세트(1)된다.

 

□ DIV(Divide Unsigned Integer)

8, 16, 32비트 부호 없는 정수의 나눗셈을 수행한다.

DIV reg

DIV mem

결과에 따라서 CF, OF, ZF가 세트(1) 될 수 있다.

 

□ MOV(Move)

Source에서 Destination으로 데이터를 복사한다.

MOV Destination, Source

MOV reg, reg

MOV reg, imm

MOV mem, reg

MOV mem, imm

 

□ MOVS(Move String)

Source에서 Destination으로 데이터를 복사한다.

MOVS   Destination, Source

 

□ MOVSB, MOVSW, MOVSD(Move String)

SI 또는 ESI 레지스터에 의해 지정된 메모리 주소의 내용을 DI 또는 EDI 레지스터에 의해 지정되는 메모리 주소로 복사한다.

MOVSB는 BYTE 단위로 복사, MOVSW는 WORD 단위로 복사, MOVSD는 DWORD 단위로 복사한다. 방향 플래그(DF)가 1로 세트 되어 있으면 ESI와 EDI는 복사 시에 감소하게 되고, DF가 0으로 세트 되어 있으면 ESI와 EDI는 복사 시에 증가하게 된다.

MOVSB

MOVSW

MOVSD

 

□ MOVSX(Move with Sign-Extended)

BYTE나 WORD 크기의 피연산자를 WORD나 DWORD 크기로 확장하고 부호는 그대로 유지한다.

MOVSX reg32, reg16       MOVSX reg32, mem16

MOVSX reg16, reg8        MOVSX reg16, mem8

 

□ MOVZX(Move with Zero-Extended)

BYTE나 WORD 크기의 피연산자를 WORD나 DWORD 크기로 확장하고 남은 비트는 0으로 채운다.

MOVZX reg32, reg16      MOVZX reg32, mem16

MOVZX reg16, reg8        MOVZX reg16, mem8

 

□ INT(Interrupt)

소프트웨어 인터럽트를 발생시켜 운영체제의 서브루틴을 호출한다.

INT imm

INT 3

 

□ AND(Logical AND)

Destination과 Source 피연산자의 각 비트가 AND 연산된다. AND 연산을 통해서 OF, CF가 0으로 세트 되고 결과에 따라서 ZF가 1로 세트 될 수 있다.

AND reg, reg                AND reg, imm

AND mem, reg              AND mem, imm

AND reg, mem

 

□ OR(Inclusive OR)

Destination과 Source 피연산자의 각 비트가 OR 연산된다. OR 연산을 통해서 OF, CF가 0으로 세트 되고 결과에 따라서 ZF가 1로 세트 될 수 있다.

OR reg, reg                  OR reg, imm

OR mem, reg                OR mem, imm

OR reg, mem

□ XOR(Exclusive OR)

Destination과 Source 피연산자의 각 비트가 XOR 연산된다. XOR 연산을 통해서 OF, CF가 0으로 세트 되고 결과에 따라서 ZF가 1로 세트 될 수 있다. 피연산자의 두 값이 같은 값이라면 결과는 항상 0이 된다.

레지스터를 0으로 초기화 시킬 때 MOV 명령어를 사용하기보다는 XOR reg, reg로 많이 사용한다.

XOR reg, reg                XOR reg, imm

XOR mem, reg              XOR mem, imm

XOR reg, mem

 

□ TEST(Test)

두 피연산자 사이에 논리적인 AND 연산을 수행하여 플래그 레지스터에 영향을 주지만 결과값은 저장하지 않는다. OF, CF는 항상 0으로 세트 되고 TEST 연산 결과값이 0이면 ZF가 1로 세트, 0이 아니면 ZF가 0으로 세트 된다.

TEST reg, reg                TEST reg, imm

TEST mem, reg              TEST mem, imm

TEST reg, mem

 

□ STC(Set Carry Flag)

캐리 플래그(CF)를 1로 세트 한다.

STC

 

□ CLC(Clear Carry Flag)

캐리 플래그(CF)를 0으로 세트 한다.

CLC

 

□ STD(Set Direction Flag)

방향 플래그(DF)를 1로 세트 한다.

STD

 

□ CLD(Clear Direction Flag)

방향 플래그(DF)를 0으로 세트 한다.

CLD

 

□ STI(Set Interrupt Flag)

인터럽트 플래그(IF)를 1로 세트 한다.

STI

 

□ CLI(Clear Interrupt Flag)

인터럽트 플래그(IF)를 0으로 세트 한다.

CLI

 

□ SHL(Shift Left)

Destination 피연산자를 Source 피연산자의 크기만큼 왼쪽으로 각 비트를 시프트 시킨다. 최상위 비트는 캐리 플래그(CF)로 복사되고 최하위 비트는 0으로 채워진다.

SHL reg, imm8              SHL mem, imm8

SHL reg, CL                  SHL mem, CL

 

□ SHR(Shift Right)

Destination 피연산자를 Source 피연산자의 크기만큼 오른쪽으로 각 비트를 시프트 시킨다. 최상위 비트는 0으로 채워지고, 최하위 비트는 캐리 플래그(CF)로 복사된다.

SHR reg, imm8              SHR mem, imm8

SHR reg, CL                  SHR mem, CL

 

□ PUSH(Push on Stack)

스택에 값을 넣는다. ESP의 값이 4만큼 줄어들고 이 위치에 새로운 값이 채워진다.

PUSH reg16                  PUSH reg32

PUSH mem16                PUSH mem32

PUSH imm16                 PUSH imm32

 

□ PUSHAD(Push All)

EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP 레지스터의 값을 스택에 PUSH한다. 레지스터의 값을 보관해야 할 필요가 있을 때 사용한다.

PUSHAD

 

□ PUSHFD(Push Flags)

플래그 레지스터를 스택에 PUSH한다. 플래그 레지스터의 값을 보관해야 할 필요가 있을 때 사용한다.

PUSHFD

 

□ POP(Pop from Stack)

ESP 레지스터가 가리키고 있는 위치의 스택 공간에서 4Byte 만큼을 Destination 피연산자에 복사하고 ESP 레지스터의 값에 4를 더한다.

POP destination

POP reg16                   POP reg32

POP mem16                 POP mem32

 

□ POPAD(Pop All from Stack)

스택에 존재하는 값을 EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP 레지스터로 POP한다. PUSHAD 명령어로 스택에 보관해 놓은 레지스터 정보를 다시 이용하려고 할 때 사용한다.

POPAD

 

□ POPFD(Pop Flags from Stack)

스택에 존재하는 값을 플래그 레지스터로 POP한다. PUSHFD 명령어로 스택에 보관해 놓은 레지스터 정보를 다시 이용하려고 할 때 사용한다.

POPFD

 

□ XCHG(Exchange)

두 피연산자 내용이 서로 교환된다. XCHG 명령은 imm 값이 피연산자로 올 수 없다.

XCHG reg, reg

XCHG reg, mem

XCHG mem, reg

 

□ NEG(Negate)

피연산자의 2의 보수를 계산하여 결과를 피연산자에 저장한다.

NEG reg

NEG mem

 

□ PTR

피연산자의 크기를 재설정한다.

MOV eax, DWORD PTR value

위의 명령어는 value의 크기를 DWORD 크기로 재설정하여 eax 레지스터에 복사하라는 의미이다.

 

□ OFFSET

세그먼트의 시작으로부터 변수가 위치한 거리까지의 상대적 거리를 리턴한다.

MOV esi, OFFSET value

위 명령어는 value가 존재하는 위치를 세그먼트 시작 지점으로부터의 상대적 거리로 구해서 esi 레지스터에 복사하라는 의미이다.

 

□ LEA(Load Effective Address)

Source 피연산자의 유효 주소를 계산하여 Destination 피연산자에 복사한다. 간단히 주소를 알아내서 복사하는 명령어라고 생각하면 쉽다.

LEA reg, mem

 

□ REP(Repeat String)

ECX 레지스터를 카운터로 사용해서 문자열 관련 명령을 ECX > 0인 동안 반복한다.

REP MOVS destination, source

 

□ JMP(Jump Unconditionally to Label)

피연산자의 위치로 실행 흐름이 변경된다. 피연산자가 가리키는 코드로 점프해서 실행한다고 생각하면 된다. 피연산자에는 레이블이나 레지스터, 메모리 값이 올 수 있다.

short 점프는 -127~127바이트 범위 안에서 사용되고, near 점프는 같은 세그먼트 내부에서 사용된다. far 점프는 현재 세그먼트를 벗어날 때 사용되며 JMP 명령어는 되돌아올 리턴 어드레스 값을 저장하지 않는다. 이 명령어는 무조건 점프 명령어인데 점프 명령은 어떤 형식에 맞을 때만 점프하는 조건 형식의 점프 명령도 있다.

JMP shortlabel              JMP reg16

JMP nearlabel               JMP mem16

JMP farlabel                 JMP mem32

 

□ CALL(Call a Procedure)

함수 호출 시 사용된다. JMP 명령어 같이 프로그램의 실행 흐름이 변경되지만 JMP 명령어와 다른 점은 되돌아올 리턴 어드레스(CALL 다음 명령)를 스택에 저장한다는 것이다. 되돌아올 주소를 저장하기 때문에 함수 호출 후 원래 위치로 실행 흐름을 되돌릴 수 있다. 호출한 함수가 일을 다 마치면 원래 위치에서 다시 프로그램이 실행될 수 있음을 의미한다.

CALL nearlabel              CALL farlabel

CALL mem16                CALL mem32                CALL reg

(디버깅을 하면서 보게 되는 Call 형식들)

Call 함수 주소

Call DWORD PTR[EAX+5]

Call <JMP to API> 특정 api 지목

 

□ CMP(Compare)

두 피연산자를 비교하는 작업을 한다. Destination 피연산자에서 Source 피연산자를 묵시적으로 빼서 값을 비교한다. 두 피연산자의 값이 같다면 결과는 0이 되고 제로 플래그(ZF)가 1로 세트 된다. 다르다면 제로 플래그(ZF)는 0으로 세트 된다.

CMP reg, reg                CMP reg, imm

CMP mem, reg              CMP mem, imm

CMP reg, mem

 

□ NOP(No Operation)

아무 일도 하지 않는 명령어이다. 리버싱 작업에서 목적에 따라 유용하게 사용될 수 있다.

NOP

 

조건 점프 명령

조건 점프 명령은 JMP 명령어와는 다르게 CMP 명령 같이 특정 플래그 레지스터를 변경시킬 수 있는 명령어를 통해서 특정 조건이 만족하게 된다면 점프를 수행하게 되는 명령어이다. 명령어 자체가 처음 볼 땐 어떤 일을 하는지 이해하기가 힘들게 되어 있다. 다음에 여러 조건 형식의 점프 명령과 명령의 의미, 그리고 어떤 조건(플래그 레지스터의 상태)을 만족해야 명령어가 수행되는지 표로 정리하였다.

 

명령어

명령어의 의미

명령어가 수행되기 의한 플래그 레지스터와 범용 레지스터의 상태

JAJump if (unsigned) aboveCF=0 and ZF=0
JAEJump if (unsigned) above or equalCF=0
JBJump if (unsigned) belowCF=1
JBEJump if (unsigned) below or equalCF=1 or ZF=1
JCJump if carry flag setCF=1
JCXZJump if CX is 0CX=0
JEJump if equalZF=1
JECXZJump if ECX is 0ECX=0
JGJump if (signed) greaterZF=0 and SF=0
JGEJump if (signed) greater or equalSF=OF
JLJump if (signed) lessSF!=OF
JLEJump if (signed) less or equalZF=1 and SF!=OF
JNAJump if (unsigned) not aboveCF=1 or ZF=1
JNAEJump if (unsigned) not above or equalCF=1
JNBJump if (unsigned) not belowCF=0
JNBEJump if (unsigned) not below or equalCF=0 and ZF=0
JNCJump if carry flag not setCF=0
JNEJump if not equalZF=0
JNGJump if (signed) not greaterZF=1 or SF!=OF
JNGEJump if (signed) not greater or equalSF!=OF
JNLJump if (signed) not lessSF=OF
JNLEJump if (signed) not less or equalZF=0 and SF=OF
JNOJump if overflow flag not setOF=0
JNPJump if parity flag not setPF=0
JNSJump if sign flag not setSF=0
JNZJump if not zeroZF=0
JOJump if overflow flag is setOF=1
JPJump if parity flag setPF=1
JPEJump if parity is equalPF=1
JPOJump if parity is oddPF=0
JSJump if sign flag is setSF=1
JZJump if zero flag is setZF=1

 


출처 : http://sarghis.com/blog/357/



2.범용 레지스터


데이터와 주소를 모두 저장할 수 있는 레지스터.

컴퓨터의 중앙 처리 장치(CPU) 내에 있는 레지스터 중에서 계산 결과의 임시 저장, 산술 및 논리 연산, 주소 색인 등의 여러 가지 목적으로 사용될 수 있는 레지스터. 이것은 프로그램 카운터나 명령어 레지스터와 같이 특별한 용도로 사용되는 레지스터들에 대비되는 개념이다. 많은 컴퓨터가 CPU 내에 여러 개의 범용 레지스터를 갖고 있으며 레지스터 번호로 각각을 지정한다.



1. EAX(Extended Accumulator Register) 

-곱셈과 나눗셈 명령에서 자동으로 사용되고 함수의 리턴 값이 저장되는 용도로도 사용된다.


2. EBX(Extended Base Register) 

-ESI나 EDI와 결합하여 인덱스에 사용된다.


3. ECX(Extended Counter Register) 

-Dump창, 텍스트영역이나 힙등의 구조를 Hex형태로 확인할수 있다.


4. EDX(Extended Data Register) 

-EAX와 같이 쓰이며 부호 확장 명령 등에 쓰인다.


5. ESI(Extended Source Index) 

-데이터 복사나 조작 시 Source Data의 주소가 저장된다. ESI 레지스터가 가리키는 주소의 데이터를 EDI 레지스터가 가리키는 주소로 복사하는 용도로 많이 사용된다.


6. EDI(Extended Destination Index)

-복사 작업 시 Destination의 주소가 저장된다. 주로 ESI 레지스터가 가리키는 주소의 데이터가 복사된다.


7. ESP(Extended Stack Pointer)

-하나의 스택 프레임의 끝 지점 주소가 저장된다. PUSH, POP 명령어에 따라서 ESP의 값이 4Byte씩 변한다,


8. EBP(Extended Base Pointer)

-하나의 스택 프레임의 시작 지점 주소가 저장된다. 현재 사용되는 스택 프레임이 소멸되지 않는 동안 EBP의 값은 변하지 않는다, 현재 스택 프레임이 소멸되면 이전에 사용되던 스택 프레임을 가리키게 된다.



참고 : http://sarghis.com/blog/357/







WRITTEN BY
Who1sth1s

,

1.프로그램(디버거)

 

 올리디버거

-직관적인 인터페이스와 강력한 확장 기능을 가진 Win32 디버거로써, 무료로 제공되며 가볍고 빠르기 때문에 많은 리버서들이 애용.



2.실행



한번 올리디버거로 파일(팀프로젝트-심심이)을 가져와 보겠다







처음보는 에러다.. 구글링을 통해 알아보니깐 컴파일 할때 모드를 Debug에서 Release로 바꿔줘야된다고 한다.







바꾸고 다시 열어보았다.







시작할때 에러는 안나지만 error_proc_not_found (0000007F) 라는 에러가 발생해 멈춰있다.. 왜인지 모르겠다..







아쉬운데로 올리리버거의 기본적인 화면에 대해서 알아보자.


1. 분석된 어셈블리어 코드

2. 레지스터상태

3. Dump창, 텍스트영역이나 힙등의 구조를 Hex형태로 확인할수 있다.

4. 해당 프로세스의 스택상태

라고한다.





WRITTEN BY
Who1sth1s

,

1.정의


 리버싱이란 리버스 엔지니어링(reverse engineering, RE) 또는 역공학(逆工學)

이라고 불리며 이는 이미 만들어진 시스템을 역추적하여 처음의 문서나 설계기법 등의 자료를 획득하는 것을 의미한다.



2.기원 및 목적

 

  원래 리버싱은 상업적 또는 군사적으로 하드웨어 분석을 목적으로 사용 되었다가 현재는 오류, 미완성, 접근불가 문서를 수정하기 위해 사용됨. 즉, 만들어진 프로그램을 재분석하여 내부의 코드를 수정하거나 캐내는 목적을 지니게 됨.


3.방법 


 1. 정적분석

-파일을 실행하지 않고 컴파일된 바이너리의 종류(exe, dll, zip 등), 크기, 헤더(PE), Import/Export Api, 디버깅 정보 등을 알 수 있음.


 2. 동적분석

-파일을 실행하면서 행위를 분석, 디버깅을 통한 코드 흐름과 메모리 상태 관촬, 파일,레지스트리, 네트워크 등을 관찰& 분석 가능.

 




WRITTEN BY
Who1sth1s

,