반응형


1.3. add() 함수 파라미터 입력 및 add() 함수 호출




add() 함수에는 a, b 2개의 파라미터가 존재합니다.

401034 ~ 40103B 주소의 명령어들은 변수 a, b 를 스택에 저장하고 있습니다.



[EBP-8]에는 2가 [EBP-4]에는 1이 저장되게 되는데 C언어의 입력 순서와는 반대로 2부터 저장이 되고있습니다.


1.3.1 복귀주소



CALL 명령어( 40103C )가 실행되어 해당 함수로 들어가기 전에 CPU는 무조건 해당 함수가 종료될 때 복귀할 주소를 스택에 저장합니다.



CALL 명령어 ( 40103C )를 실행한 직후 스택에는 복귀주소가 저장되어 있습니다.


1.4. add() 함수 시작 & 스택 프레임 생성




① StackFrame 게시물의 Main() 함수 부분과 동일합니다.

원래의 EBP 값을 스택에 저장 후 현재의 ESP를 EBP에 넣습니다.


1.5. add() 함수의 로컬 변수( x, y )세팅




① : 스택 메모리 영역 ( 8 byte ) 확보

② : [ EBP + 8 ] = a

③ : [ EBP - 8 ] = x

④ : [ EBP + C ] = b

⑤ : [ EBP - 4 ] = y



1.6. ADD 연산




① : EBP-8에 있는 변수 x의 값 1을 MOV명령어를 통해 EAX로 넣는다.

② : EBP-4에 있는 변수 y의 값 2를 ADD명령어를 통해 EAX의 값 1과 더한다 = 3


1.7. add() 함수의 스택 프레임 해제 & 함수 종료 ( 리턴 )


이제 add() 함수의 스택 프레임을 해제 시키고 함수를 리턴시킬 차례입니다.



앞선 MOV EBP, ESP에 대응하는 명령어입니다.

add() 함수가 시작할 때 ESP값을 EBP에 넣어 보존했다가 함수가 종료될 때 ESP값을 복원시키는 겁니다.



앞선 PUSH EBP에 대응하는 명령어입니다.

add() 함수가 시작되면서 스택에 백업한 EBP값을 복원하고 스텍프레임을 해제시킵니다.


이제, RETN명령어를 실행시키게되면 스택에 저장된 복귀 주소로 리턴하게됩니다.



add() 함수를 호출하기 전의 스택상태로 돌아왔습니다.


1.8. add() 함수 파라미터 제거 ( 스택 정리 )



add() 함수가 완전히 종료되었기 떄문에 파라미터 a,b가 필요 없습니다.

따라서 ESP에 8을 더하여 스택을 정리하는 부분입니다.


1.9. printf() 함수 호출


자, 이제 프로그래밍 공부를하며 늘 써왔고 궁금했던 printf() 에 대해 알아보겠습니다.



이전의 과정에서 EAX에 1과 2를 더한 3을 넣어놨었습니다.

PUSH 40B384에는 %d가 들어있고 CALL 401067 명령어는 printf() 함수입니다.

printf() 안에 2개의 파라미터가 있기 때문에 ADD ESP, 8 로 정리해줍니다.


1.10. 리턴 값 세팅




'XOR' Exclusive OR bit 연산은 같은값 끼리 XOR하면 0이 되는 연산입니다.

MOV EAX, 0 명령어보다 실행속도가 빨라 자주 사용된다고합니다.


XOR 아래의 코드들은 나머지 부분은 앞에서했던 MOV, POP RETN 으로 같은 동작을 하며 메인 함수가 종료됩니다.



이번 실습을 진행하면서 막히는 부분도 많았고 꽤 어려웠습니다.

하지만 스택 프레임을 어설프게나마 공부해보니 실력이 많이 향상된 것 같고 리버싱 공부 의지가 더욱 불타는 것 같습니다.


오늘은 여기까지입니다 감사합니다 !




참고서적 이승원 「리버싱 핵심원리」 인사이트(2018) p82~91

반응형




안녕들 하시죠!


오늘은 스택 프레임에 대해 알아보겠습니다.


스택 프레임은 프로그램에서 선언되는 로컬 변수와 함수 호출에 사용됩니다.

