[그림1] stolenbyte를 구하는 문제
[그림2] 실행창1
[그림3] 실행창2

 

StolenByte

 패킹된 바이너리를 언패킹할 때의 과정을 방해하기 위한 방법으로, 프로그램의 일부 바이트를 별도의

영역에서 실행되게 하여 OEP를 다른 위치로 가장하고 덤프를 쉽게 하지 못하도록 구현한 기법

 

 패커가 패킹을 진행할 때, 원본 코드 중 일부를 다른 곳으로 이동 시킨 코드로써, 주로 EP 위의 몇 줄의 코드이다

 

  올바른 프로그램을 얻기 위해서는 패킹 과정에서 떼어진 StolenByte를 함께 복원해야 덤프가 성공적으로 이루어진다

 

[그림4] 프로그램의 시작 코드가 PUSHAD로 되어 있다. 패킹된 파일임을 짐작할 수 있다.

 

[그림5] PEiD로 확인한 결과. 역시 UPX 로 패킹이 되어있다.

 

[그림6] upx -d 옵션을 활용해 unpacking

 

[그림7] unpacking 후 확인한 코드
[그림8] 0040100E 주소의 MessageBox함수 호출 결과

unpacking 후 디버거로 열었을 때, 처음에 NOP로 덮어진 부분이 눈에 띄었다.

첫 번째로 실행되는 MessageBoxA 함수에 전달할 인자가 부족함을 알 수 있었고

이를 실행했을 때, 온전하지 못한 글자들과 함께 결과가 보여짐을 확인할 수 있었다.

 

이를 통해 부족한 인자들이 문제와 연관있음을 예측했다.

 

 

[그림9] unpacking 하지 않은 실행파일의 POPAD 부분 확인

unpacking 시 stolenbyte 문제가 생긴다는 것을 깨달은 후,

다시 원본 실행 파일에서 POPAD 부분을 찾아갔다.

(POPAD 찾는 방법은 아래를 확인)

2021.04.07 - [Study/Reversing] - CodeEngn Challenge : Basic RCE L06

 

그리고 POPAD 이후 스택에 적재되는 세 개의 값을 확인할 수 있었다.

 

OEP로 점프하는 구간에 BP를 걸고 실행시켰더니

MessageBoxA 함수 호출에 필요한 인자들이 스택에 PUSH 됨을 알 수 있고,

이를 통해 PUSH 되는 인자들이 StolenByte 임을 확인했다.

 

 

[그림10] NOP로 덮여진 부분

 

[그림11] [Ctrl+E] 로 코드 변경

 

[그림12] 정상적 unpacking 성공

 

따라서 stolenbyte는 6A0068002040006812204000 이다.

'Study > Reversing' 카테고리의 다른 글

CodeEngn Challenge : Advance RCE L09  (0) 2021.04.28
CodeEngn Challenge : Advance RCE L03  (0) 2021.04.28
CodeEngn Challenge : Basic RCE L06  (0) 2021.04.07
CodeEngn Challenge : Basic RCE L16  (0) 2021.03.30
CodeEngn Challenge : Basic RCE L15  (0) 2021.03.30

[그림1] 문제

문제에서 packing이 된 실행파일이라는 것을 알려주고 있다.

 

[그림2] 첫 실행화면

 

[그림3] 값 입력 후 뜨는 결과창

 

[그림4] PEiD로 확인한 실행파일

+) PEiD는 파일의 PE 정보로 패킹, 암호화, 컴파일에 사용된 프로그램을 찾아준다.

실행파일을 올려 확인해 보니 UPX 프로그램으로 패킹됐음을 유추할 수 있다.

 

 

<Packing & UnPacking 설명 참고>

더보기

패킹(packing)은 "실행 압축"이라고 표현할 수 있다.

실행 파일 내부에 있는 코드를 압축하여 평소에는 코드를 압축한 상태로 저장하고 있다가

파일을 실행하면 메모리에서 압축을 해제 시킨 후 파일을 실행시키는 기술이다.

 

장점

1. 파일의 크기를 줄일 수 있다.(전 후 3~4배의 차이가 있다)

2. 크래커의 리버싱으로부터 보호할 수 있다.

 


언패킹(unpacking)은 말 그대로 패킹을 푸는 것이다.

특히 UPX Compressor를 사용하는 경우는 아래의 상황이다.

- UPX 기준 설명

1. 운영체제의 Loader가 실행파일을 메모리에 올린다

2. Packing된 파일은 Unpacking Code 시작부분이 Entry Point이다.

3. EP에서 프로그램이 시작된다.

4. Packing된 파일은 Unpacking Code 영역에 들어있다.

5. Unpacking Code는 패킹된 데이터를 하나씩 읽어 압축을 풀고 Empty Space(빈 공간)에 원래 데이터를 저장한다.

