constWORLDant

Self HomeWork 본문

0x03 Reversing Theory

Self HomeWork

data type ConS_tanT 2018.03.11 22:59

Self Home Work aka. review 

 

읽고 있는 책 : 윈도우 실행파일 구조와 원리로 배우는 리버스 엔지니어링 2권 디버거 편


오늘부터 읽기 시작했다.. 분명 저번주에 샀는데 ㅎㅎㅎㅎ 공부할게 너무나도 많다. 근데 다 할거다 재밌다. 


책을 읽다가 스스로 궁금해진 것을 메모해두고 자가 숙제처럼 공부를 진행해보았다. (새로운 시도랄까..)

 

1. FPU는 부동소숫점이 쓰인다. 하지만, FPU의 하위 64BIT인 MMX는 레지스터를 공유하고 있다는 이유로 부동소숫점연산이 

동시에 불가능 하다. 그렇다면, FPU의 상위 16BIT는 무슨 역할을 하는가?


FPU란 Floating Point Unit이라고 부르고, CPU의 일부로서 부동 소수점 연산을 효율적으로 처리하기 위한 하드웨어 논리회로 모듈이다.

부동소수점 실수에 대한 사칙연산을 지원하며, 모듈에 따라 거듭제곱뿐 아니라 삼각 함수 등 다양한 수학 계산을 할 수 있다.


공부해보니 MXX는 4가지로 쓰이게 된다.


1) A single 64bit Quadword


2) 2 32-bit DoubleWords


3) 4 16-bit Words


4) 8 8-bit Bytes 


그렇다면, 상위 16BIT는 도대체 뭐로 쓰이는가?

http://softpixel.com/~cwright/programming/simd/mmx.php에 들어가보면 그냥 xx표시로 되어있고

Notice how the top 16 bits of each 80-bit FPU register are unused in MMX mode.라고만 되어있다.

상기의 사이트에 MMX에 대해 엄청 상세하게 나와있다. 종종 헷갈릴 때 참고자료로 써야겠다.. ^^ 


The upper 16 bits of the x87 registers thus go unused in MMX, and these bits are all set to ones, making them NaNs or infinites..

여기에서 보면 알 수 있다. 상위 16비트는 1로 set되고, NaN이나 Infinity로 쓰이게 된다.


this can be used by applications to decide whether a particular register's content is intended as floating point or SIMD data

그리고, 특정 레지스터가 부동 소숫점 또는 SIMD Data중에 무엇으로 사용될 지 여부를 결정해준다고 한다.




2. CMP와 JLE를 사용하지 않고, rep를 이용하여 코드를 작성해보자.

-> 문자열 루프 


code 


//

// develope : c0nstant

// rep example 


// rep is use to loop about range of ECX


// this example is so simple. 


// goal : just one array loop.. 


// compile option : Release X86 && NON ASLR (coz Access Memory)

#include <stdio.h>

#include <string.h>


char word[] = "Hello REP";


int main(int argc, char*argv[])

{

int n = strlen(word);

int i = 0;

__asm

{

jmp init

init:

mov ecx, n

mov eax, dword ptr [0x403018] // n's addr 

mov esi, eax

lea edi, dword ptr ds:[0x403028] // used near the n's addr coz confirm the comportable

rep movs byte ptr es:[edi], byte ptr [esi] //0x403028에 복사 함 

pop ecx

}

return 0;

}

//

[사진 첨부]



3. DWORD PTR이 정확히 무엇인가?

쉽게만 생각하면 pointer인데 변수의 타입에 따라 조금씩 달라진다.


1round. 정수형 변수 


example. int i = 5;  // 4바이트의 정수형 공간에 5를 복사! (이동이라는 표현을 쓰면 안 됨)


'이동'이라고 표현하면 안되는 이유 : 예를들어 C드라이브에서 D드라이브로 파일을 옮긴다고 생각해보자.

C드라이브에 위치해 있는 test.c를 그냥 드래그 해서 D드라이브로 옮기게 되면 더이상 C드라이브에는 test.c가 존재할 수 없다.

잠시 프로그래밍 관점으로 건너가보자. 어셈블리어로 표현을 해보겠다.

mov eax, ecx가 있다고 가정을 해보자. 만약, mov가 "이동"의 의미였다면, ecx레지스터는 텅텅 비게 된다.

텅텅 비는게 말이 되는가? 말이 안된다. 왜냐하면 mov eax, ecx의 결과는 eax==ecx가 되기 때문이다.

그렇기 때문에 값의 복사가 맞는 표현이 된다. 


다시 본론으로 돌아가보자. 

int i =5;는 둘 중에 무엇일 까?

1) mov dword ptr[i], 0x5 

2) mov i,0x5 


정답은 1번이다. 직접 디스어셈블리로 본 화면을 올려두었다.

int i = 5;

00401037  mov         dword ptr [i],5  

왜 이렇게 되는 것일까? 우리는 변수에 대해서 배울 때 주소에 복사하는게 하는게 아니라, 주소 안에 값을 복사하는 것으로 배웠다.

그렇기 때문에 [i]가 맞는 표현이 되는 것이다. 


i의 주소를 한번 살펴보자. 

i는 지역변수로써 스택에서 놀기 때문에 0x0019FF24가 i의 주소가 된다.


[사진 첨부]


2round. 포인터 변수 


example. int *ptr = &i;

이건 어떻게 해석해야할까? *는 포인터 &는 주소연산자임은 알 것이다.