이번 실습을 진행하기 위해서는 지난시간에 공부했던 'IA-32 Register' 의 ESP, EBP 개념이 필요합니다.


ESP는 스택포인터로서 프로그램 안에서 ESP 레지스터는 수시로 변경되기 때문에 스택에 저장된 변수, 파라미터에 접근하고자 할 때 ESP 값을 기준으로 하면 프로그램을 만들기 힘들고, CPU가 정확한 위치를 참고할 때 어려움이 있습니다.

따라서, 어떤 기준 시점( 함수 시작 )의 ESP값을 -> EBP에 저장하고 이를 함수내에서 유지해줍니다.

그러면 ESP값이 아무리 변하더라고 EBP를 기준으로 안전하게 해당 함수의 변수, 파라미터, 복귀 주소에 접근이 가능합니다.

이것이 EBP 레지스터의 베이스 포인터 역할입니다.


PUSH EBP  

함수 시작 ( EBP를 사용하기 전에 기존의 값을 스택에 저장한다 )


MOV EBP, ESP

현재의 ESP( 스택포인터 ) 를 EBP에 저장.


///


함수 본체 

여기서 ESP가 변경되더라도 EBP에 저장되어 있으므로 안전하게 로컬 변수와 파라미터를 엑세스 할 수 있다.


///


MOV ESP, EBP

ESP를 정리 ( 함수 시작했을 때의 값으로 복원시킴 )


POP EBP

리턴되기 전에 저장해 놓았던 원래 EBP 값으로 복원.


RETN

함수종료


이런 식으로 스택 프레임을 이용해 함수 호출을 관리하면 스택을 완벽하게 관리할 수 있습니다.


이제, 아주 간단한 프로그램을 통해 스택 프레임을 이해해보겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
// StackFrame.cpp
#include<stdio.h>
 
long add(long a, long b){
    long x = a, y = b;
    return (x+y);
}
 
int main(int argc, char* argv[]){
    long a = 1, b = 2;
    printf("%d\n", add(a,b));
    return 0;
}
cs


위에있는 코드를 StackFrame.exe로 빌드한 후 Ollydbg를 이용해 열어보겠습니다.

아래에 파일도 같이 첨부하겠습니다.


StackFrame.exe



Go to 명령어로 401000 주소로 이동하겠습니다.


위에 올렸던 코드를 부분부분 찾아 이해해보겠습니다. 


1. StackFrame.exe


1.1. Main() 함수 


우선, Main() 함수 부분 ( 201020 ) 주소 에 BP를 설치[F2] 한 후 [F9]로 실행하겠습니다.




ESP 19FF3C, EBP 19FF80 이고 아래 ④번을 보시면 ESP가 가리키고 있는 19FF3C에 저장된 값 401250은 Main() 함수의 실행이 끝난 후,

돌아갈 리턴 주소입니다.


00401020        PUSH EBP

위의 명령은 'EBP값을 스택에 집어넣어라' 라는 뜻입니다. 

Main() 함수에서 EBP가 베이스 포인터의 역할을 하게 될테니 EBP가 이전에 가지고 있던 값을 스택에 백업해두기 위함입니다.


00401021        MOV EBP, ESP

위의 명령은 'ESP값을 EBP로 옮겨라' 라는 뜻입니다.



이젠 ESP와 EBP 둘다 19F88값을 가지게 되었구요, 19FF80 주소에는 19FF80이라는 값이 저장되어 있습니다.

19FF80은 Main() 함수가 시작할 때 EBP가 가지고 있던 초기 값입니다.


1.2. 지역 변수



로컬 변수 ( a, b )를 위한 공간을 만들고 값을 입력한다.

위의 명령은 'ESP 값에서 8을 빼라' 라는 뜻입니다.



위의 자료형 크기표를 보시면 long 은 4byte로 되어있습니다.

a 와 b 는 long 타입으로 선언되었습니다.

long 타입이 2개이니 8byte를 SUB로 빼주면 a와 b 두 변수가 스택에 저장될 공간이 생기게 됩니다.


위의 명령은 '[EBP-4] 에는 1을 넣고, [EBP-8] 에는 2를 넣어라' 입니다.



