Ctrl+F2 프로그램 재실행
Alt+F2 프로그램 닫기
F3 새 프로그램 열기
F5 활성화 되어 있는 윈도우 최대화, 혹은 원복
Alt+F5 olly 디버거 윈도우를 항상 위로
F7 Step into (함수 진입)
Ctrl+F7 Animate into (entering functions)
F8 Step over (executing function calls at once)
Ctrl+F8 Animate over (executing function calls at once)
F9 실행
Shift+F9 Pass exception to standard handler and run
Ctrl+F9 Execute till return
Alt+F9 Execute till user code
Ctrl+F11 Trace into
F12 Pause
Ctrl+F12 Trace over
Alt+B Open Breakpoints window
Alt+C Open CPU window
Alt+E Open Modules window
Alt+L Open Log window
Alt+M Open Memory window
Alt+O Open Options dialog
Ctrl+T Set condition to pause Run trace
Alt+X Close OllyDbg
F2 Toggle breakpoint
Shift+F2 Set conditional breakpoint
F4 Run to selection
Alt+F7 Go to previous reference
Alt+F8 Go to next reference
Ctrl+A Analyse code
Ctrl+B Start binary search
Ctrl+C Copy selection to clipboard
Ctrl+E Edit selection in binary format
Ctrl+F Search for a command
Ctrl+G Follow expression
Ctrl+J Show list of jumps to selected line
Ctrl+K View call tree
Ctrl+L Repeat last search
Ctrl+N Open list of labels (names)
Ctrl+O Scan object files
Ctrl+R Find references to selected command
Ctrl+S Search for a sequence of commands
Asterisk (*) Origin
Enter Follow jump or call
Plus (+) Go to next location/next run trace item
Minus (-) Go to previous location/previous run trace item
Space ( ) Assemble
Colon (:) Add label
Semicolon (;) Add comment
ctrl+, 아래 화살표키 디스어셈블 위치 지정

 

 

 

Main menu + 단축키

F9 Run
F12 Pause
F7 Single-step / Step-into
F8 Step-over
Ctrl+F7 Animate into
Ctrl+F8 Animate over
Ctrl+F9 Run until return
Alt+F9 Run until user code

 

[L] Log data
[E] Executable modules
[M] Memory map
[T] Threads
[W] Windows
[H] Handles
[C] CPU
[/] Patches
[K] Call stack of main thread
[B] Breakpoints
[R] References
[…] Run trace
[S] Source

 

Command + 단축키

  단축키   설명
 Go to [Ctrl+G]  원하는 주소로 이동(코드,메모리를 확인할 때 사용. 실행 X)
 Execute till Cursor [F4]  커서 위치까지 실행(디버깅하고 싶은 주소까지 바로 이동 가능)
 Comment ;  Comment 추가
 User-defined
 comment
   마우스 우측 메뉴 Search for User - defined comment
 Label :  Label 추가
 User-defined label    마우스 우측 메뉴 Search for User - defined label
 Set/Reset BreakPoint [F2]  BreakPoint 설정/해제
 Step Into [F7]  하나의 OP Code 실행(CALL 명령을 만나면 그 함수 코드 내부로 들어감)
 Step Over [F8]  하나의 OP Code 실행(CALL 명령을 만나면 따라 들어가지 않고 그냥 함수 자체를 실행함)
 Run [F9]  실행(BreakPoint가 걸려 있는 곳에서 실행 정지)
 Execute till return [Ctrl+
F9]
 함수 코드 내에서 RET 명령어까지 실행(함수 탈출 목적)
Show the current EIP *  현재 EIP의 위치를 보여줌
 Show the previous
 Cursor
-  직전 커서 위치를 다시 보여줌
All referenced text
strings
   마우스 우측 메뉴 - Search for - All referenced text stromgs (코드의 참조 문자열 확인)
All intermodular calls    마우스 우측 메뉴 - Search for - All intermodular calls (코드에서 호출되는 모든 API 함수 확인)
Name in all modules    마우스 우측 메뉴 - Search for - Name in all modules (모든 API 함수 확인)
Edit data [Ctrl+E]  데이터 편집
Assemble  [Space
  Bar]
 어셈블리 코드 작성
Copy to executable file    마우스 우측 메뉴 Copy to executable file (파일의 복사본 생성 - 변경사항 반영)
 Preview CALL/JMP
 address
[Enter]  커서가 CALL/JMP 등의 명령어에 위치하면 해당 주소를 따라가서 보여줌
 (실행 X, 간단한 함수 내용 확인에 유용함)

                                                                                            

 

 

참고 :

https://ac3lucifer.tistory.com/34

http://mer-bleu.tistory.com/7

 

 

 

어셈블리 언어 기본

 

 

레지스터

 

< Basic program execution register >

 

 

 

< General-Purpose Registers >

 

 

EAX : Accumulator for operands and result data)

EBX : Pointer to data in the DS Segment)

ECX : Counter for string and loop operations)

EDX : I/O pointer

 

 위의 4개의 레지스터들은 주로 산술연산(ADD, SUB, XOR, OR 등) 명령어에서 상수/변수 값의 저장 용도로 많이 사용된다. 어떤 어셈블리 명령어(MUL, DIV, LODS 등)들은 특정 레지스터를 직접 조작하기도 한다(이런 명령어가 실행된 이후에 특정 레지스터들의 값이 변경됨).

 그리고 추가적으로 ECX와 EAX는 특수한 용도로도 사용되는데, 

 ECX는 반복문 명령어(LOOP)에서 반복 카운트로 사용된다(루프를 돌 대마다 ECX를 1씩 감소시킴)

 EAX는 일반적으로 함수 리턴값에 사용된다. 모든 Win32 API 함수들은 리턴 값을 EAX에 저장한 후 리턴한다.

 

 

EBP : Pointer to data on the stack (in the SS segment)

ESP : Stack pointer (in the SS segment)

 

 위 4개의 레지스터들은 주로 메모리 주소를 저장하는 포인터로 사용된다.

 ESP는 스택 메모리 주소를 가리키며, 어떤 명령어들(PUSH, POP, CALL, RET)은 ESP를 직접 조작하기도 한다.

 EBP는 함수가 호출되었을 때 그 순간의 ESP를 저장하고 있다가, 함수가 리턴하기 직전에 다시 ESP에 값을 되돌려줘서 스택이 깨지지 않도록 한다. (이것을 Stack Frame 기법이라고 함)

 

 

ESI : source pointer for string operations

EDI : destination pointer for string operations

 

 ESI와 EDI는 특정 명령어들(LODS, STOS, REP MOVS 등)과 함께 주로 메모리 복사에 사용된다.

 

 

 

< EFLAGS: Program Status and Control Register >

 

 

 

0. CF(Carry Flag) :

 부호 없는 수(unsigned integer)의 오버플로가 발생했을 때 1로 세팅된다.

예를 들어 1바이트의 값 1111 1111에 1을 더하면 1 0000 0000이 되어 마지막 1이 1바이트를 넘어서는 오버플로가 발생하여 CF는 1로 세팅된다. 그런데 더하기 뿐만 아니라 뺄셈에서도 오버플로가 발생하는데,

예를 들어 1바이트 값 0000 0001에 10을 빼면, .... 1111 1111 1111이 되어 1바이트를 넘어서는 오버플로가 발생하게 되어 CF는 1로 세팅된다. 이러한 성질 때문에 조건 점프 명령어 JA, JB에 이용된다.

 

2. PF(Parity Flag): 

연산 결과, 하위 8비트에 '1'이 짝수개 있으면 1로 세팅되고 그 외에는 0으로 세팅된다.

 

4. AF(Auxiliary carry Flag):

연산 결과 하위 니블(4bits)에서 비트 범위를 넘어섰을 때 참이 된다. 이진화 십진법(BCD) 연산에 사용된다.

 

6. ZF(Zero Flag)

 연산 명령 후에 결과 값이 0이 되면 ZF가 1(True)로 세팅된다. CMP 명령어도 내부적으로 피연산자끼리 빼는 역할을 하기 때문에, 두 피연산자가 같으면 ZF가 1이 된다. 단, MOV 명령어로 0을 만들어줄 경우는 제외. 주로 조건 점프 명령어 JE, JNE에 이용된다.

 

7. SG(Sign Flag)

 연산 명령 후 최상위 비트(부호 비트)가 1이라면 SG=1이 되고, 최상위 비트가 0이라면 SG=0이 된다.

 

8. TF(Trap Flag)

  TF값을 1로 세팅하면, CPU는 Single Step 모드로 변경된다. CPU는 Single Step 모드에서 하나의 명령어를 실행시킨 후, EXCEPTION_SINGLE_STEP 예외를 발생시킨다. 이후 Trap Flag는 자동으로 초기화(0) 된다.

 

11. OF(Overflow Flag) :

 연산 명령후 부호 있는 수(signd integer)의 최상위 비트에 오버플로가 발생했을 때 1로 세팅된다.

예를 들어 1바이트 값 0111 1111에 1을 더하게 되면, 최상위 비트가 1로 되기 때문에 OF=1이 된다. 그러나 오버플로가 발생했더라도, 최상위 비트가 0이라면 OF=0이 되는데,

예를 들어, 1바이트 값 0111 0000에 1100 0000을 더하게 되면, 1 0011 000이 되어 오버플로가 발생하긴 했지만, 최상위 비트가 0이기 때문에 OF=0이 된다.

 

 SG와 별반 차이 없어보이지만, SG는 연산결과 단순히 최상위 비트가 1이면 SG=1이지만, OF는 "오버플로" and "최상위 비트 1"이라는 조건이 충족되어야 OF=1이 된다.

 

 