그런데 몇몇 사람들은 *는 주소다 라고 잘못알고있다.

*는 주소를 따라간 곳의 공간이다. 


즉, 0x400000번지에 10이라는 값이 들어 있고, i의 주소가 0x400000번지라고 가정하면

코드는 source(rvalue) -> target(lvalue) (C 기준)으로 읽으니까 

&i는 i의 주소, *ptr은 ptr을 따라간 공간의 값 

ptr을 따라가보니 i의 주소를 복사 한다. 그렇다면 ptr은 i의 주소 0x400000번지가 복사 될 것이다.



그렇다면, 얘는 어셈블리어로 표현하면 어떻게 될까? 


lea eax, [i]

mov dword ptr [ptr], eax


여기서 의문점이 하나 생길 수도 있다. 그냥 귀찮게 저러지 말고 mov [ptr], [i]하면 안되는지?

아쉽게도, 메모리에서 메모리의 복사는 이루어지지 않는다.

반드시 레지스터를 거쳐야 한다.


직접 코드를 짜서 에러를 확인해보자. 백문이 불여일견이다.


[ERROR]


[SUCCESS]



[사진첨부]



[사진 첨부]


3round. 레퍼런스 변수 


int &ptr = i;

레퍼런스는 cpp에서만 되는 문법이다. 

c에서는 모든 참조는 포인터로 수행을 하지만, cpp에서는 레퍼런스라고 해서 i의 별명이라고도 볼 수 있다.

리눅스로 치면 alias가 되겠다. 

즉 i값이 5라면 &ptr은 5가 되는것이다. 추가적으로 더 알려주자면, 참조가 된 뒤로는 바꿀 수가 없다.

음 일상생활에서도 1개의 이름에 다수의 별명은 되지만, 여러개의 이름에 다수의 별명을 자신이 소지 못한다.

프로그래밍의 세계도 어쩌면 일상생활이 녹여있다고 볼 수 있다?? 


레퍼런스 변수는 어셈블리어로 어떻게 나타낼 수 있을 까?


lea eax, [i]

004010EC  lea         eax,[i]  

mov dword ptr[_ptr], eax

004010EF  mov         dword ptr [_ptr],eax


아까와 동일하다. 하지만, 디버깅을 해보면 엄연히 다르다 사진을 첨부한다.



[사진 첨부]




4. 64비트 RIP 상대적 번지 지정 방식은?


우선, 64비트를 논하기 전에 3학년 때 학교에서 배웠던 주소지정방식에 대해 복습하는 시간을 가져보겠다.


주소지정방식 중에 몇 가지를 까먹은 것 같아 반성의 시간도 잠시 가지고...공부한것을 정리해본다. 


내용은 그렇게 어렵지 않다. 


1. 암시(묵시) 주소지정방식 (Implied)

말 그대로 암시적으로 슬쩍 주소를 지정해주는 방식을 의미한다. 

주소를 지정해줄 때는 offset을 신경 써야하는데, 데이터의 위치를 지정하지 않고 ALU나 스택의 데이터를 묵시적으로 지정하여 사용하는 방식을 의미한다.

Operand가 없는 명령이나 Operand가 1개인 명령어 형식에 사용된다.

데이터 위치 저장하지 않고 바로 가져다 쓰기 때문에 빠르긴 하지만, 수의 크기에 제한이 있다.


[OPCODE][OPERAND]


2. 직접 주소지정방식 (Directed)

Instruction에 Operand의 주소가 들어가게 된다. 

직접이라는 키워드에 걸맞게 레지스터에 액세스 되지 않고, CPU에서 데이터를 이용할 수 있어서 속도가 빠르다.

이 역시 명령어의 길이에 영향을 받기에 표현 가능한 데이터 값의 범위가 제한적인 단점이 있다.


[OPCODE][ADDRESS]   ---> Memory's Operand

 

3. 간접 주소지정방식 (Indirect)

Instruction의 주소필드는 실제 Operand의 주소 값을 가지는 곳의 주소를 가지고 있다. 

즉, 곧바로 해당주소로 점프하는게 아니라, 1차 관문으로 타겟의 주소가 저장된 곳의 주소로 가게 된다. (어후 복잡해)

이 방식은 명령어에 나타낼 주소가 할당 되어 있는 비트수로 나타낼 수 없을 때 사용되게 된다.


[OPCODE][ADDRESS] --> Memory's point to operand -> Memory's real operand 

거쳐서 가야하기 때문에 직접 주소지정방식보다 느릴 수 밖에 없다. 지름길 놔두고 뱅뱅 돌아가면 시간이 지연되니까..

(근데 얘도 이러고 싶어서 그러는건 아닐거야..)


4. 레지스터 주소 지정(Register Directed)

Directed라고 해서 2번의 직접 주소지정방식과 같다!! 이렇게 생각하면 안 된다. 엄연히 가리키는 것이 다르다.

직접 주소지정방식은 "RAM의 주소"를 가리키고, 레지스터 주소 지정은 주소 필드가 레지스터를 가르키기 때문이다.

우리의 cpu에는 레지스터의 갯수가 제한적이다. 메모리 보다 레지스터의 데이터 교환시간이 더 빠르다. 


[OPCODE][Register ADDRESS] -> Register's Operand (Direct)

 

5. 레지스터 간접 주소지정 (Register Indireted)

레지스터 역시 간접 주소지정이 있다. 


5. P.79 그대로 배낀 뒤 직접 인라인어셈블리로 작성해보기

0 Comments
댓글쓰기 폼