6. 모든 코드가 Unpacking 되었다면 Original Entry Point(OEP)에서 프로그램이 다시 시작된다.

 

즉, OEP는 Unpacking 되기 이전 실행파일의 시작점이다

 

<UPX로 패킹된 실행파일을 unpacking 하는 방법>

1. upx 프로그램의 -d 옵션 사용

[그림5] upx 프로그램으로 unpacking 하는 과정

-d 옵션을 사용하면 패킹된 파일을 언패킹한 후 덮어 씌울 수 있다.

 

그 후 디버거에 올려서 실행하면 바로 OEP를 알 수 있다.

 

 

2. PUSHAD/POPAD 명령어 찾기

- UPX는 실행 파일 압축 프로그램으로 함수를 계속 진행하면 OEP에 도달할 수 있다.

UPX는 PUSHAD와 POPAD를 활용해 언패킹한다.

 

PUSHAD는 범용 레지스터의 값들을 스택에 저장하며, POPAD와 짝을 이루어서 사용된다.

코드의 PUSHAD가 진행되면 범용 레지스터의 값들을 EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI 순서로 스택에 저장한다.

POPAD는 스택에 저장된 레지스터 값들을 다시 레지스터에 입력한다.

보통 POPAD 밑에 점프문이 있으며 이 점프문 주소에 해당하는게 OEP인 경우가 많다.

 

① PUSHAD를 지나 마지막 레지스터 값(EDI)이 스택에 저장되었을 때,

② 그 스택부분에 BP(Break Point)를 걸면

③ POPAD에서 마지막 레지스터(EDI)를 복구시킨 후 정지된다. (스택 BP 사용시 해당 스택의 데이터가 접근 되는 시점에 브레이크가 잡히게 된다.)

④ 마지막으로 , POPAD가 끝난 후 진행되는 코드를 진행하면 원본 시작 코드로 진입할 수 있다.

 

[그림7] packing된 실행파일 처음 화면

 

[그림8] PUSHAD 작동 후 상황

각 레지스터에 값이 저장되어 있고 마지막으로 PUSH 되었던 EDI의 주소로 가 스택에 저장된 값을 확인한다.

 

[그림9] 마지막으로 PUSH 된 레지스터 값에 접근

 

[그림10] 스택에 BP 걸기

 

[그림11] [F9]키 실행 결과

이 점프문 주소(00401360)에 해당하는 게 OEP 이다.

 


문제는 OEP 뿐만이 아니라 Serial 값을 찾도록 요구하고 있다.

따라서 이 실행파일을 unpacking 한 후 다시 분석해보았다.

 

[그림12] 프로그램에 사용된 문자열들을 확인

잘못된 값을 입력했을 때 GUI 모드로 'Wrong serial!'이라는 문자열이 떴기 때문에 

프로그램에 사용된 문자열들을 확인한다.

 

[그림13] 선택한 문자열 주변 코드

'You got it' 이라는 문자열 코드가 있는 부분을 선택해 주변을 살펴보았다.

CMP 문과 JMP문이 사용된 것으로 보아 내가 입력한 값과 정답 serial 값을 비교해 

성공인지 실패인지를 가려내어 결과를 보여주는 것으로 보인다.

 

JMP문에 [F2]키를 사용해 BP를 걸고 [F9]키를 사용해 실행한다.

 

[그림14] 그러면 이러한 실행창이 뜬다.

 

[그림15] 값을 입력하고 Check Serial 버튼을 누른 결과

 

내가 입력한 값이 어디론가 PUSH 되고 있다.

아마 그 다음에 나오는 "AD46DFS547"이라는 값과 비교하여 결과가 나뉘는 것 같다.

 

따라서 정답 serial 은 "AD46DFS547" 이라고 유추 가능하다.

 

'Study > Reversing' 카테고리의 다른 글

CodeEngn Challenge : Advance RCE L03  (0) 2021.04.28
CodeEngn Challenge : Basic RCE L09  (0) 2021.04.07
CodeEngn Challenge : Basic RCE L16  (0) 2021.03.30
CodeEngn Challenge : Basic RCE L15  (0) 2021.03.30
Reversing.kr 1번 풀이  (0) 2021.03.24

분석 도구 : Immunity Debugger

 

저번 게시물에 올렸던 CodeEngn 15번 문제와 아주 유사하다

 

2021.03.30 - [Study/Reversing] - CodeEngn Challenge : Basic RCE L15

참고하면 이해하는 데 아주 도움이 될 것이다!!!!!!!

 

이번엔 CUI 모드로 작동하는 듯 싶다.

 

잘못된 password를 입력하면 'Wrong password!' 문자열이 뜨고

해당 exe 파일이 있는 곳에 null 이라는 파일이 생긴다.

 

[Search for] - [All referenced text strings] 를 통해 힌트 구간을 얻어본다.

 

'Good job!' 이 있는 곳을 더블클릭하여