< Instruction Pointer >

 

EIP : Instruction Pointer

 

 EIP는 CPU가 처리할 명령어의 주소를 나타내는 레지스터이며, 크기는 32비트(4바이트)이다(16비트 IP 레지스터의 확장 형태임). CPU는 EIP에 저장된 메모리 주소의 명령어(instruction)를 하나 처리하고 난 후 자동으로 그 명령어 길이만큼 EIP를 증가시킨다. 이런 식으로 계속 명령어를 처리해 나간다.

 범용 레지스터들과 다르게 EIP는 그 값을 직접 변경할 수 없도록 되어 있어서 다른 명령어를 통하여 간접적으로 변경해야 한다. EIP를 변경하고 싶을 때는 특정 명령어(JMP, Jcc, CALL RET)를 사용하거나 인터럽트(interrupt), 예외(exception)를 발생시켜야 한다.

 

 

 

 

명령어

 

 MOV

 

ex) MOV Reg, Imm

ex) MOV Reg, Reg

ex) MOV [Mem], [Mem] (메모리에서 메모리로 이동 불가능)

 

 LEA(Load Effective Address)

주소를 가져오는 명령어, 전역변수의 주소도 가져올 수 있다.
Source 피연산자의 유효 주소를 계산하여 Destination 피연산자에 복사한다.
간단히 주소를 알아내서 복사하는 명령어다.
 ex) lea EAX,[EBP-4]

 

 TEST: 논리 비교(Logical Compare)

bit-wise logical 'AND' 연산과 동일(operand 값이 변경되지 않고 EFLAGS 레지스터만 변경됨)

두 operand 중에 하나가 0이면 AND 연산 결과는 0 → ZF=1로 세팅됨. TEST의 AND 연산 결과값이 0이면 ZF가 1로 세트, 0이 아니면 ZF가 0으로 세트된다.

 

(++ TEST 추가

어셈블리어에서 TEST연산은  오퍼랜드 끼리 AND연산하여 결과값을 CMP연산과 같이 저장하진 않는다. 

 

단지 플래그 값을 세팅하여 분기문에게 영향을 준다 ex) JE, JZ.... 같은 분기문 

 

여기서 중요한것은 TEST EAX, EAX와 같은 명령어다.   

 

얼핏보면 같은 레지스터끼리 AND연산하면 EAX값이 나올텐데 왜 하는걸까라는 생각을 할 수 있다. 

 

하지만 바로 위에 쓰인 TEST명령어는 단지 EAX의 값이 0이냐 아니냐를 판단하기 위해서다 

 

만약 EAX의 값이 0이라면 AND연산결과 당연히 0이 나올것이고, Z플래그 (ZF가 세팅될 것이다.) 

밑에 JE 0040100 

 

점프문이 있다면 ZF가 세팅되므로 0040100주소로 점프할 것이다. )


 

 

 

 

 JMP(Jump):

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

 short 점프는 -128 ~ 127 byte범위 안에서,

 long 점프는 -2147483648 ~ 2147483647 byte 범위에서 사용된다.

JMP 명령어는 되돌아올 리턴 어드레스 값을 저장하지 않는다.

가리키는 값을 NULL과 비교. 아니면 갯수를 세고 NULL을 만나면 갯수를 리턴한다.

레이블은 인라인 함수 밖에서도 지정가능하다. 같은 레이블은 불가하고 숫자, 공백 또한 불가능하다.

ex) JMP short Imm

ex) JMP Reg

ex) JMP [Mem]

 

건점프 명령: TEST로 검사 혹은 CMP로 비교 조건에 맞는 경우에만 점프

 

조건 점프 명령 (조건 분기)  산술, 논리 연산 
 JA (Jump if above)  CMP a > b
 JB (Jump if below)  CMP a < b
 JE (Jump if equal)  CMP a == b
ZF가 1이면, 해당 주소로 점프.
 JNE (Jump if not equal)  CMP a != b
ZF가 0이면, 해당 주소로 점프.
 JZ (Jump if zero)  TEST EAX, EAX ( EAX = 0 )
 JNZ (Jump if not zero)  TEST EAX, EAX ( EAX = 1 )


점프 계열 명령어들은 상당히 많은 조합이 가능한데 이러한 조합을 전부 다 외울 필요는 없으며, 아래 Keyword만 알고 있으면 된다.

 

J + ...

 N (not)
 E (if equal)
 Z (if zero)
 A (if above)
 B (if below)
 L (if less than)
 G (if greater than)
 S (EFLAGS의 Sign Flag)
 C (EFLAGS의 Carry Flag) 
 P (EFLAGS의 Parity Flag) 
 O(EFLAGS의 Overflow Flag)


예를 들어 JNAE 면, Jump if not above or equal 이라는 의미로, 같거나(equal) 크지(above) 아니하면(not) 점프하라는 명령이다.

 

CMP

 두 피연산자의 값이 같은지 검사한다. 만약 두 값이 같으면 ZF는 1이 되고, 다르다면 ZF는 0이 된다. 비교를 위해 내부적으로는 두 피연산자를 빼는 연산을 하는데, 예를 들어 A=1, B=1일 때 CMP A, B라는 명령어를 내리면 A-B==0이기 때문에 ZF는 1이 된다. (ZF가 연산결과가 0이되면 1이되는 성질을 이용한 것).

 

 

JE(Jump if equal) : 두 값이 같으면 점프(A == B) == JZ(Jump if zero): ZF가 1이면 점프.

JNE(Jump if not equal) : 두 값이 다르면 점프 (A != B) == JNZ(Jump if not zero): ZF가 0이면 점프.

 

JE(JZ), JNE(JNZ)는 제로 플래그(ZF)를 참조하여 점프할지 안할지 결정하는 명령어이다.

JE(JZ)는 ZF가 1이면 점프를 하고, JNE(JNZ)는 ZF가 0이면 점프를 한다.

주로 CMP 명령어와 함께 쓰이는데, CMP 명령어는 내부적으로 빼기 연산을 하기 때문에, A, B가 같으면 ZF가 1이 된다.

 

JE와 JZ는 제로 플래그(ZF)가 0인지 아닌지 조사하는 명령어이기 때문에, 두 개는 똑같은 동작을 하며 실제로 기계어(IA-32)도 보면 똑같이 생성된다. 그래서 OllyDbg같은 디버깅 프로그램에서 JE로 작성하든 JZ라고 작성하든 전부 JE로 통일되어 나온다. 같은 맥락으로 JNE와 JNZ도 똑같다. 이런식으로 같은 동작을하는 다른 명령어들이 꽤 있다.

 

 

 

JA(Jump if above): 왼쪽값이 크면 점프. (A > B)

JB(Jump if below): 왼쪽값이 작으면 점프. (A < B) == JC(Jump if carry flag 1): CF가 1일 때 점프.

JAE(Jump if above or equal): 왼쪽값이 크거가 같으면 점프(A >= B) == JNC(Jump if carry flag 0): CF가 0일 때 점프.

JBE(Jump if below or equal): 왼쪽값이 작거나 같으면 점프

 

 JA와 JB는 캐리 플래그(CF) 참조하여 점프할지 안할지 결정하는 명령어이다. JA는 CF가 0일 때 점프를 하며, JB는 CF가 1일 때 점프를 하게 된다. 주로 CMP와 함께 쓰이는데CMP 명령어는 내부적으로 빼기 연산을 하기 때문에, A, B에서 A가 크면 오버플로가 발생하지 않아 CF=0, B가 크다면 오버플로가 발생하여 CF=1이 되는 성질을 이용한 것이다.

 

ex)

401000 MOV EAX, 1

401005 CMP EAX, 2 (오버플로 발생되어 CF 1로 세팅)

401008 JA SHORT 401000 (CF가 1이므로 거짓)

40100A JB SHORT 401000 (CF가 1이므로 참) 40100으로 점프.

 

 

 

JG(Jump if greater): 왼쪽값이 크면 점프 (A > B) == JNGE()

JL(Jump if less): 왼쪽값이 작으면 점프 (A < B) == JNLE()

 

 JA, JB와 별반 다를게 없어보이지만, JG, JL은 피연산자 값을 부호있는 데이터로 판단하여 크냐 작으냐를 판단하는 차이점이 있다. JG와 JL도 상태 플래그를 보고 판정을 내리는데,

JG의 경우 ZF==0 and SF==OF

JL의 경우 SF != OF

이다. 즉 CMP 명령어 따위를 사용하여, 위의 조건이 맞으면 해당 주소로 점프하게 된다.

왜 저런 조건이 나오냐 하면, 일단 SF만을 보면, A > B일 경우 A-B=양수값(최상위 비트 0, SF=0)이 나오고, A < B일 경우 A-B=음수값(최상위 비트 1, SF=1)이 나오므로 SF만 봐도 누가 더 큰지 알 수 있을 것 같다. 하지만 A= 7, B= -1일 경우 7-(-1) == 0111 + 0001이므로 8이 나오는데 이를 비트로 표현하면 1000(최상위 비트 1, SF=1)이 된다. 그렇다면 A가 더 작다고 나오는데 이는 틀린 것이다. 그러므로 만약 OF=1(오버플로가 발생하여 최상위 비트가 1이 됨)가 되었을 때는 SF=1이라도 A가 더 크다는 올바른 결과가 나올수 있도록 한 조건인 것이다. OF=0(오버플로가 발생하지 않을 때)는 ZF, SF값만으로 대소관계를 알 수 있다.

 

 