다음 게시물에 이어서...



참고서적 이승원 「리버싱 핵심원리」 인사이트(2018) p75~82

반응형




안녕들 하시죠 !


드디어 개인적으로 첫번째 목표인 crackme 부분까지 오게 됬습니다.

이부분을 공부하며 어셈블리어와 친숙해지고 싶습니다.

abex' crackme #1 파일을 가지고 조금 시간이 걸리더라도 하나하나 분석해서 알고 넘어가볼 생각입니다.

아래의 샘플을 사용하시면 됩니다.


abex' crackme #1.exe


한번 실행을 해보겠습니다.


내가 생각하게 만들어라 너의 HD가 CD-Rom이라고... 뭔소리지 했습니다.

확인을 눌러보면,


요런 식으로 Error가 뜨네요.


Ollydbg로 열어보겠습니다.


요런식으로 뜨네요.

음 추측을 해보자면... 출력부를 보면 여기가 Main문인것 같긴하고, 40103D부분을 출력하게되면 성공하는것 같네요.

책을 찾아보니 401013주소의 GetDriveType() API로 C드라이브의 타입을 얻어오는데 이걸 조작해야 합니다.


Win32 API 코드를 아예 몰라서 MessageBoxA() 부분만 한번 가볍게 찾아봤습니다.

crackme #1 같은 경우 이정도만 알아도 이해가 가능했습니다.

추후에 더 공부해보고 틀린 부분이있다면 수정하겠습니다.


MessageBoxA( ①, ②, ③, ④ )


① : hOwner ( 메시지 박스의 오너 윈도우. 메시지 상자를 소유한 윈도우를 말하며 메시지 박스가 떠있는동안 다른 행위를 할수없다 )

② : text ( MessageBox에 출력할 텍스트, 여기에선 Make me think your HD is a CD-Rom 부분 )