해당 코드가 있는 곳으로 이동한다.

 

 

이번에도 역시 분기문이 보인다. (CMP)

 

EAX 값과 SS 값(EBP-3C 주소 기준 4byte) 를 비교한 후,

두 값이 같지 않으면 

'wrong password!' 문자열이 출력되는 주소로 점프해버린다.

 

따라서 우리는 EAX 값에 어떤 값이 들어가는지EBP-3C에 들어가있는 값을 확인해야 한다.

 

 

4015A2 에 BP를 건 후 실행하였고, password(serial)에는 1234를 입력해 주었다.

EAX에 들어가는 것은 우리가 입력했던 1234(10진수)가 000004D2(16진수)로 바뀐 형태의 값인 것을 알 수 있다.

 

 

EBP-3C에 있는 값은 E4C60D97 이다.

따라서 우리가 원하는 serial 값을 찾을 수 있다.

 

 

 

'Study > Reversing' 카테고리의 다른 글

CodeEngn Challenge : Basic RCE L09  (0) 2021.04.07
CodeEngn Challenge : Basic RCE L06  (0) 2021.04.07
CodeEngn Challenge : Basic RCE L15  (0) 2021.03.30
Reversing.kr 1번 풀이  (0) 2021.03.24
main 함수 찾기  (0) 2021.03.23

분석 도구 : Immunity Debbuger

 

문제는 Name이 CodeEngn일 때 Serial을 구하라는 것

 

해당 exe 파일을 실행해보니 실제로 Name과 Serial을 적는 칸이 있다.

 

애초에 문자는 입력할 수 없는 것 같고

 

맞지 않는 Serial 숫자를 적으면 'Try Again !' 이라는 문자열이 뜨는 것 같다.

 

 

디버거에 실행 파일을 올린 후 분석 시작

 

먼저 'Try Again !' 등 문자열이 있는 곳에 문제의 해답이 있을 수 있으므로

[Search for] - [All referenced text strings] 로 실행 파일에 담겨진 모든 문자열을 확인한다.

 

 

crack이 목적이므로

'CRACKED' 가 있는 곳으로 더블클릭하여 이동한다.

 

 

+) CMP Dest, Src

CMP 구문은 두 개의 피연산자를 비교한다.

Destination 피연산자에서 Source 연산자를 묵시적으로 빼서 값을 비교한다.

 

두 피연산자의 값이 같으면 결과는 0이 될 것이고, ZF(Zero Flag)는 1이 된다.

두 값이 다르면 결과는 0이 아닌 값, ZF는 0이 된다.

 

+) 명령어 DWORD PTR DS : [주소값]

DWORD 는 크기(BYTE : 1byte, WORD : 2bytes, DWORD : 4bytes),

PTR 은 기준을 의미한다.

 

즉, '주소값을 기준으로 4byte를 ~명령어~ 한다' 는 의미

 

 

따라서, CMP EAX, DWORD PTR DS:[45B844]의 의미는

EAX 값과 DS 값(45B844 주소 기준 4Byte 값)을 비교해서 

두 값이 같으면 ZF=1, 다르면 ZF=0 이 된다는 것

 

 

+) JNZ (Jump Not Zero) 주소 = JNE (Jump Not Equal) 주소

결과가 0이 아닐 때(ZF=0) 해당 주소로 이동하고,

0이 아닐 때(ZF=1) 바로 다음(아래) 명령어를 실행한다.

 

 

따라서, JNZ SHORT 00458854 의 의미는

위에서 비교했을 때 두 값이 같으면 바로 다음 명령어를 실행(성공 메시지),

두 값이 다르면 00458854(Try Again) 로 점프한다는 것

 

 

분기점 (00458837) 에 BP를 설정하고

두 개중 어떤것에 serial 값이 저장됐는지 확인하면 쉽게 serial 값을 얻을수 있을 것이다.

 

 

serial 값에 1234 를 입력했더니 

EAX 에 000004D2가 저장이 된다.

 

우리가 입력한 1234(10진수) → 4D2(16진수)로 바뀌어 저장된 것 !!!

 

 

CMP 명령어에서 DS 값 (45B844 주소 기준 4Byte)과 비교하므로

45B844 주소로 가서 들어있는 값을 확인하였다.

00006160(16진수)가 저장되어 있다. 

이제 이를 10진수로 변경하면 원하는 serial을 획득할 수 있다!

 

00006160 → ?????

 

'Study > Reversing' 카테고리의 다른 글

CodeEngn Challenge : Basic RCE L06  (0) 2021.04.07
CodeEngn Challenge : Basic RCE L16  (0) 2021.03.30
Reversing.kr 1번 풀이  (0) 2021.03.24
main 함수 찾기  (0) 2021.03.23
OllyDBG, 어셈블리어  (0) 2021.02.04

+ Recent posts