JLE(JBE 비슷함)

 - 형식 : JLE [Code Address]
 - 내용 : 비교 결과 값이 '0'이거나(ZF=1), 작을 경우 해당 주소로 점프한다.
 - 예제 : JLE 401140
 - 해석 : ZF=1 이거나, 결과값이 작은 경우 401140으로 점프한다.
 - 조건 : Operand 1 <= Operand 2

 

 

 

 NOP(No Operation):

아무 동작을 하지 않는 명령어(그냥 CPU 클럭만 소모됨).

 

 SHL(Shift Left):

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

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

 

 REP(Repeat String)
ECX 레지스터를 카운터로 사용해서 문자열 관련 명령을 ECX>0인 동안 반복한다.
한번 진행될 때마다 ECX 레지스터값이 -1 된다.

 

 NOT 

1의 보수 (비트 반전)

 

 NEG(Negate): 

피연산자의 2의 보수를 계산하여 결과를 피연산자에 저장한다. 즉, 부호를 반전 시킨다.

 

 PUSHAD

EAX → ECX → EDX → EBX → ESP → EBP → ESI →EDI 순서로 레지스터의 값을 스택에 PUSH한다.
레지스터들의 값을 보관해야 할 때 사용한다.

 

 POPAD

PUSHAD에 대응되는 명령어. 

스택에 존재하는 값을 EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP 레지스터로 POP한다.

스택의 일관성을 유지하기 위해 PUSH해준만큼 POP해주는 것처럼, PUSHAD를 사용했으면, POPAD를 사용하여 일관성을 유지해주면 되겠다.

 

XOR

 XOR EAX, EAX -> EAX를 0으로 만듬. 0으로 초기화하는 가장 쉽고 빠른 명령어이다(CPU에게는 MOV EAX, 0 명령어보다 더 쉽고 빠르다).

 

 

 

XOR EAX, EAX  => EAX를 0으로 만듬.

 

절대값 주소로 점프하는 방법.1

PUSH 401000

RETN

 

절대값 주소로 점프하는 방법.2

MOV EAX, 401000

JMP EAX

 

 



참고 : 

https://dopamine-plaza.tistory.com/34 [Dopamine Plaza]

 

https://gutte.tistory.com/24 [Tuuna Computer Science]

 

웹 다운 파일이 있음에도 패키지 폴더를 찾지 못해 설치를 진행하지 못하는 문제입니다. 알아보니 꽤나 많은 사람들이 겪는 문제 같은데, 정확한 원인은 알아내지 못했습니다.


한 가지 해결 방법

말씀드릴 방법으로 하면 오프라인으로 하지 않고도 설치할 수 있습니다. 방법은 다음과 같습니다.

1. 공식 홈페이지에서 웹 다운 파일(아래 사진 EXE)iso 파일(DVD)을 다운로드합니다.

2. 두 파일을 같은 폴더에 두고 iso 파일을 같은 폴더에 압축 풀기 합니다.

압축 풀기를 하면 아래 사진과 같이 v 체크된 4가지 파일이 생깁니다.

3. 웹 설치 파일을 실행시켜 일반적인 설치 절차를 진행합니다.

이렇게 하니 문제없이 설치할 수 있었습니다.


제시된 다른 해결 방법들

여러 방법들이 있었는데, 제 경우엔 기존에 제시된 방법들로는 해결이 안 되거나 또 다른 문제가 생겼습니다.

1. 윈도우 포맷 후 vs2015 재설치

2. iso 파일 설치 후, 오프라인으로 vs2015 설치

저는 오프라인 설치 시 Common Tools for Visual C++ 2015가 설치되지 않는 문제가 생겼습니다.

3. vs2015 완전 제거 프로그램으로 깔끔하게 제거 후 재설치

4. 패키지를 찾지 못한 파일을 iso파일에서 찾아 하나씩 직접 설치하고 skip을 누르는 방법

이 방법은 .Net 설치 과정에서 skip 버튼이 없어서 막혔습니다.

이외에도 다른 여러 가지 방법들이 제시되어 있었습니다.

글 하단 참고 자료 패키지 이슈 공유 링크에 있습니다.


다른 방법은 안된다는 게 아니라

제 경우엔 실패한 것입니다...

위에 방법으로 설치하신 분들도 있으니 여러 방법으로 시도해보시길 바랍니다.

 

 

출처 : https://blog.naver.com/alstjr91k/222353057405김민석

'Reverse Engineering' 카테고리의 다른 글

어셈블리어) R로 시작하는 레지스터  (0) 2020.11.20
ld -I/lib/ld-linux.so.2 명령  (0) 2020.10.19
리눅스) objdump -d 명령어  (0) 2020.10.18

레나 튜토리얼 실습파일을 3번을 풀어봅시다.

 

이번시간의 핵심은 PE 구조에 대한 기초적인 이해와 nag 제거 입니다.

 

우선 PE 구조를 분석할때 기본적으로 많이 쓰이는 것은 PEview 라는 툴 입니다.

PEview는 인터넷 검색을 통해 쉽게 찾을 수 있습니다.

 

https://www.aldeid.com/wiki/PEView

 

PEView - aldeid

DRAFT This page is still a draft. Thank you for your understanding. Description INCOMPLETE SECTION OR ARTICLE This section/article is being written and is therefore not complete. Thank you for your comprehension. Installation Download PEView from following

www.aldeid.com

 

여기서 다운받으시면 되구요 설치까지 해줍시다.

 

 

설치하면 이런 돋보기 모양 아이콘의 파일이 나오는데 클릭해서 열어봅시다.

 

파일을 열면 파일 오픈하는 창이 나오는데 일단 이건 닫아주시고

우리가 실습할 3번 폴더의 파일을 드래그해서 열어봅시다.

 

오늘 실습할 파일은 3번 폴더에 있는 RegisterMe.exe 입니다.

 

 

열어보면 이런식으로 구성되어있습니다.

자세히 살펴보면

 

RegisterMe.exe 라는 파일이 무엇으로 구성되어있는지 볼 수 있고 (PE 파일의 구조)

 

 

파일은 Byte들로 이루어져 있는데, 왼쪽에 있는 디렉토리 별로 바이트를 볼 수 있습니다.

 

디렉토리들을 보면

 

보시면 크게 두 가지로 나눌 수 있는데, HEADER 와 SECTION 부분 입니다.

 

헤더들은 파일의 모양이 어떻게 정의되어있는지, 메타데이터 같은 느낌이구요,

주요 내용들은 SECTION들에 들어있습니다.

 

간단하게 헤더부터 살펴보고 갑시다.

 

 

가장 위에는 IMAGE_DOS_HEADER 입니다.

 

DOS는 옛날에 콘솔창만 뜨는 윈도우가 있었는데, 도스 환경에서도 쓸 수 있는 exe 파일이 있었습니다. 

현재는 잘 쓰지 않는데 그때 사용하던 헤더가 DOS 헤더 입니다.

 

 

보시면 시그니처가 5A4D 로 시작하고, 번역하면 MZ라는 단어로 시작한다는 뜻입니다.

 

 

 

실제 바이트를 봐도 MZ로 시작한다는 것을 알 수 있습니다.

+ EXE 파일 앞에는 MZ라는 헤더가 붙습니다.

 

여기서 보시는 정보들도 시그니처를 제외하고는 옛날 파일들에 대한 정보라서 딱히 볼 건 없습니다.

 

그렇다면 옛날에 쓰던걸 왜 아직도 쓰냐는 의문이 들 수 있는데,

이 부분은 옛날부터 쭉 쓰던 부분이라 호환성을 위해서 계속 유지를 하다보니 이 부분이 아직 있는것 입니다.

 

 

MS-DOS Stub Program 은 DOS 모드에서 사용할 수 있는지를 알려주는 부분입니다.

오른쪽 문자열을 보시면 "This program cannot be run in DOS mode" 라고 적혀있는데,

이 파일은 도스 모드에서는 실행되지 않는다 라는 것을 의미합니다.

 

 

다음은 IMAGE_NT_HEADERS 입니다.

이 헤더는 새로 만들어진 헤더이구요,  예전 헤더인 IMAGE_DOS_HEADER의 마지막 오프셋을 보시면

새로운 헤더의 위치를 가르쳐 줍니다. 해당 부분은 0000003C 을 가리키고 있는데

IMAGE_NT_HEADERS의 바이트를 보시면 해당 위치부터 시작하는 것을 알 수 있습니다.

 

오른쪽에 문자열에 PE라고 적혀있는데, 이곳이 바로 PE 라는 헤더를 가지고 있는 부분입니다.

왼쪽에 보시면 PE 헤더가 세 가지 구조체로 이루어져있다는 것을 볼 수 있습니다.

 

시그니처는 위에서 보신것 처럼 PE가 박혀있구요, 

이 PE가 없으면 EXE 파일이 동작하지 않습니다.

 

 

 

다음 밑에는 IMAGE_FILE_HEADER 부분입니다.

 

크게 세 부분으로 볼 수 있는데,

Machine -> 32비트에 돌아가는지 or 64비트에 돌아가는지

Time Date Stamp -> 프로그램이 만들어진 날짜

Characteristics -> 프로그램의 특징

 

등을 알 수 있습니다.

 

 

 

다음은 IMAGE_OPTIONAL_HEADER 입니다.

 

 

기본적으로 컴파일 버전 정보, 코드의 사이즈 (올리디버거로 열었을 때 코드 영역의 크기가 얼마냐를 나타냄)

등을 볼 수 있는데,

 

이번시간에 가장 중요하게 봐야될 부분이 바로 엔트리 포인트와 이미지 베이스 입니다.

 

엔트리 포인트는 1000 번지, 이미지 베이스는 40만 번지를 가리키는데,

 

우선 이해를 위해 이 파일을 올리디버거로 열고 메모리 창을 열어봅시다.