③ : title ( MessageBox 윗부분의 텍스트, 여기에선 abex' 1st crackme 부분 )

④ : style ( MB_OK = OK버튼 1개, MB_OKCANCEL = OK와 CANCEL 총 2개 버튼 등 몇몇 종류의 값이 있습니다.)


abex' crackme #1 파일은 어셈블리 언어로 만들어졌기 때문에 보시는바와 같이 코드가 간결합니다.

VC++, VC, Delphi 등의 개발툴을 사용하면 Stub Code가 추가되서 저번시간처럼 복잡한 코드를 보게 되는거였습니다.


먼저, 어셈블리 명령어를 알아보고 한줄한줄씩 직접 이해하고 넘어가보겠습니다.


명령어 

설명 

 PUSH

스택에 값을 입력 

 CALL

지정된 주소의 함수를 호출 

 INC

값을 1 증가 

 DEC

값을 1 감소 

 JMP

지정된 주소로 점프 

 CMP

주어진 두 개의 operand 비교

* SUB 명령어와 동일하나 operand 값이 변경되지 않고 EFLAGS 레지스터만 변경됨 ( 두 operand의 값이 동일하다면 SUB 결과는 0이고 ZF = 1로 세팅됨 )

 JE

조건분기( JUMP if equal ) *ZF = 1이면 점프 


아래 이미지를 보시면, 

Title = "abex' 1st crackme" 는 402000 주소에 들어있구요.

Text = "Make me think your HD is a CD-ROM. 은 402012 주소에 들어있습니다.




이제 코드를 한번 봐보겠습니다.


401000 주소에서 MessageBoxA()를 호출하고 있고,

Title, Text, hOwner가 우리가 아는 MessageBoxA함수 순서와 반대로 들어있는거같네요 ?

실제 C 소스코드는 MessageBox(NULL, "Make me think your HD is a CD-ROM.", "abec' 1st crackme", MB_OK|MB_ARRPLMODAL); 입니다.


스택 메모리 구조는 먼저 들어간것이 나중에 나오는 형태( FILO )이기 때문에 파라미터를 역순으로 넣어주면 받는 쪽에서 올바른 순서로 꺼낼 수 있기 때문입니다.


40100E주소의 MessageBoxA를 실행하면 


딱 요까지 나오고 확인을 누르면 꺼집니다.


다음 코드를 보면,


GetDriveType() API로 C드라이브 타입을 얻어오는 부분입니다.

401018주소를 지나면서 우측에 있는 Registers 창의 값들이 변하는걸 봤습니다.


우선 첫번째,

401026 주소의 JE( Jump if Equal ) 명령어를 JMP( 점프 ) 명령어로 바꿔주어 EAX 와 ESI의 값에 상관없이 원하는 출력을 하게 만들겠습니다.


# 40101F주소의 JMP명령어를 보시면 바로밑의 주소로 점프를 하라고 하는데 이러한 경우 의미없는 garbage code 라고 합니다.


# 401024 주소의 CMP EAX, ESI 

   401026 주소의 JE SHORT 40103D

   = EAX와 ESI 값을 비교하여 두 값이 같으면 40103D로 점프, 다르면 바로 밑에 있는 주소( 401028 )로 진행.



성공했습니다 !


이제 두번째 방법으로 궁금했던 EAX와 ESI값을 수정해서 성공시켜보겠습니다.


GetDriveTypeA 함수를 지나며 오른쪽 Registers 창의 EAX와 ESI를 잘 보셔야 할 것 같습니다.

하드디스크 c 드라이브를 인식한 후 EAX는 3으로 세팅이 되고, ESI는 주소값 401000으로 나옵니다.


그래서 c 드라이브의 EAX 값에는 어떤 종류가 있을까 싶어 찾아봤습니다. 


https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getdrivetypea


구글에 GetDriveTypeA 로 검색한 결과입니다.


검색 결과를 보니 Hard drive나 flash drive는 3, CD-ROM은 5로 나타나고 있으니 맞는 것 같습니다.


자, 그럼 이제 EAX와 ESI 값을 변경하여 JE 명령어를 실행할 때 같은값으로 인식하게 만들겠습니다.


401024 주소까지 진행을 하게되면 EAX = 1, ESI = 401003 의 값을 가지고 있는것을 확인할수 있습니다.

그럼 EAX의 값을 401003으로 바꿔 보겠습니다.



성공했습니다.


이제 NOP( 아무것도 하지 않음 ) 명령어로 수정해보겠습니다.



무식한 방법이지만 성공했습니다.

이외에도 INC와 DEC를 수정해보는 방법도 있을것 같습니다.


오늘은 여기까지 입니다 감사합니다 !




참고서적 이승원 「리버싱 핵심원리」 인사이트(2018) p67~74


반응형



안녕들 하시죠 !


오늘은 어플리케이션 디버깅을 위해 기본적으로 알아야할 IA-32 ( Intel Architecture 32 비트 )의 레지스터에 대해 공부해보겠습니다.

레지스터 또한 바이트 오더링 처럼 학교다닐때 컴퓨터 구조 시간에 공부했던것 같은데... 다 까먹었습니다.


CPU 레지스터란 ?


레지스터란 CPU 내부에 존재하는 다목적 저장공간 입니다.

우리가 일반적으로 저장공간( 메모리 )라고 얘기하는 RAM( Random Access Memory )와는 조금 성격이 다른데요.

CPU가 RAM에 있는 데이터를 엑세스하기 위해서는 물리적으로 먼 길을 돌아가야 합니다.

하지만 레지스터는 CPU와 한 몸이기 떄문에 고속으로 데이터를 처리할 수 있습니다.


IA-32는 지원하는 기능도 무척 많고 그만큼 레지스터의 수도 많다고합니다.

어플리케이션 디버깅의 초급 단계에서는 앞으로 가장 많이 보게될 Basic Program execution register에 대해 알아두어야 한다고 합니다.


Basic Program execution registers


ㆍ x87 FPU registers

ㆍ MMX registers

ㆍ XMM registers

ㆍ Control registers

ㆍ Memory management registers

ㆍ Debug registers

ㆍ Memory type range registers

ㆍ Machine specific registers

ㆍ Machine check registers 

등등 더 많은 레지스터들이 존재한다고합니다.


또 Basic Program execution registers는 다시 4개의 그룹으로 나눌 수 있다고 합니다.


ㆍ General Purpose Registers ( 32비트 8개 )

ㆍ Segment Registers ( 16비트 6개 )

ㆍ Program Status and Control Register ( 32비트 1개 )

ㆍ Instruction Pointer ( 32비트 1개 )


이후 뒷부분까지 쭉 읽어봤는데요... 레지스터 4개의 그룹을 한번에 다 이해하기는 너무 어려웠습니다.

저자께서는 처음에는 일단 General Purpose Registers 8개와 각각의 쓰임새 정도만 알아두어도 되고, 디버깅을 많이 하다 보면 저절로 쓰임새를 

알게 될 것이라고 하시니 오늘은 General Purpose Registers 에 대해서만 공부해보겠습니다.  


범용 레지스터 ( General Purpose Registers )


범용 레지스터는 범용으로 막 쓰는 레지스터 입니다.

IA-32에서 각각의 범용 레지스터들은 32비트( 4바이트 )씩 8개가 있는데요.

보통은 상수( 연산 처리, 연산 결과 ), 주소 ( 번지지정, 복귀주소 ) 등 작은 데이터의 임시 저장 공간 입니다. 




위에 있는 이미지는 Intel 홈페이지에서 무료 배포중인 IA-32 메뉴얼에서 발췌 했습니다.

4670장이나 되는 방대한 양인데요. 

우리가 찾는 범용 레지스터는 p73~74에 있으니 아래 링크를 참고해보시면 좋을 것 같습니다.


https://www.intel.co.kr/content/www/kr/ko/architecture-and-technology/64-ia-32-architectures-software-developer-manual-325462.html


8개의 범용 레지스터가 있고 각 레지드터들은 16비트 하위 호환을 위하여 몇 개의 구획으로 나뉘어집니다.



가장 위에있는 EAX로 봤을때 ( 1byte = 8bit )


32비트 ( 0 ~ 31 )를 전부 다 사용하고 싶을때는 EAX를,


16비트 ( 0 ~ 15 )만 사용하고 싶을때는 AX를,


AX는 다시 상위 8비트 AH하위 8비트 AL로 나뉘어집니다. 


이해 되시죠 ?


자, 이제 8개를 4개씩 나누어 알아보려고 합니다. 

레지스터 이름 앞에 붙은 E ( Extended )는 예전 16비트 시절부터 존재하던 레지스터들의 크기를 확장시켰다는 뜻입니다.


아래 4개의 레지스터들은 주로 산술연산 ( ADD, SUB, XOR, OR 등 ) 명령어에서 상수나 변수 값의 저장 용도로 많이 사용됩니다.


EAX ( Accumulator Register ) : 산술, 논리 연산을 할때 사용되며 함수의 리턴 값을 저장.

EBX ( Base Register ) : 메모리 주소를 저장.

ECX ( Count Register  ) : 우리가 흔히 아는 반복문에서 카운트로 사용.

EDX ( Data register ) : 보통 EAX와 함께 연동해서 사용하며, 큰수의 복잡한 연산 과 부호 확장 명령에 사용.


아래 4개의 레지스터들은 주로 메모리 주소를 저장하는 용도로 많이 사용됩니다.


EBP ( Base Pointer Register ) : Stack의 시작 주소 저장.

ESP ( Stack Pointer Register ) : Stack의 끝지점 주소가 저장. push pop 에 따라 4byte씩 값이 변한다.

ESI ( Source Index Register ) : 데이터를 조작하거나 복사할 때 데이터의 주소 저장.

EDI ( Destination Index Register ) : 데이터를 복사할 때 목적지의 주소 저장.


낯설은 용어들이 많이나와 혼란스럽지만 자주 봐서 눈에 익혀야 할 것 같습니다.


오늘은 여기까지 입니다 감사합니다 !




참고서적 이승원 「리버싱 핵심원리」 인사이트(2018) p53~58

반응형




안녕들 하시죠 !


오늘은 컴퓨터 메모리를 저장하는 방식 ( 바이트 오더링 ) 에 대해 알아보겠습니다.


앞으로 리버싱 공부를 하는데 기초가 되는 중요한 부분이라고 합니다.

학교다닐때 컴퓨터 구조 시간에 배웠던 내용인데 하나도 기억이 안나서 다시 공부해보려고 합니다.


바이트 오더링에는 크게 두가지 방식이 있는데요.

빅 엔디언리틀 엔디언 입니다.

우리가 앞으로 자주 사용할 방식은 리틀 엔디언 이라고 합니다.


아래에 간단한 예제를 통해 빅 엔디언 과 리틀 엔디언의 차이를 알아보려고 합니다.


BYTE b    =            0x12;

WORD w    =         0x1234;

DWORD dw    =    0x12345678;

char str[]    =       "abcde";


첫번째 줄을 보시면 타입은 BYTE, 이름은 b, 사이즈는 1, 빅 엔디언 = [12], 리틀 엔디언 = [12] 로 표현됩니다.

여기에선 차이가 없지만 2바이트 이상을 표현할때 부터 달라집니다.


두번째 줄을 보시면 타입은 WORD, 이름은 w, 사이즈는 2, 빅 엔디언 = [12][34], 리틀 엔디언 = [34][12] 로 표현됩니다.

빅 엔디언 방식은 데이터를 사람이 앞에서부터 글을 읽는 방식으로 저장합니다.

리틀 엔디언 방식은 데이터를 역순으로 저장합니다. 저장되는 바이트의 순서가 뒤집어져 있는 것 입니다.


네번째 줄의 str 문자열은 엔디언 형식에 관계 없이 저장 순서가 동일한데요. 

문자열이란 char 배열이기 때문에 각 바이트를 하나씩 연속해서 저장해 나가기 때문입니다.

빅 엔디언 과 리틀 엔디언 둘 다 [61][62][63][64][65][00] 으로 저장이 되는데 맨 뒤에있는 [00]은 문자열의 마지막에 항상 들어가는 NULL값 입니다.

( 아스키 문자 ' a ' 는 0x61, ' e '는 0x65 입니다. )


빅 엔디언은 사람이 숫자를 읽고 쓰는 방법과 같기 때문에 메모리 값을 보기 편하고, 소프트웨어의 디버그를 편하게 해 주는 경향이 있습니다.

리틀 엔디언은 Intel x86 CPU에서 사용하는 방식이고 산술 연산과 데이터의 타입이 확장/축소될 때 더 효율적인 장점이 있습니다.


오늘은 여기까지입니다 감사합니다 !


 


참고서적 이승원 「리버싱 핵심원리」 인사이트(2018) p49~52

반응형




안녕들 하시죠 !


오늘은 저번 시간에 이어 문자열패치 두번째 방법 다른 메모리 영역에 새로운 문자열을 생성하여 전달 을 살펴보겠습니다.


저번 시간에 공부했던 문자열 버퍼를 직접 수정방법은 원본 문자열보다 긴 문자열로 수정하게되면 큰 문제가 생길수도 있다고 합니다.

 

우선 저번시간과 동일하게 Hello World.exe 파일을 실행시켜주시구요.

저번시간에 캠프로 사용했던 401000 위치까지 가서 F9로 그 위치까지 실행시켜줍시다.



401007 주소에서는 PUSH 411AF0 명령으로 MessageBox() 함수에게 411AF0 주소의  “ Hello World!” 문자열을 파라미터로 전달하고있습니다.

 

우리는 이제 411AF0 주소의 문자열 대신 다른주소에 있는 다른 문자열을 전달할 것입니다.


밑에 있는 dump창에서 Go to ( ctrl + G ) 명령으로 411AF0 주소로 가주시구요.

저번시간 Hello World ! 문자열을 패치했던 주소에서 조금만 밑으로 스크롤을 내려보세요.


그럼 아래 이미지와 같은 00 으로 가득찬 공간이 나올껍니다. 이부분은 프로그램에서 사용되지 않는 NULL padding 영역 입니다.



자 이제 이 널널한 부분에 문자열을 써줍시다.



정말 널널하군요...


이제 해줄작업은 버퍼를 새로 구성하였으니 MessageBoxW() 함수에게 새로운 버퍼 주소 412940을 알려줘야합니다.

위에있는 Code창에서 Assemble 명령어를 사용하여 코드를 수정해야합니다.


아까 “Hello World !” 가 있던 곳의 주소에 커서를 놓고 space을 누르면 Assemble이 나옵니다.

그곳에 우리의 새로운 문자열의 주소 412940을 입력해줍니다.



F9를 눌러 실행하게되면



, 이렇게 주소를 바꿔주어 더 긴 문자열 패치에 성공했습니다.


오늘은 여기까지입니다 감사합니다 !




참고서적 이승원 「리버싱 핵심원리」 인사이트(2018) p41~47

반응형




안녕들 하시죠 !


지금까지 리버싱을 배우기 위한 준비를 했다면 본격적으로 공부를 해볼까 합니다.

 

우선, 오늘은 문자열 패치에 관한 내용입니다.

 


패치란?


기존 응용 프로그램의 버그를 수정하거나 또는 새로운 기능을 추가시킬수 있다.

패치 대상은 파일 혹은 메모리가 될 수 있으며, 프로그램의 코드와 데이터 모두 패치가 가능하다.


 

저번시간에 저희는 Main() 함수를 찾았고, 그 안에있는 “Hello World !” 문자열을 찾았습니다.


이제 “Hello World !” 문자열을 다른 문자열로 변경시키는 패치 작업을 해보겠습니다.

 

Ollydbg를 실행시켜주시구요, Main() 함수 시작주소까지 가보겠습니다.



다들 잘 이동하셨죠?

 

, 그럼 이제 Main() 함수 시작주소 ( 401000 ) F2Break Point를 설정하고 F9를 눌러 실행하겠습니다.

 

문자열을 패치하는 방법은 두가지가 있습니다.


첫번째, 문자열 버퍼를 직접 수정.


두번째, 다른 메모리 영역에 새로운 문자열을 생성하여 전달.


가장 쉬운 부분이라고 하니 우선 확실히 익히고 넘어가야 할 것 같습니다.

 

문자열 버퍼를 직접 수정하는 방법인데요

아래 이미지에 빨간 화살표로 보이는 부분 덤프창 )을 클릭한 후에 Go to (Ctrl + G) 명령으로 “Hello World !”의 함수 전달인자 주소로 갑니다.

“Hello World !” 의 함수 전달인자 주소가 밑에 이미지 처럼 ??_C@_1BK@EKFA.... 으로 되어있으실 경우 더블클릭하면 해당하는 주소가 나옵니다.



OK를 누르게 되면 아래와 같은 이미지가 나오게 됩니다.



“Hello World !” 부분을 마우스로 드래그하신 후에 맨 왼쪽 주소부분을 보시게 되면,

우리의 문자열은 411AF0 ~ 411B00 부분을 차지하고 있습니다.

 

Ctrl + E (Edit 다이얼로그) 를 띄워봅시다.



저는 이미 수정을 했는데요. 주황색 포인트 부분을 보시면 될것같습니다.


우선 왼쪽 하단 Keep size 부분의 체크를 해제시켜주시구요, UNICODE 부분의 “Hello World !” Hello Reversing으로 바꿔줍니다.

 

이때 주의 할점은 유니 코드 문자열은 2바이트 크기의 NULL로 끝나야 한다는 것입니다.

원본인 “Hello World !” 보다 “Hello Reversing” 문자열의 길이가 더 길고, 원본 문자열 뒤쪽에 어떤 데이터가 존재할 수 있기 때문에 원본 길이를 넘는 

문자열로 덮어쓰는 것은 위험하여 주의하여하 할 것 같습니다.

 