메모리 창은 올리디버거에서 청록색으로 표시된 알파벳 버튼 중에 M 버튼을 클릭하시면 볼 수 있습니다.

 

 

일단 40만번지를 보시면, 오른쪽에 PE header가 올라와 있는 것을 알 수 있습니다.

 

여기서 다시 엔트리 포인트를 보시면 1000번지를 가리키는데 이는 실제로

이미지 베이스로부터 1000바이트 다음 주소에 진입 지점이 있다는 뜻입니다.

따라서 프로그램을 실행하면 401000 번지의 주소부터 시작을 하겠다 라는 말이 되는거죠.

 

실제로 올리디버거에서 보면

 

401000 번지의 주소에서 시작한다는 것을 볼 수 있습니다.

 

정리하자면 Address of Entry Point와 Image Base를 더하면 프로그램 시작 지점을 예측할 수 있습니다.

 

 

 

이 외에 알 수 있는 정보들은 이미지 사이즈 (EXE 파일 자체가 이미지 입니다.), 헤더의 사이즈, 체크섬(데이터의 문제가 있는지 확인하는 용도) 등이 있습니다.

 

다음은 섹션 헤더인데, 섹션 헤더는 밑에 나오는 섹션들에 대한 정보를 가지고 있습니다.

 

안에 내용으로는 섹션의 이름이 있고, Size of Raw Data와 Virtual Size가 있는데,

PE 파일은 기본적으로 메모리에 적재될 때 사이즈가 늘어나게 됩니다.

 

여기서 Size of Raw Data는 원래 데이터의 사이즈이고, Virtual Size가 메모리에 적재될 때 늘어난 사이즈 입니다.

 

밑에 나오는 Characteristcs는 읽기나 쓰기 같은 권한을 가지고 있는 것을 나타냅니다.

 

 

 

 

일단 PE 구조는 이정도로 알아보고, 이제 문제로 넘어가도록 하겠습니다.

 

RegisterMe.exe 파일을 실행했을 때 어떻게 되는지부터 살펴봅시다.

 

클릭하면 레지스터 프로그램에서 nags 를 제거해달라는 메시지가 출력됩니다. OK버튼을 누르면

 

패치를 해서 nag를 삭제해 달라고 합니다.

 

 

여기서 말하는 nag는 쉽게말해서 경고창을 없애달라는 말입니다.

올리디버거로 프로그램을 켜봅시다.

 

 

2번째 라인에 보시면 핸들을 가져오는 함수를 실행을 하는데, 하게 되면 결과값으로 400000이 나오게 되고 EAX에 저장하게 됩니다. (40만은 엔트리 포인트가 있는 곳으로, MZ 헤더가 있는 곳입니다.) 

 

근데 밑에 보시면 CMP 명령으로 EAX와 0이 같은지 비교를 하고, 같다면 점프를 하게 되는데, 만약 점프를 하지 않게 되면 nag를 제거하라는 메세지가 출력됩니다. 

 

사실상 점프를 할 수 없는 구간인데, 임의로 점프를 하게 만들어야 하는 구간이죠.

 

저번시간에 배운 내용으로 직접 JMP 명령어로 수정해서 갈 수도 있겠지만, 

이번에는 엔트리 포인트를 변경해서 진행해보도록 하겠습니다.

 

보시면 우리가 점프해서 도착해야 할 지점은 401024 지점입니다.

 

따라서 엔트리 포인트를 401024로 바꿔서 프로그램 시작을 401000 부터 시작하지 않고 401024 부터 시작하도록 바꾸는 것 입니다.

 

우선 그 전에 밑에 또 잔소리를 하는 구간이 있는데, 이 부분은 강제로 JMP 할 수 있는 부분이 없으니 NOP으로 채워줍시다.

 

이 부분을 드래그 해서 우클릭 후 Binary -> Fill with NOPs 를 클릭해줍시다.

 

잔소리 부분을 NOP으로 채웠습니다.

 

이제 다시 엔트리 포인트를 바꿔보도록 합시다.

아까 봤던 'M' 버튼을 클릭 해 메모리 창으로 가봅시다.

 

원래 엔트리 포인트였던 40만 번지가 있는 곳에 더블 클릭을 해서 들어갑니다.

 

더블 클릭 후 나오는 창을 확대해 보면

이런 창이 나옵니다.

여기서 쭈우우욱 내려 보면..

 

 

4000C0 구간에 PE 시그니처가 있는 것을 볼 수 있습니다. 위에서 PEview에서 보던 것을 올리디버거로 보는 셈이죠.

 

좀더 내려 보면

 

 

 

4000E8 자리에 엔트리 포인트가 보입니다!

 

 

여기서 잠깐 PEview를 보시면

도구 창에서 표시된 부분 (VA) 을 클릭 하고

 

엔트리 포인트 위치를 보면 가상 메모리에 올라와 있는 곳의 주소를 알 수 있습니다.

여기에 나와있는 엔트리 포인트의 위치 4000E8 을 보니 올리디버거와 매칭이 될 수 있다는 것을 알 수 있습니다.

 

이제 다시 올리디버거의 덤프 창으로 가서 확인해보도록 하겠습니다. 'C' 버튼을 클릭하시면 됩니다.

 

 

덤프창에서 컨트롤 + G 누르고 4000E8번지를 검색합니다.

 

 

리틀 엔티안 방식이라 실제는 00 00 10 00 바이트 인데, 젤 마지막 00 바이트 부분을 클릭하고 스페이스를 누른 다음

 

 

24로 바꿔주시면 됩니다.

 

그 이유는 바로

 

위에서 JE 명령을 통해 원래 점프 후 도착해야하는 구간이 401024 였고, 

원래 프로그래 시작점은 401000 이었습니다. (400000 + 1000)

 

때문에 E8 구간에 저장되어있던 00 10 00 00 (실제 읽을 때는 00 00 10 00) 이 바로 1000 이 들어가 있었고,

24 10 00 00 으로 바꿔줌으로써 00 00 10 24 즉 1024가 들어가서 프로그램 시작 지점이 401024로 바뀌는 것 입니다.

 

 

일단 이때까지 수정했던 부분만 해서 저장을 해봅시다.

덤프 창에 마우스 우클릭 -> Copy to executable file -> 새로 뜨는 창에 다시 마우스 우클릭 ->save file -> save

 

저장하고 실행하면 전에 나왔던 경고 창이 안뜨고 바로 흰색 화면이 나온 것을 볼 수 있습니다.

 

하지만 X 버튼을 누르니 경고창이 하나 남아있네요. 

 

아마 바뀐 부분 동시에 저장하는 과정에서 문제가 생겼나 봅니다.

 

crack 파일을 다시 올리디버거로 불러온 다음 경고창을 띄우는 곳을 다시 NOP 로 바꿔준 다음 crack_2로 새로 저장해봅시다.

 

실행하면 시작했을 때와 X를 눌렀을 때 둘 다 사라졌는 것을 볼 수 있습니다.

 

 

RegisterMe.exe 파일은 이것으로 해결이 되었고, 3번 실습파일 보시면 RegisterMe.Oops.exe 파일이 또 있습니다.

 

이 파일은 어떤 건지 올리로 한번 올려보면

 

에러 창이 떠버리고

 

아까와는 전혀 다른 주소 공간이 보입니다. 

 

디버깅을 하기 어려운 곳으로 온 것 같습니다.

이럴때는 두 가지 분석 방법이 있습니다.

 

첫 번째는 PE 구조를 보고 엔트리 포인트를 직접 보는 것이고,

두 번째는 현재 에러는 PE 구조가 이상해서 에러가 났기 때문에, PE 구조를 수정해서 에러를 잡는 방법이 있습니다.

 

 

가장 쉬운 방법은 401000 번지 주소를 찾는 것인데, 우선 컨트롤 + G 로 찾아가봅시다.

일단 프로그램 시작 지점이 401000이 아니기 때문에 저 위치에 브레이크 포인트를 걸어주고 F9를 눌러 401000 까지 오게 만들어 줍시다.

 

이제 여기까지 왔기 때문에 위에서 했던 분석 과정을 똑같이 해주시면 됩니다.

 

 

두 번째는 PE 구조 수정인데,

우선 두 개의 파일을 비교하기 위해 PEview를 두개 열고 RegisterMe와 RegisterMe.Oops를 각각 열어봅시다.

 

왼쪽이 RegisterMe, 오른쪽이 RegisterMe.Oops 입니다. 

한눈에 보기에도 Oops는 없는 부분이 많은 것으로 보이는데, 이 파일은 올바른 PE 구조가 아닙니다.

 

그렇다고 해서 윈도우에서 PE 파일을 실행할 때 모든 구조를 검사하고 실행하는 것이 아니기 때문에 실제로 파일을 클릭해서 실행해 보면 RegisterMe와 똑같이 실행되게 됩니다.

 

하지만 올리디버거를 통해 실행해보면 제대로 실행되지 않죠.

 

한번 수정을 해보도록 합시다.

 

일단 위에서 엔트리 포인트와 이미지 베이스 등 중요한 정보를 가지고 있는 IMAGE_OPTIONAL_HEADER가 Oops 파일에는 보이지 않습니다. 그래서 이것으로는 PEview를 통해 분석할 수 없습니다.

 

 

대신에 HxD 를 사용해서 보도록 하겠습니다.

 

https://mh-nexus.de/en/downloads.php?product=HxD20

 

Downloads | mh-nexus