이제 위에있는 코드창으로 가서 F9를 눌러 실행시켜봅시다.



, 우리가 원하는 대로 문자열 패치를 성공시켰습니다 .

하나하나 따라서 해보니 너무 재미있어서 시간가는줄 모르겠습니다.

 

이제 우리가 패치한 파일을 저장하는 단계만 남았습니다.


아래 dump창에서 변경된 내용 ( “Hello Reversing” ) 문자열을 블록 잡고 우측 클릭하여 Copy to executable file 메뉴를 선택하면 Hex창이 나옵니다. 

여기서 다시 마우스 우측 버튼의 Save file 메뉴를 선택하고 이름을 원하는대로 바꾸어주면 저장이됩니다.

다시 실행하여 문자열 값이 잘 변경되어있는지 확인해주시면 됩니다.



다음시간에는 문자열 패치 두번째 방법 다른 메모리 영역에 새로운 문자열을 생성하여 전달부분을 진행해보겠습니다. 


오늘은 여기까지입니다 감사합니다 !




참고서적 이승원 「리버싱 핵심원리」 인사이트(2018) p37~41

반응형


안녕들 하시죠 !


이번시간에는 베이스캠프를 지정하는 4가지 방법에 대해 공부해보겠습니다.

 

베이스 캠프란?