Downloads Note: Starting with HxD 2.3, the portable edition is available as separate setup program, and can be run with minimal privileges (no admin rights required). For the portable edition, the setup program writes only into the selected folder (e.g., U

mh-nexus.de

여기서 다운로드 하시면 되구요 현재 우리가 실습하고 있는 윈도우 XP는 영문판이라서 한국어 버전보다 영문버전을 다운받는 것이 좋을 것 같습니다.

 

 

일단 RegisterMe 하나를 열어 보시고 위에 도구 창에

Analysis -> Data comparison -> Compare 를 클릭해봅시다.

 

밑에 비교 대상 파일을 Oops 파일로 설정한 다음 OK를 누릅니다.

 

그러면 이런식으로 한 화면에 두 파일이 비교되어 나타나고, 다른 부분을 자동으로 Search 해줍니다.

 

위에 사진 보시면 이미 다른 부분이 하나 나와있고, 단축키로 F6을 누르시면 다음 다른 부분을 자동으로 찾아줍니다.

 

 

우선 처음 다른 곳을 살펴보면

 

000000DF 의 위치에 있는 데이터가 정상파일에서는 00이지만 Oops 파일에서는 40 입니다.

이 부분에 대해서 좀 더 보기위해 PEview를 살펴보면

 

Size of Code 의 부분입니다. 

 

정상 파일은 Size of Code가 00 04 00 00 (00 00 04 00) 인데, Oops 파일은 00 04 00 40 (40 00 04 00) 이 되어버린 것이죠. 

 

이제 정상파일 처럼 고쳐 봅시다.

 

오른쪽 Oops 파일에서 다른 부분에 00을 입력해서 수정해줍니다.

수정한 부분은 붉은색으로 표시가 되었습니다. 위와 같은 방식으로 F6을 눌러 다른 부분을 찾고, 정상 파일과 같은 값으로 모두 바꿔주면 RegisterMe.exe와 똑같은 파일이 되고, PEview나 올리디버거에 올려봐도 정상적으로 나오는 것을 볼 수 있습니다. 

 

 

3번 문제도 해결!

 

 

 

출처 :

https://cha4ser.tistory.com/entry/%EB%A0%88%EB%82%98-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-3-nag-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0-with-PE-%EB%B6%84%EC%84%9D?category=892885

레나 튜토리얼 실습파일을 2번을 풀어봅시다.

 

2번 폴더에 들어가면 reverseMe.exe 파일이 있는데 파일 자체는 1번이랑 똑같습니다.

그리고 클릭해보면

 

 

You really did it Contratz 라는 메세지가 뜹니다.

 

일단 폴더에 있는 Keyfile이 이미 있는데 이 파일을 만드는게 2번의 목적이니 실습을 위해 지워줍시다.

 

 

지우고 다시 파일을 열어보니 1번문제와 유사한 메세지가 떴습니다.

 

일단 올리디버거로 한번 열어봅시당

 

역시 같은 파일이라 익숙한 화면이 나오네요.

복습을 위해 F8을 눌러서 진행을 한번 해봅시다.

 

 

CreateFileA까지 왔습니다. 이 함수는 Keyfile.dat 파일을 불러오는데 파일이 없기 때문에 오류가 났었죠. 

이제 파일을 만들러 가봅시다.

 

우선 실습파일이 있는 폴더에 빈 텍스트 파일을 하나 만들어 줍니다.

그리고 파일 이름을 Keyfile.dat 라고 바꿔줍시다.

(경고창이 나와도 무시하시고 확인 누르시면 됩니다.)

 

 

그 다음 메모장을 켜서 Keyfile.dat 파일을 열어봅니다.

 

우선 아무런 문자 abcd를 넣고나서 올리디버거로 돌아가봅시다.

 

 

이번엔 Keyfile.dat 파일이 있으니 CreateFileA 함수가 실패하지 않겠죠?

1번에서는 함수가 실패하여 EAX에 "FFFFFFFF" 가 들어갔는데, 지금은 성공해서 44가 들어갔습니다.

 

그럼 밑에 CMP 명령을 통한 JNZ가 정상적으로 실행되면서 점프가 됩니다!

 

 

쭉쭉 진행한 다음 ReadFile이 있는 곳까지 왔습니다.

hFile을 보시면 44가 들어가있는데 아까 CreateFileA를 실행한 결과로 EAX에 들어가 있는 값과 동일한 값인 것을 알 수 있습니다.

 

우선 F8로 ReadFile 함수도 정상적으로 진행되는지 확인해봅시다.

 

왼쪽 밑에있는 덤프창을 클릭 후 Ctrl + G를 누르고 ReadFile의 버퍼가 있는 곳인 0040211A 를 입력해 이동해봅시다.

 

 

 

확인해 보시면 우리가 임의로 입력했던 abcd가 잘 들어간 것을 볼 수 있습니다.

 

또한 ReadFile 함수에 pBytesRead라는 변수가 있는데 이는 몇 바이트를 읽었는지를 저장하는 변수입니다. 

위치는 위에 나온것 처럼 00402173 이니 이것도 Ctrl + G로 찾아서 확인해봅시다.

 

 

입력한 대로 4Byte가 저장이 되었습니다.

 

다음 F8로 진행하면 JNZ 명령어가 나오는데 ReadFile 또한 정상적으로 되었기 때문에 임의로 플래그를 수정하지 않아도 정상적으로 점프하게 됩니다.

 

 

다음으로 XOR 연산이 두번 나옵니다.

여기 나오는 XOR 연산의 목적은 레지스터를 초기화 하기 위함입니다.

XOR 연산은 비트연산으로 같으면 0, 다르면 1인데, 

"XOR EBX EBX" 처럼 같은 값을 XOR 연산을 하니까 당연히 0이 되겠죠?

 

여기서도 마찬가지로 콤마를 기준으로 오른쪽에 있는 데이터를 왼쪽에 넣는 것이니 XOR 연산을 한 결과를

EBX에 넣는다 라는 의미가 됩니다.

 

실제로 실행해 보면 EBX와 ESI가 0으로 바뀐 것을 볼 수 있습니다.

 

쉽게 C언어로 설명드리자면 

int EBX = 0;

int ESI  = 0;

 

과 같은 개념입니다.

 

다음은 CMP 명령어인데, 402173 자리에 있는 수와 10과 비교를 하는 명령어입니다.

 

402173은 위에서 본 것 처럼 pBytesRead가 들어가 있는 곳으로, 읽어들인 바이트 수 입니다.

현재 402173에 저장된 것은 4바이트 인데, 10과 비교를 하면 4가 더 작습니다. 

이 상황에서 CMP 명령을 했을 때의 결과를 한 번 봅시다.

 

 

 

1번 문제를 풀 때와 같이 10보다 4가 더 작기 때문에 S 플래그가 1로 셋팅되었습니다.

때문에 JL 명령이 활성화 되었고, 점프하게되면 키 파일이 올바르지 않다는 메세지박스를 띄우는 곳으로 가게 됩니다.

 

*여기서 주의할 점은 저기 나와있는 10은 16진수를 의미하기 때문에 10바이트가 아닌 16바이트로 해석해야 합니다.

(CMP 명령을 할 때도 사실은 10과 4를 비교하는 것이 아니라 16과 4를 비교하는 셈이죠)

 

 

그럼 여기서 할 수 있는 일은 키 파일을 수정하는 것입니다.

 

키 파일은 우리가 좀전에 "abcd"의 4바이트를 넣어놨었는데, CMP와 JL을위해 16바이트를 넘겨서 저장 후 다시 돌려봅시다!

 

키 파일을 적당히 16바이트 이상으로 저장을 하고 새로 올리디버거로 돌려봅시다.

 

 

 

다시 돌려본 후 402173 자리에 가보면 16진수로 11 즉, 17바이트가 저장되었다 라고 볼 수 있고, S플래그도 0으로 되어 JL 명령어가 활성화되지 않았습니다.

 

다음으로 넘어가봅시다.

 

다음은 MOV AL, BYTE PTR DS: [EBX + 40211A] 입니다.

MOV는 오른쪽에 있는 값을 왼쪽에 넣는 명령어 입니다.

이 명령어는 40211A와  EBX를 더한 위치에 있는 값을 AL에 넣는 것 입니다.

 

우선 EBX는 위에서 XOR을 통해 0으로 초기화를 해주었으니 40211A에 있는 값을 다시 확인해봅시다.

 

 

보시면 아까 임의로 넣었던 키 파일의 내용이 들어가있습니다.

 

즉, 40211A + 0 이 위치한 자리의 값 => '0'을 AL에 저장하는 명령입니다.

제대로 들어갔는지 확인해봅시다.

 

EAX의 AL 부분을 보면 30이 들어갔습니다.

0이 아닌 30이 들어간 이유는 위의 HEX dump 창에서도 알 수 있듯이 숫자 0은 아스키 코드로 '30'이고, 이 30이 40211A의 위치에 저장된 것입니다. 따라서 AL에는 숫자 0이 아닌 문자'0'을 뜻하는 Hex값 30이 들어간거죠. 

 

때문에 다음 명령어인 CMP AL, 0 을 실행하게 되면 서로 다르다라는 결과가 나오고, JE 명령어도 활성화되지 않습니다. 

(JE 명령어가 활성화 되려면 문자 '0'이 아닌 Hex값으로 0이 AL에 저장되어야 합니다.)

 

실행해봅시당.

 

실제로 값이 다르기 때문에 Zero 플래그가 0으로 남아있고, JE 명령어도 활성화되지 않았습니다.

 

그 다음 명령은 CMP AL, 47로, AL과 47을 비교하는 명령어 입니다.

여기 나오는 47은 위에 0과 마찬가지로 Hex값의 47을 의미하고, 이는 아스키코드 표를 참고하면 알파벳 'G' 입니다. 

즉, 키 파일에서 읽어들인 문자가 'G'인지 아닌지를 비교하는 명령어 입니다.

 

만약 'G'라면  제로 플래그가 1이 될것이고, 바로 아래에 있는 JNZ 명령어가 활성화 되지 않고, INC ESI와 INC EBX를 모두 실행할 것이고, 'G'가 아니라면 INC EBX만 실행하게 될 것입니다.

 

(JNZ는 제로 플래그가 0일때 점프를 하며, 제로 플래그는 CMP 명령을 통해 값이 같으면 1, 다르면 0입니다.

즉, 값이 다르면 점프를 하게 됩니다.)

 

INC 명령어는 해당 레지스터에 1을 더해주는 명령입니다.

EBX와 ESI는 0으로 초기화 했으니 

만약 읽어들인 문자가 'G'이면 점프를 안하기 때문에 ESI와 EBX 모두 1이 되고,

아니라면 점프를 하기 때문에 EBX만 1이고 ESI는 그대로 0이 되겠죠?

우리의 첫번째 문자는 '0'으로 'G'와 다르기 때문에 제로 플래그가 0이고, JNZ 명령이 활성화되면서 INC ESI는 건너뛰고 INC EBX만 실행됩니다.

 

다음은 4010C1의 위치에 있는 명령어로 점프하는 명령어 입니다.

이동한 위치의 명령어는 40211A 에 EBX를 더한 위치의 값을 AL에 넣는 곳 입니다.

처음에는 EBX가 0이었지만, 이제는 INC EBX를 통해 1이 되었습니다.

그럼 AL에는 40211A + 1의 위치에 있는 데이터를 넣게 됩니다. 

 

40211A + 1을 하게되면 40211B가 될 것이고, 해당 위치를 보면

 

키 파일에 입력한 내용 중 두번째 문자 입니다. 

 

 

따라서 AL은 문자 '1'의 아스키 값 31이 들어가게 되고 위에 했던 과정을 반복하게 됩니다.

 

현재 키 파일은 0123456789abcdefg 총 17개의 문자가 저장되어 있고,

 

40211A에서부터 저장된 문자를 모두 읽은 후에는 Hex 값 0이 저장되어 있습니다.

 

따라서 총 17번의 반복을 진행하면서 키 파일의 문자를 모두 읽은 다음,

CMP AL, 0의 명령을 통해 반복이 종료된 다는 것을 알 수 있습니다.

 

우선 모든 반복문을 진행한 후에 다음 명령어들을 살펴봅시다.

 

 

 

모든 반복이 끝나면 AL에 0이 들어가게 되면서 점프를 하게되는데, 점프를 하면

ESI와 8을 비교한 후에 ESI가 8보다 작으면 아래 JL 명령을 수행하게 되고,

8 이상일 경우 JL 명령은 활성화되지 않고 우리의 목표인 JMP 00401205 를 수행할 수 있습니다.

 

ESI는 위에서 키 파일의 내용 중 'G'라는 문자가 있을 경우에 1씩 증가했습니다.

즉, 키 파일의 내용에 'G' 문자가 총 8개 이상일 경우에 성공한다고 볼 수 있습니다.

 

 

위의 반복문을 보기 쉽게 C++ 언어로 표현하자면

이런식으로 나타낼 수 있을것 같습니다.

 

 

이제 키 파일을 다시 수정해봅시다.

 

G를 8번 이상 넣으면서, 키 파일의 첫 번째 조건이었던 16바이트 이상을 만족시켜준 후에 저장하고 새로 실행해봅시다.

 

첫 번째 문자가 'G'였으니 AL에 47이 들어갔고, INC ESI 명령이 정상적으로 수행된 후에 ESI가 1 증가하였음을 볼 수 있습니다. 이제 반복문을 끝내고 나와봅시다.

 

반복이 끝나면 ESI가 8을 넘었기 때문에 JL은 활성화되지 않고 바로 JMP 명령을 수행할 수 있게됩니다.

 

점프를 하게 되면

성공 메세지를 띄워주는 곳까지 오게됩니다.

 

다시 폴더로 돌아와서 reverseMe.exe. 파일을 실행해도 성공 메세지가 뜹니다.

 

이로써 2번 문제도 해결!

 

 

 

출처 :

https://cha4ser.tistory.com/entry/%EB%A0%88%EB%82%98-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-2-%EB%9D%BC%EC%9D%B4%EC%84%BC%EC%8A%A4-%ED%82%A4-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%B6%84%EC%84%9D%ED%95%98%EA%B8%B0?category=892885

Tutorial 1 - 라이센스 루틴 지나가기

 

 

실습준비가 끝났으면 1번부터 문제를 풀어봅시다.

 

우선 1번문제 files 폴더에 reverseME.exe 파일을 올리디버거로 끌어서 실행시킵니다.

 

(Options - Add to Explorer 에서 Add Ollydbg to menu~~ 를 클릭해두시면 reverseMe 파일을 우클릭 하여 Open with Ollydbg 클릭해서 바로 올리디버거로 띄울 수 있습니다. )

 

그럼 이제 올리디버거 첫 화면부터 알아봅시다.

 

[그림 1] 가상메모리

 

먼저 가장 왼쪽에 나와있는 부분이 가상메모리 주소를 나타내는 부분입니다.

 

프로그램은 실행이 되는순간 프로세스가 되는데, 이는 가상메모리에 프로그램 파일이 올라간다라고 할 수 있습니다.

 

여기서 가상메모리란 운영체제가 물리메모리를 효율적으로 사용하기 위한 시스템입니다.

우리가 흔히 말하는 메모리 8GB, 16GB는 물리메모리를 뜻하며, 가상메모리가 없으면 이 물리 메모리를 효율적으로 사용하지 못할 것입니다.

 

[그림 2] 기계어 코드와 어셈블리어

 

그 오른쪽에는 기계어 코드와 어셈블리어가 있습니다.

기계어 코드는 CPU가 읽어들여 실행시키는 코드를 뜻하는데 사람이 잘 알아볼 수 없는 숫자들로 이루어져있습니다. 

이를 사람이 알아볼 수 있게 1대1 매칭한 것이 바로 옆에있는 어셈블리어 입니다. 

 

기계어 코드는 일반적으로 잘 안보고 필요할때만 가끔 보기때문에 자리가 부족하다면 살짝 가려놔도 무방합니다.

 

[그림 3] 주석

 

그 다음엔 주석이 나옵니다. 프로그램 실행에 직접적인 영향이 있는 부분은 아니며, 코드를 이해하기 쉽게 메모하는 부분이라고 생각하시면 될 것 같습니다. 주로 함수에 실행에 필요한 매개변수에 대한 정보를 나타내고 있는 것으로 보입니다.

 

[그림 4] 레지스터

 

다음은 레지스터 입니다. 레지스터는 CPU가 사용하려고 가지고 있는 데이터인데 일종의 변수라고 생각하시면 편할거같습니다. 일반적으로 각각 레지스터는 쓰이는 용도가 다르기 때문에 어느정도는 알고가시면 좋습니다.

 

EAX : 계산에 대한 저장을 하는 데이터

EBX : (base register) 다목적으로 사용

ECX : (counter register) 숫자를 세거나 for문 등

EDX : (data register) 다목적으로 사용

 

ESI : (source index) 출발지

EDI : (destination index) 목적지

=> 복사, 붙여넣기 (컨c 컨v)같은 개념

 

EBP : (base pointer) 스택의 아랫부분

ESP : (stack pointer) 스택의 윗부분

 

EIP : CPU가 다음에 실행할 명령어 주소

=> 위의 그림에서 검은색으로 표시된 00401000번지가 다음에 실행항 명령어의 위치이며, 실제 명령은 PUSH 0이다.

 

 

각 레지스터 앞에 공통적으로 E가 붙어있는데 이는 32비트 환경을 뜻합니다. 

숫자 두개를 묶어서 한 바이트로 보는데 8개가 있어서 4바이트 -> 32비트 입니다. 

 

만약 E가 없고 AX만 있으면 16비트를 의미하고, 

AH와 AL은 8비트를 의미합니다.

 

레지스터 부분 더블클릭을 하면 직접 데이터를 컨트롤 할 수도 있습니다. 

 

64비트에서는 RAX라고 표현을 하며, 그림으로 설명하면 아래 그림과 같습니다.

 

 

[그림 4-1] 레지스터 구조

 

 

 

[그림 5] 덤프

 

다음 왼쪽 아래에는 덤프값을 볼 수 있습니다. 위에는 명령어 형식으로 되어있고, 아래는 그냥 데이터로 쫙 표현해놓은 값입니다. 찾고싶은 위치가 있다면 컨트롤 + G를 누르고 401000 같은 주소를 입력하면 됩니다.

 

[그림 6] 스택

 

마지막으로 오른쪽 밑에는 스택을 나타내는 창 입니다. 위에 레지스터에 ESP와 EBP와 비교해보면 스택이 0012FFC4 주변을 가리키고 있다는 것을 알 수 있고, 왼쪽에 나타나있는 가상메모리와는 많이 다른 부분에 위치하고 있음을 알 수 있습니다.

 

이쪽 영역에 마우스 우클릭 후 Address - Relative ESP or EBP를 클릭하면 ESP나 EBP로부터 얼마나 떨어져있는지도 확인할 수 있습니다.

 

 

F8을 누르면 한단계 실행이 됩니다. 한번 눌러봅시다.

 

[그림 7] 한단계 실행 화면

 

F8을 누르고 나면 가장 왼쪽에 검은색으로 칠해진 것이 한단계 내려갔고, PUSH 0 이라는 명령어가 실행되었습니다.

오른쪽에 레지스터도 바뀐 것을 볼 수 있습니다. PUSH는 스택에 값을 넣는 것을 의미하며, 스택에 값을 넣었기 때문에 ESP가 한 바이트 이동하게 되었습니다. (12FFC4 -> 12FFC0) 

 

컨트롤 F2를 누르면 처음으로 돌아가는데, 처음 공부할 때 F8을 하나하나 눌러가면서 레지스터나 스택이 어떤식으로 바뀌고 돌아가는지 계속 확인해 보면 유익할것 같습니다.

 

 

어느정도 사용법을 익혔으면 이제 본격적으로 문제를 풀어봅시다!

 

우선 실습 파일인 ReverseMe.exe를 실행시켜 봅시다.

 

 

 

일단 클릭을 해보면 라이센스가 없고 새로 구입하라는 메시지가 뜹니다. 1번 문제의 목적은 이 오류를 우회를 해서 라이센스가 있는 척을 하는게 문제입니다.

 

다시 올리디버거로 돌아가봅시다. 

 

==================

 

올리디버거 단축키

 

한스텝 진행 (함수 진입 x) : F8

한스텝 진행 (함수 진입 x) : F7

재시작 : Ctrl + F2

브레이크 포인트 : F2

주소 이동 : Ctrl + G

브레이크 포인트까지 계속 진행 : F9

 

==================

 

좀전에 가장 처음 명령어를 실행해두었으니 그 다음부터 봅시다.

 

 

다음 나온 명령어는 CALL 이라는 명령어 입니다. 

CALL은 함수를 호출하는 명령어로 예를들어 C언어로 짜여진 프로그램의 함수를 실행하는 명령어 입니다.

[그림 7] 한단계 실행 화면

 

다음 나온 명령어는 CALL 이라는 명령어 입니다.

CALL은 함수를 호출하는 명령어로 예를들어 C언어로 짜여진 프로그램의 함수를 실행하는 명령어 입니다.

호출하는 함수는 KERNEL32.DLL에 있는 GetModuleHandleA라는 함수 입니다.

 

마이크로소프트에서 제작한 DLL인데, 본 문제에서는 크게 중요한 부분이 아니기 때문에 넘어가도록 하겠습니다.

(자세한 설명이 궁금하면 MSDN 사이트에서 검색하시면 됩니다.)

 

다음 명령어로 가봅시다. (F8)

 

 

다음은 MOV 명령어들이 많이 나옵니다. 

 

처음 나와있는 명령어를 보면 

 

MOV DWORD PTR DS:[402177], EAX

 

라고 적혀있는데, EAX의 값을 402177 주소의 값에 넣는다는 의미입니다.

DWORD PTR DS는 크게 신경쓸건 아니고 뒤에 괄호안에있는 주소로 넣는다 정도만 생각해도 될 것 같습니다.

 

 

명령어를 실행한 후에  Ctrl + G를 눌러서 실제 저 자리에 EAX 값이 들어갔는지 한번 확인해봅시다.

 

 

현재 EAX값은 400000인데,  해당 주소로 들어가보면 00 00 40 00 이 들어가있습니다.

 

이는 리틀엔디안 방식 때문에 그렇습니다.

 

그런데, 리틀엔디안과 빅엔디안에 대해서는 잘 설명해놓은 글이 많기때문에 자세히 알고싶으면 구글링해보시기 바랍니다. 

 

간단하게 설명드리자면

 

12345678 이 있으면 리틀엔디안 방식이면

78 56 34 12 이런식으로 들어가게 됩니다.

 

따라서 400000도 

00 00 40 이 됩니다.

 

다음 MOV 명령어들도 마찬가지로 들어가게 되며, 하나하나 보시면서 이해해보시기 바랍니다.

 

다만

이 명령어는 402177의 주소에 있는 값을 EAX로 넣는 명령어 입니다.

항상 콤마를 기준으로, 뒤에 있는 값을 앞에 있는 값에 붙여넣는다는 개념으로 이해하시면 됩니다.

 

 

다음 PUSH 명령으로 스택에 4와 EAX에 있는 값인 400000을 넣었습니다.

 

다음은 LoadIconA라는 함수를 호출하는데 별다른 내용이 없기 때문에 이런게 실행되고 있구나 라는 정도만 아시면 됩니다.

 

(함수에 대한 리턴값은 EAX에 저장이 되니 참고해주시면 되겠습니다.)

 

 

그 밑에도 딱히 문제될 것 같은 명령어는 안보이니 계속 실행해 주시면 되는데, 밑에 보시면 CreateFileA라는 함수가 보입니다. 다른건 몰라도 파일을 만들고 하는 함수는 살짝 의심스러우니 자세히 봅시다.

 

위에도 보신것 처럼 별다른 문제가 없는 명령어가 많고 하나하나 F8을 눌러가면 불편하기 때문에, 의심스러운 곳을 BreakPoint로 걸어놓고 (F2) 해당 지점까지 자동으로 실행하는 F9를 누릅시다.

 

보시는것 처럼 00401073 위치에 F2로 BreakPoint를 설정해 놓으면 빨간색으로 변하고, F9를 누르면 해당 위치까지 한번에 이동하게 됩니다. 

 

CreateFileA 함수는 유명한 함수이기 때문에 msdn에서 구조를 살펴볼 필요가 있습니다.

 

검색은 구글에 CreateFileA msdn 이라고 치면 MS에서 만든 사이트가 나오는데 함수 구조를 잘 설명해두었습니다.

 

 

msdn에서 보시면 이처럼 CreateFileA 함수 호출에 필요한 여러가지 인자들을 소개해 주었는데, 

이 그림에서 함수호출 직전 PUSH 명령어들이 많습니다. 이 과정이 인자를 채워주는 과정이라고 보시면 됩니다.

오른족에 주석을 보시면 어떤 변수에 어떤 값을 넣었는지 쉽게 볼 수 있습니다.

 

 

일단 한번 함수를 호출해 봅시다 (F8)

 

 

함수를 실행하면 EAX가 FFFFFFFF로 채워진 것을 볼 수 있다.

 

위에 함수에 대한 결과를 EAX에 저장한다고 했는데 CreateFileA 함수를 호출하고 난 결과가 FFFFFFFF이란 뜻입니다.

 

함수 리턴값은 msdn에서 Return Value를 보시면 됩니다.

 

해당 함수에서는 실패하면 INVALID_HANDLE_VALUE가 반환된다고 하였는데 그 결과가 -1 (FFFFFFFF) 입니다.

 

 

 

 

실패한 이유는 함수 msdn에 나와있는것 처럼 CreateFileA 함수는 파일을 만드는것 뿐만 아니라 파일을 Open하기도 합니다.

 

하지만 우리의 실습파일을 보면

함수의 옵션에 OPEN_EXISTING으로 설정되어 있고 해당 파일을 Keyfile.dat라고 설정해 두었습니다. 

하지만 실습폴더에는 Keyfile.dat 파일이 없죠. 그래서 오류가 나게 된 것입니다.

 

OPEN_EXISTING 역시 msdn에서 검색하시면 어떤 옵션인지 알 수 있습니다.

(해당 함수 페이지에서 컨트롤+f로 OPEN_EXISTING 검색하시면 편리합니다.)

 

 

함수 호출에는 실패했지만 일단 다음으로 넘어가봅시다.

 

 

 

다음은 EAX와 -1을 비교하는 부분입니다. 현재 FFFFFFFF이 들어와있는데 이건 -1과 같은 값을 의미합니다.

00000000 에서 1을 빼면 FFFFFFFF이 되겠죠.

 

따라서 해당 명령어는 위의 함수가 실패했는지 성공했는지를 따지는 부분이 되겠습니다. 

 

그리고 해당 명령어를 실행하면 

 

 

우측 레지스터 부분에 Z라는 부분이 0에서 1로 바뀌었습니다.

C,P,Z ... 나와있는 부분은 Flag인데, 각 연산의 결과에 따라 바뀌는 값들입니다.

 

CMP 명령을 했을때 두 값이 같다면 0, 다르면 1이 결과값으로 나오는데,

Z는 Zero Flag로, CMP의 결과값이 0이면 1로 바뀌게 됩니다.

즉, CMP 명령 이후 값이 같다면 Z가 1로 된다는 뜻입니다.

 

위의 경우에도 EAX가 -1과 같은 값이기 때문에 Z가 1로 바뀌게 된 것입니다.

 

 

 

다음은 JNZ 명령어가 나옵니다.

기본적으로 앞에 J가 붙으면 JUMP 명령으로, 지정한 위치로 이동을 하는것이고, 뒤에 NZ는 옵션입니다.

NZ 같은 경우는 Not Zero 이고, Zero의 값이 0일때 JMP 명령을 실행하게 됩니다.

만약 JZ였으면 Zero가 1이기 때문에 점프를 하게 되는것이고

 

 

위치는 보기 쉽게 화살표로 나타내져 있고, 중간 명령어들을 건너뛰고 바로 화살표가 가리키는곳으로 이동하게 됩니다.

 

만약 점프를 안해버리면 저 위치로 넘어가지 않고, 바로 다음 명령어인 PUSH 0을 하게 되고, 결국은 MessageBoxA라는 함수를 호출하게 되버리는 것이죠. 저 메세지 박스가 처음 ReverseMe.exe를 실행했을때 라이센스를 구입하라는 메시지를 의미합니다. 그리고 그 다음은 ExitProcess를 호출하여 프로세스를 종료하는 루틴을 밟게 됩니다.

 

 

그렇다면 우리가 할 수 있는 일은 CMP 이후 JNZ 명령을 실행하기 전에 Zero Flag를 바꾼 다음 JNZ에서 정상적으로 JUMP할 수 있게 해줘야 합니다. Zero Flag는 더블클릭으로 쉽게 바꿔줄 수 있으니 한번 바꿔봅시다.

 

표시된 부분을 더블클릭해서 Zero Flag를 0으로 바꿨습니다. 

 

이제 F8을 눌러 명령을 실행하면 정상적으로 JUMP가 됩니다.

 

 

다음으로 쭉 진행하다 보면 ReadFile 함수가 실행되고, 그 다음 

TEST EAX, EAX 가 나옵니다.

이건 EAX의 값이 0인지 아닌지를 확인하는 명령어라고 생각하시면 됩니다.

 

그런 다음 JUMP 명령어가 두개가 나옵니다. 위에는 좀전에 했던 Zero Flag가 0일때 JUMP를 하고,

밑에는 아무런 조건없이 바로 JUMP를 하는 명령어 입니다. 

 

JNZ로 점프를 하게되면 바로밑에 있는 점프 명령어는 실행되지 않을 것이고,

제로 플래그가 1이라서 JNZ명령을 건너뛰면 밑에있는 JMP 명령을 수행하게 되겠죠.

 

그럼 여기서의 상황을 봅시다.

 

Zero Flag는 TEST 명령을 통한 결과도 마찬가지로 반영을 합니다.

결과가 0이면 플래그가 1이되고, 결과가 1이면 플래그가 0이 됩니다.

 

지금은 EAX의 값이 0이기 때문에 TEST의 결과가 0이 되고, 오른쪽에 제로 플래그도 1이 되어버렸습니다.

이렇게 되면 JNZ 명령이 아닌 JMP 명령이 있는 곳을 수행하게 되죠. 그럼 JMP 명령을 실행했을때 어디로 가는지 한번 확인해봅시다. 

 

아까처럼 명령어 위치에 클릭했을 때, 빨간색 화살표가 가리키는 위치를 보시면 됩니다.

 

 

그쪽을 찾아가 보면 4010F7의 위치를 볼 수 있는데, 이 위치에 가게 되면, 아까와 유사한 메세지 박스 함수와 프로세스를 종료하는 함수가 있습니다. 

 

결론적으로 말하자면, CreateFileA함수에 대한 결과를 임의로 바꿨기 때문에, ReadFile의 결과도 00000000이 되어 EAX에 들어가게 되었고, 이때 TEST 명령을 통해 JNZ 명령을 수행하지 않고 JMP 명령을 통해 다시 KeyFile이 올바르지 않다는 메세지박스를 보게되는 것입니다.

 

(CreateFileA 함수에서 정상적인 파일이 있었으면, ReadFile의 결과도 정상적으로 나왔을것입니다. ReadFile의 결과인 0도 CreateFileA 함수에서의 -1처럼 비정상적인 값이라는 뜻입니다.)

 

 

 

그럼 위에서 했던것 처럼 JNZ명령을 수행하기 전에 제로 플래그를 바꾸고 JNZ 명령을 수행해 봅시다.

 

 

정상적으로 점프가 되었고, XOR이 있는 곳까지 왔습니다.

 

 

그 밑에 명령어들을 보시면, 어려워보이는 많은 점프문들이 있습니다. 일단 F8을 눌러 진행해봅시다.

 

 진행을 하다보면, 왠지모르게 결국은 키파일이 이상하다는 메시지 박스를 호출하는곳으로 이동합니다.

 

위의 방법처럼 진행을 하려면 점프문을 지나는곳마다 플래그값을 찾고 하나하나 비교해야하는데 너무 번거로울것 같습니다. 다른방법을 찾아봅시다. 

 

 

 

명령어가 나와있는곳에 마우스 우클릭을 한 후에 Search for -> All referenced text strings를 클릭해 봅시다.

 

 

여기는 프로그램을 실행하면서 참조되는 메세지나 스트링을 볼 수 있는데

보시면 우리가 아까 봐왔던 메세지들을 볼 수 있습니다.

 

근데 맨 아래를 보면 

You really did it! Congratz!!! 라는 메세지가 보입니다. 뭔가 모든것이 정상적으로 되었고, 문제를 풀었을때 띄워주는 메세지처럼 생겼습니다.

 

원하는 메세지를 더블클릭하면 해당 메세지가 있는 곳으로 바로 넘어가게 되니 축하해주는 메세지를 더블클릭해서 위치로 가봅시다.

 

 

이동 후 자세히 보면 빨간색 화살표가 있고 어디선가 점프를 해왔다 라는 것을 알 수 있습니다. 어디서 점프가 이루어졌는지 한번 따라가봅시다. 

 

출발지를 찾았습니다. 근데 보시면 ReadFile 함수도 보이고 아까 우리가 했던 위치에서 조금 더 가야 되는것을 알 수 있습니다.  얼마 안남았으니 조금만 더 힘내서 찾아봅시다!

 

 

우선 아까 점프를 다른곳으로 했기 때문에 처음으로 돌아가봅시다. (Ctrl + F2)

 

CreateFileA 부분에서는 브레이크포인트를 이미 걸어놨으니 놔두고, ReadFile 이후 JNZ 하는 위치에서도 플래그를 바꿔줘야하기때문에 브레이크포인트를 걸어 둡시다.

 

 

그리고 위에서 했던것 처럼 제로 플레그를 바꿔가며 ReadFile 다음까지 실행해 봅시다.

 

먼저 F9를 눌러 처음 브레이크포인트까지 진행을 하시고, 제로플래그를 바꾼 후에 다시 F9를 눌러 4010B0까지 가서 제로플래그를 바꿔주시면 됩니다.

 

이제 아까 했던곳으로 다시 왔습니다. 다음 명령어를 살펴봅시다.

 

JL이 나옵니다. JL은 제로플래그로 결정되는것이 아니고, Less than 의 약자로 비교를 했을때 더 더 작냐를 판단합니다.

(JE는 같을때, JG는 클때 점프합니다.)

 

비교대상은 위의 CMP 명령을 통한 S플래그 입니다.

 

위의 CMP 명령에서는 402173에 있는 값과 10을 비교합니다. 

CMP 명령은 기본적으로 뺄셈을 통해 이루어지고, 

S 플래그는 CMP를 통한 결과가 양수이면 0, 음수이면 1이 됩니다. 

 

이 경우는 402173에 있는 값이 0인데 10과 비교하면서 뺄셈을 하게 되면 -10이 되어 음수가 되었고, 이때문에 S플래그가 1이 되었습니다. 때문에 JL 명령이 실행하게 되는것이죠. 

 

이번에는 S 플래그를 더블클릭해서 0으로 바꿔서 점프하지않도록 해줍시다.

 

내려간 다음 JE 명령어가 보이지만 최종 목적지인 JMP reverseM.00401285가 실행되는 위치까지는 지장이 없으니 계속 진행해줍시다.

 

 

다음 JL 명령어를 보니 또 딴길로 빠지려고 합니다. S플래그도 1로 되었구요. 

JL은 S플래그가 1일때 점프를 하니 바로 0으로 바꿔주고 F8로 넘어갑시다.

 

정상적으로 넘어갔고 해당 점프문을 실행하면

 

아까봤던 축하 메세지를 띄우는 곳까지 왔습니다!

계속 F8로 넘어가봅시다.

 

드디어 성공했습니다! 여기서 OK를 누른다음 F8을 더 누르면 프로그램이 종료됩니다.

 

하지만 여기서 저장을 하지 않으면 항상 이 파일을 열때마다 플래그를 바꿔주는 작업을 반복해야하기 때문에 변경한 크랙 파일을 저장해주는것까지 해줍시다. 

 

명령어가 있는곳에서 스페이스바를 누르면 편집할 수 있습니다.

우선 처음 플래그를 바꾼 곳인 40107B를 찾아가서 스페이스를 눌러봅시다.

 

 

원래는 이런식으로 JNZ로 나와있는데, 우리는 원래 점프해야되는 부분을 강제로 점프하게 해주었으니 

JNZ에서 JMP로 바꿔줍시다.

 

정상적으로 JMP로 바뀐것을 볼 수 있습니다.

 

 

그다음 바꿔줄 부분은 4010B0 입니다. 이부분도 JNZ였으니 JMP로 바꿔줍시다.

 

다음 수정할 부분은 JL이 나오는 4010BF입니다.

이 부분에서는 점프를 하면 "key is not valid" 메시지 박스가 발생되는 지점으로 가게 되므로, JMP로 바꿔주면 안되고, 아무런 명령을 실행하지 않도록 바꿔줘야 합니다.

 

이렇게 nop으로 바꿔줍시다.

요로케

 

다음 JL이 또 등장하는 4010D6 부분도 nop로 바꿔줍시당

 

 

다 바꿨으면 저장해줍시다.

 

 

수정은 우선 우리가 바꿔준 부분(ex. 401073 ~ 4010D8) 을 드래그해서 마우스 우클릭을 한 후에

Copy to executable -> Selection을 클릭합시다.

 

그럼 선택한 부분이 창으로 뜨는데 거기서 다시 마우스 우클릭 후 Save file을 누르시고 새로 파일을 만들면 됩니다.

이런식으로 크랙파일을 만들어봅시다.

저장한다음 크랙 파일을 클릭해보면 바로 실행이 된 것을 확인할 수 있습니다.

 

 

참고

https://cha4ser.tistory.com/entry/%EB%A0%88%EB%82%98-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-1-%EB%9D%BC%EC%9D%B4%EC%84%BC%EC%8A%A4-%EB%A3%A8%ED%8B%B4-%EC%A7%80%EB%82%98%EA%B0%80%EA%B8%B0?category=892885

+ Recent posts