디버거를 재실행(Ctrl + F2) 할 때 마다 첫 화면으로 돌아가서 굉장히 불편합니다

그래서 디버깅을 실행하는 중간중간에 중요 포인트를 지정해놓고 그 포인트로 빠르게 이동할수 있도록 도와주는 기능입니다.



이번 실습에서는 PUSH 14 명령어가 있던 주소( 2F104F )를 베이스캠프로 지정하는 연습을 해보겠습니다.


저번 시간에 사용했던 HelloWorld.exe 파일을 열어주시면 아래와 같은 화면이나오죠.



여기에서 F9번을 눌러 실행을 시키게되면 아래와 같이 나옵니다.



F8번을 눌러 JMP 2F104F 명령어가 있는 2F11A6주소로 이동 후에 F7번을 눌러 함수로 들어가보겠습니다.



이곳이 우리가 베이스캠프로 이용하게될 곳입니다.

주소는 2F104F이구요.


이젠 다들 볼줄 아시죠?

 


자, 우선 첫번째  Go to ( Ctrl+G ) 명령 이 있습니다.

 

Ctrl+G 를 입력하게 되면,



이런 식으로 나오게 되고 OK를 누르면 해당 주소로 이동하게 됩니다.

그 후에 Execute till cursor ( F4 ) 명령으로 그곳까지 실행하게 됩니다.

이제는 주소만 알고있다면 쉽게 찾아갈수 있을것같습니다.

 



, 이제 두번째로 BP(Break Point)설치 입니다.


BP를 설치 ( F2 )하고 실행 ( F9 )하게되면 현재 실행 위치에서부터 실행하다가 BP에서 멈추게 됩니다.

저자께서는 이방법이 가장편하고 많이 사용하는 방법이라고 말씀하십니다.



메인 메뉴의 View – BreakPoints ( Alt+B )를 누르면 BP목록이 나오고 더블클릭하면 그 주소로 이동하게됩니다.

 



, 다음 세번째는 주석입니다.

 

( ; ) 단축키로 주석을 달고 이 주석을 찾아가는 방법입니다.

원하는 라인에서 ; 를 누르게되면 다음과 같은 화면이 나옵니다.



그 다음 OK를 누르게되면,



자 이렇게 >>>베이스캠프 라고 주석이 달리게됩니다.


잠시 커서를 다른곳에 두고 마우스 우클릭을 한 후에 Search for -> user-defined comment 항목을 선택하면,



이렇게 주석을 단 주소와 명령어가 나오게되고 더블클릭시에 그 주소로 이동하게 됩니다.

 



, 이제 마지막 네번째 레이블 방법입니다.

 

레이블은 특정 주소에 이름을 붙여 코드를 매우 직관적으로 변경해주는 기능입니다.

 

원하는 주소에 커서를 위치시킨 다음 ( : ) 키를 누르게 되면,



이렇게 창이 뜨게되고 OK를 누르면,





이렇게 베이스캠프의 주소 ( 2F104F ) base camp로 매우 직관적으로 바뀐 것을 확인할 수 있었습니다.

 

, 마우스 우측버튼 더블클릭 후 Search for -> user defined labels 탭에 들어가보시면,




이렇게 확인되고 원하는 레이블을 더블클릭해서 해당 주소로 이동한 후, Execute till cursor ( F4 )를 누르게 되면 그 위치에서부터 디버깅을 할수 있다니... 


너무나도 편리한 것 같습니다.


오늘은 여기까지입니다 감사합니다 !




참고서적 이승원 「리버싱 핵심원리」 인사이트(2018) p24~36


+ Recent posts