10번 문제는 Reversing 문제이다.

 

홈페이지에서 파일을 Download 한 후에 열어보면 Reversing 이라는 exe파일이 들어있다.

 

 

이 파일을 우선 Hex Editor로 열어보자.

 

 

대충 내리다 보면 이렇게 Auth Key가 나온다.

 

하지만 이렇게 풀면 재미가 없다.

 

ExeInfo PE로 들어가보자.(HxD가 PEID를 사용할 줄 아냐고 물어봤으니까)

 

 

.NET으로 코딩되어 있는 프로그램인 것 같다.

 

.NET 디스어셈블러를 찾아보니 괜찮은 프로그램이...

 

 

믿고 쓰는 Jetbrains의 dotPeek 이라는 프로그램이 있다!

 

설치 후 Reversing.exe를 dotPeek으로 열어본다.

 

 

 

이런 화면이 뜬다.

 

수업시간에 C# 코딩할 때, Visual Studio로 프로젝트를 만들면 볼 수 있는

 

WindowsFormsApplication 을 찾아본다.

 

 

여기서 button1_Click, button2_Click, label2_Click 세 개가 보인다.

 

하나씩 살펴본다

 

1. button1_Click

 

 

button1을 클릭하면 실행되는 것이 적혀있다.

 

str="2theT@P" 이랑 textBox1에 들어간 Text랑 비교해서

같으면 → text="Authkey : Did U use the Peid?" 출력

다르면 → "Try again!" 출력

 

여기서 정답은 2theT@P 이라는 것을 알 수 있다.

 

 

정답!!!

 

2. button2_Click

 

 

아마 button2를 누르면 프로그램이 종료되는 것으로 예상할 수 있다.

 

 

실제로 눌러보면 프로그램이 종료된다.

 

3. label2_Click

 

 

label2를 클릭하면, suninatas 메인 홈페이지로 들어간다는 것을 알 수 있다.

 

 

고로 정답은...

 

2theT@P 입력 시...

Authkey : Did U use the Peid? 출력!

'보안 & 모의해킹 > SuNiNaTaS' 카테고리의 다른 글

[SuNiNaTaS] challenges_01  (0) 2019.10.07
[SuNiNaTaS] challenges_04  (0) 2019.10.07
Posted by NDC :

어떤 문자열을 입력하면 Check를 한다.

 

asp 웹 문법이 2종류가 나온다.

1. Replace(str, "a", "aad") → 입력한 문자열(=str)에서 "a"라는 문자를 "aad"로 바꿈.

2. Mid(str, 2, 2) → 입력한 문자열(=str)에서 2번째 문자포함 2개의 문자를 가져온다.

(3번째 인자의 값이 총 문자열의 길이 이상일 경우(out of range) : 문자열 끝까지 반환)

(문자열을 셀 때 맨 앞의 첫 문자가 0번째 문자가 아님. 1번째 문자)

 

Replace(str, "a", "aad")

입력한 문자열(=str)에서 "a"라는 문자를 "aad"로 바꿈.

Replace(str, "i", "in")

입력한 문자열(=str)에서 "i"라는 문자를 "in"로 바꿈.

Mid(str, 2, 2)

입력한 문자열(=str)에서 2번째 문자포함 2개의 문자를 가져온다.

Mid(str, 4, 6)

입력한 문자열(=str)에서 4번째 문자포함 6개의 문자를 가져온다.

 

result = result1 & result2

result1 = "abc" 이고 result2 = "def" 일 경우

result 는 "abcdef" 가 된다.

 

Response.write result

이 코드는 result 라는 변수에 결과 값을 쓰는 코드로 보인다.

 

그렇게 해서 만들어진 result 변수의 문자열을 검사해서 "admin"이라는 문자열과 같으면

PW를 알려준다.

 

그럼 우리가 할 일은?

 

"STR" → Replace(str, "a", "aad") → Replace(str, "i", "in") → Mid(str, 2, 2) → Mid(str, 4, 6) → & 연산 → "admin"

에서

 

"STR"에 어떤 문자열을 넣어야 하나? 이다.

 

나는 이 문제를 풀 때 Replace(str, "a", "aad")가 있으므로

"a"부터 시작했다.

 

STR = "a"

 

그러면 "a"는 "aad"로 바뀌고, "admin"이 나와야 하므로 "m"을 추가했다.

 

STR = "am" → "aadm"

 

그리고 "in"이 나와야 하는데, "i"만 넣어도 "in"이 반환되므로 "i" 만 추가한다.

 

STR = "ami" → "aadmi" → "aadmin"

 

다음, result1은 Mid(str, 2, 2) 이므로

result1 = "ad"

 

result2는 Mid(str, 4, 6) 이므로

result2 = "min" (→ 6개가 다 없기 때문에 문자열 끝까지만 반환한다)

 

결국, result = result1  & result2 이기 때문에

result = "admin" 이 나온다.

 

blank에 ami를 치면

 

정답이다.

 

그러면 아래에 이렇게 Authkey가 공개된다.

 

1번은 너무 쉬운 문제였다.

'보안 & 모의해킹 > SuNiNaTaS' 카테고리의 다른 글

[SuNiNaTaS] challenges_10  (0) 2019.11.06
[SuNiNaTaS] challenges_04  (0) 2019.10.07
Posted by NDC :

Plus 버튼을 누르면 Point 값이 올라간다.

하지만 무한정 올라가는 것이 아님.

 

25까지 올리게 되면

이런 창이 뜨면서 더 이상 올라가지 않는다.

 

Firefox 에서 F12를 눌러 페이지 소스를 본다.

 

일부분 캡쳐본.

 

Hint 를 보면

Make your point to 50 & 'SuNiNaTaS'

라고 써져있다.

 

대충 볼 수 있는것이

1. User-Agent

2. Auth key

두 개이다.

 

* SuNiNaTaS 브라우저를 원한다고 했으니 User-Agent 를 Mozilla/5.0~~~에서 SuNiNaTaS로 바꿔주고 *

point를 검사하는 web04_ck.asp 페이지의 헤더(User-Agent)를 바꾼 상태에서 50번 GET하고

현재 point를 볼 수 있는 web04.asp 페이지를 GET하면

 

web04_ck.asp 에서 point가 50일 때 GET한 web04.asp 페이지의 Auth key를 읽어올 수 있을 것이다.

 

이는 Python 코드로 작성하였다.

 

"""
title : SuNiNaTaS 4
date : 2019-10-07
"""
 
import requests
 
if __name__ == '__main__':
    url = 'http://suninatas.com/challenge/web04/web04_ck.asp'
    url2 = 'http://suninatas.com/challenge/web04/web04.asp'
 
    sess = requests.Session()
    headers = {'User-Agent''SuNiNaTaS'}
    count = 0
 
    while True:
        resp1 = sess.post(url, headers=headers)
        resp2 = sess.get(url2, headers=headers)
        count = count + 1
        print("POST count:", count)
        print(resp2.text)
    
        if count > 50:
            break
    
    exit(0)

url = 'http://suninatas.com/challenge/web04/web04_ck.asp'

url2 = 'http://suninatas.com/challenge/web04/web04.asp'

 

각 url 문자열을 저장한 후,

headers = {'User-Agent': 'SuNiNaTaS'} 로 바꿀 헤더의 User-Agent 정보를 저장한다.

 

response1 = sess.post(url, headers=headers) 로 web04_ck.asp 위치에 POST를 보내고

이에 따른 response2 = sess.get(url2, headers=headers) 로 web04.asp 페이지를 GET한다.

이후에 resp2.text 를 출력하면 GET한 resp2 의 소스 코드가 출력이 되는데,

 

여기서 Auth key 부분을 찾으면 된다.

 

 

 

Point=11, User-Agent=SuNiNaTaS, Auth key=?????

 

 

Point=35, User-Agent=SuNiNaTaS, Auth key=?????

 

 

Point=49, User-Agent=SuNiNaTaS, Auth key=?????

 

 

Point=50, User-Agent=SuNiNaTaS, Auth key=Change your Us3r Ag3ent

아마 50번 Plus 하면 자동으로 0으로 리셋되는 것 같다.

POST count가 50일 때 Auth key가 공개되는 것을 알 수 있다.

 

 

참고 : POST count가 51일 땐, 다시 1부터 count한다. 역시 Auth key는 POST count가 50이 아니므로 ?????로 출력된다.

'보안 & 모의해킹 > SuNiNaTaS' 카테고리의 다른 글

[SuNiNaTaS] challenges_10  (0) 2019.11.06
[SuNiNaTaS] challenges_01  (0) 2019.10.07
Posted by NDC :

문제 : 이 프로그램은 디버거 프로그램을 탐지하는 기능을 갖고 있다. 디버거를 탐지하는 함수의 이름은 무엇인가

 

실행파일을 실행해본다.

디버거 모드가 아닌 일반 모드에서 실행시켰기 때문에 저렇게 정상 문자열이 반복적으로 출력된다.

그러면 Olly-Dbg로 열었을 때는?

이렇게 디버깅 당함 문자열이 반복 출력된다.

 

이전 3번 문제와 마찬가지로 함수 이름을 묻는 문제이기 때문에

우클릭 > Search for > All intermodular calls 로 함수 목록을 볼 필요가 있다.

가장 눈에 띄는 함수는 IsDebuggerPresent이다.(지금 현재 Debugger 상태인가?)

위 함수를 더블클릭해서 해당 위치로 이동한다.

 

해당 위치에 BP를 걸고 F9를 눌러서 실행시킨다.

해당 위치까지 실행되었지만, 아무것도 발생하지 않았다.

 

추측에 따르면, 아마 저 함수가 실행되고 난 후에 어떤 반환값을 반환받고,

그 다음 줄에 있는 CMP(Compare) 명령을 실행한다.

비교되는 값에 따라서 "정상" 문자열인지 "디버깅 당함" 이라는 문자열이 출력 될 것이다.

 

F8을 눌러서 Step over를 실행하자.

Step over이후, EAX에 반환값으로 00000001이 입력되었다.

하지만, 예상과 다르게 콘솔 창에 "디버깅 당함"이라는 문자열은 출력되지 않았다.

 

F8로 계속 실행시켜보면,

딱 저 부분에서 "디버깅 당함"이라는 문자열이 출력된다.

문자열 출력 함수로 보이는데, 저기 Arg1을 인자로 받아서 출력하는 것 같다.

문자열을 코드 부분에서 찾으면 의미가 없고, 좌측 하단에 Hex값을 보여주는 부분에서 찾아본다.

 

(참고) Ctrl + G 단축키는 해당 주소로 이동시켜주는 단축키이다.

Ctrl + G 에 00431024를 입력해보면,

00431024 위치에 "디버깅 당함"이라는 문자열이 저장되어 있었다.

그럼 함수 00408190은 단순히 콘솔창에 문자열을 출력시켜주는 함수임을 알 수 있다.

 

F8로 계속 진행하면,

이 부분이 반복 실행된다.

대충 살펴보면

 

1. 1초 쉰다.(1000ms sleep)

2. CALL 00408210 실행

3. IsDebuggerPresent로 디버그 모드인지 판별

4. CALL 00408210 실행

5. "정상" 혹은 "디버깅 당함" 문자열 받아옴

("디버깅 당함" 문자열은 00431024위치에 있다는 것을 확인. "정상" 문자열은 0043101C 위치에 있을 것으로 추정)

6. 00408190 함수 실행 : 문자열을 콘솔 창에 출력해주는 함수

 

여기서 내가 궁금한 부분은, 00408210 위치에 있는 함수가 어떤 일을 하는지였다.

그래서 F7(Step into)로 들어가서 살펴보았는데, 별 중요한 함수는 아닌 것으로 판단하였다.

 

또 다른 궁금점은, 디버깅 모드에서 "정상" 문자열을 출력할 수 있는가였다.

그래서 IsDebuggerPresent가 종료된 후, EAX 값을 0으로 바꿔보았다.

예상대로 "정상" 문자열을 출력하였다.

 

그럼 IsDebuggerPresent에서는 무슨 일을 할까?

위 이미지는 IsDebuggerPresent의 내부이다.

 

딱 세 줄이 있는데,

1. MOV EAX, DWORD PTR FS:[30]

2. MOVZX EAX, BYTE PTR DS:[EAX+2]

3. RETN

이다.

 

(참고)

MOVZX

1. byte나 word를 word나 double-word 목적지에 전송

2. 0비트로 목적지 피연산자의 왼쪽 비트들을 채움.

 

첫번째, FS:[30]을 보면,

FS는 세그먼트 레지스터이다.

FS:[0x00] 4 Win9x and NT 현재 Structured Exception Handling (SEH) 프레임
FS:[0x04] 4 Win9x and NT 스택 베이스 / 스택의 바닥 (높은 주소)
FS:[0x08] 4 Win9x and NT 스택 한계 / 스택의 천장 (낮은 주소)
FS:[0x0C] 4 NT SubSystemTib
FS:[0x10] 4 NT Fiber data
FS:[0x14] 4 Win9x and NT 임의 데이터 슬롯
FS:[0x18] 4 Win9x and NT Thread Environment Block
---- NT subsystem 독립적인 부분 끝 ----
FS:[0x1C] 4 NT Environment Pointer
FS:[0x20] 4 NT 프로세스 ID (몇몇 윈도우 버전에서 이 필드는 'DebugContext'로 쓰인다.)
FS:[0x24] 4 NT 현재 스레드 ID
FS:[0x28] 4 NT 활동중인 RPC 핸들
FS:[0x2C] 4 Win9x and NT thread-local storage 배열의 선형 주소
FS:[0x30] 4 NT Process Environment Block (PEB)의 선형 주소
FS:[0x34] 4 NT 마지막 오류 번호
FS:[0x38] 4 NT Count of owned critical sections
FS:[0x3C] 4 NT CSR 클라이언트 스레드의 주소
FS:[0x40] 4 NT Win32 스레드 정보
FS:[0x44] 124 NT, Wine Win32 클라이언트 정보 (NT), user32 private data (Wine), 0x60 = LastError (Win95), 0x74 = LastError (WinME)
FS:[0xC0] 4 NT Reserved for Wow64. Wow64에서 FastSysCall에 대한 포인터를 포함한다.
FS:[0xC4] 4 NT Current Locale
FS:[0xC8] 4 NT FP Software Status Register
FS:[0xCC] 216 NT, Wine Reserved for OS (NT), kernel32 private data (Wine)

herein: FS:[0x124] 4 NT Pointer to KTHREAD (ETHREAD) structure

FS:[0x1A4] 4 NT 예외 코드
FS:[0x1A8] 18 NT Activation context stack
FS:[0x1BC] 24 NT, Wine Spare bytes (NT), ntdll private data (Wine)
FS:[0x1D4] 40 NT, Wine Reserved for OS (NT), ntdll private data (Wine)
FS:[0x1FC] 1248 NT, Wine GDI TEB Batch (OS), vm86 private data (Wine)
FS:[0x6DC] 4 NT GDI Region
FS:[0x6E0] 4 NT GDI 펜
FS:[0x6E4] 4 NT GDI 브러시
FS:[0x6E8] 4 NT Real Process ID
FS:[0x6EC] 4 NT Real Thread ID
FS:[0x6F0] 4 NT GDI cached process handle
FS:[0x6F4] 4 NT GDI 클라이언트 프로세스 ID (PID)
FS:[0x6F8] 4 NT GDI 클라이언트 스레드 ID (TID)
FS:[0x6FC] 4 NT GDI thread locale information
FS:[0x700] 20 NT Reserved for user application
FS:[0x714] 1248 NT Reserved for GL
FS:[0xBF4] 4 NT 마지막 상태 값
FS:[0xBF8] 532 NT Static UNICODE_STRING buffer
FS:[0xE0C] 4 NT deallocation stack에 대한 포인터
FS:[0xE10] 256 NT TLS 슬롯, 슬롯 당 4 byte
FS:[0xF10] 8 NT TLS 링크 (LIST_ENTRY structure)
FS:[0xF18] 4 NT VDM
FS:[0xF1C] 4 NT Reserved for RPC
FS:[0xF28] 4 NT 스레드 에러 모드(RtlSetThreadErrorMode)

FS 레지스터의 구조이다.

(출처 : https://ko.wikipedia.org/wiki/Win32_%EC%8A%A4%EB%A0%88%EB%93%9C_%EC%A0%95%EB%B3%B4_%EB%B8%94%EB%A1%9D (위키피디아))

우리가 찾는[30]은 Pointer to PEB라는 것임을 알 수 있는데,

 

Pointer to PEB를 구글에 검색해보면,

프로세스 환경 블록

프로세스 환경 블록 (PEB: Process Environment Block)은 윈도우 NT에서의 데이터 구조체이다. 이것은 운영체제 내부에서 사용하는 opaque data structure이다. 마이크로소프트는 마이크로소프트 개발자 네트워크(MSDN) 라이브러리 문서에서(오직 몇 개의 필드만 문서화한) 아마 다음 버전의 구조체에서는 바뀌게 될 것이라고 언급했다. PEB는 전역 문맥, 시작 파라미터, 프로그램 이미지 로더를 위한 데이터 구조체, 프로그램 이미지 베이스 주소 그리고 동기화 객체들을 포함하는, 프로세스 전체에 적용되는 데이터 구조체를 담고 있다.

PEB 커널 모드 EPROCESS 데이터 구조체 뿐만 아니라 클라이언트/서버 런타임 하위 시스템의 주소 공간에서 관리되는 프로세스 당 데이터 구조체와 밀접하게 관련되어 있다. 그러나 PEB는 자체적으로는 (CSRSS 데이터 구조체처럼) 커널 모드 데이터 구조체가 아니다. 이것은 관련된 프로세스의 사용자 모드 주소 공간에 위치한다. 왜냐하면 NTDLL 같은 운영체제 라이브러리의 사용자 모드 코드에서 사용되기 위해 디자인되었기 때문이다.

WinDbg에서, PEB의 내용을 복사해 오는 명령어는!peb 이다. 결국, 이 정보는 EPROCESS 데이터 구조체(필드 중 하나가 PEB의 주소이다.)의 정보를 보여주는 !process 명령어에 의해서 얻어진다.

 

출처 : https://ko.wikipedia.org/wiki/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4_%ED%99%98%EA%B2%BD_%EB%B8%94%EB%A1%9D (위키피디아)

BeingDebugged 프로세스가 디버깅 당하는 중인지 마이크로소프트는 이 필드 대신 공식적인 Win32를 사용할 것을 권장한다. CheckRemoteDebuggerPresent()[2]
Ldr 로드된 모듈에 대한 정보를 제공하는 PEB_LDR_DATA 구조체를 가리키는 포인터 kernel32ntdll의 베이스 주소가 들어 있다.
ProcessParameters 로드된 모듈에 대한 정보를 제공하는 RTL_USER_PROCESS_PARAMETERS 구조체를 가리키는 포인터 RTL_USER_PROCESS_PARAMETERS 구조체는 또한 대부분이 문서화되어 있지 않으며, 여러 버전에서 사용할 수 있다는 보장이 없다.[4]
PostProcessInitRoutine DLL 초기화 이후, 메인 실행 코드가 발동되기 이전에 호출되는 콜백 함수에 대한 포인터 이 콜백 함수는 윈도우 2000에서 사용되지만, 윈도우 NT 이후 버전에서 사용될 수 있다는 보장은 없다.[2]
SessionId 프로세스를 부분으로 갖는 터미널 서비스 세션의 세션 ID NtCreateUserProcess() 시스템 호출은 커널 내부의 MmGetSessionId() 함수를 호출함으로써 초기화한다.[3]

출처 : https://ko.wikipedia.org/wiki/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4_%ED%99%98%EA%B2%BD_%EB%B8%94%EB%A1%9D (위키피디아)

 

위 PEB 구조를 통해 안티 디버깅을 할 수 있다.

 

우선적으로 FS:[30]으로 디버깅 모드인지 아닌지 확인한다(BeingDebugged)

- 0 : 디버깅 모드가 아님(False)

- 1 : 디버깅 모드임(True)

 

그리고 MOVZX EAX, BYTE PTR DS:[EAX+2] 로 해당 값을 가져온다.

여기서 EAX = 003BA000, [EAX+2] = [003BA002] = 01

즉, 01값이 리턴된다.(앞에는 0으로 채워서 00000001)

 

그러므로 IsDebuggerPresent 함수의 RETN 값으로 00000001이 되는 것이고,

이는 디버깅 모드임을 알려주는 값이라고 볼 수 있다.

'보안 & 모의해킹 > CodeEngn' 카테고리의 다른 글

[CodeEngn] Basic RCE L03 풀이  (0) 2019.07.01
[CodeEngn] Basic RCE L02 풀이  (0) 2019.07.01
[CodeEngn] Basic RCE L01 풀이  (0) 2019.07.01
Posted by NDC :

문제 : 비주얼베이직에서 스트링 비교함수 이름은?

 

실행파일을 실행해 본다.

여기에 Regcode 부분에 문자열을 아무거나 입력해본다.

이렇게 출력된다.

 

아마 프로그램에 내장된 어떤 문자열과 사용자가 입력한 문자열을 비교해서 인증하는 방식으로 추측할 수 있다.

 

이 프로그램을 STUD_PE로 열어보자.

우선, EP(Entry Point)는 ImageBase + EntryPoint(rva) = 00401168임을 알 수 있다.

UPX 패킹은 되어있지 않고,

MSVBVM50.dll 을 사용하는 것으로 보아 Visual Basic 프로그램임을 알 수 있다.

 

이제 Olly-Dbg로 열어보자.

STUD_PE에서 본 것처럼 EP는 00401168이다.

 

보통 VB 프로그램은 진짜 main 부분이 나오기 전에 쓸모없는 코드(프로그램이 필요로 하는 라이브러리 로딩 등)가 앞에 존재한다고 알고있다.

 

그러므로 우리는 문자열 비교함수의 이름을 알기위해 해당 함수를 찾아보아야 한다.

 

디버거의 Code 부분에서 우클릭 > Search for > All intermodular calls 로 들어가면

프로그램이 사용하는 함수들의 이름을 목록으로 볼 수 있다.

이 프로그램이 사용하는 전체 함수 이름이다.

 

이 목록은 Address 정렬되어있는 상태이므로, 위에서부터 차례대로 실행된다고 볼 수 있다.

 

아래로 한줄한줄 찾다보면 눈에띄는 함수가 하나 보이는데

 

MSVBVM50.__vbaStrCmp

이다.

 

왠지 이 부분에서 문자열(Str, string) 비교(Cmp, Compare)가 이루어 질 것 같다.

 

해당 함수 이름을 더블클릭하면 위치로 이동할 수 있다.

이런 화면을 볼 수 있음.

함수가 위치한 주소는 0040114A이다.

 

이제 위에서 사용자가 입력한 문자열을 입력받고, 프로그램에 적혀있는 정답 문자열을 입력받고

이 두개를 파라미터로 사용하여 0040114A로 넘겨 vbaStrCmp 함수를 실행할 것이다.

 

이 위치에서 스크롤을 올려보면

이렇게 나와있다.

우리는 0040114A 함수를 호출해주는 004028C2에 BP를 걸고 프로그램을 돌려 볼 필요가 있다.

 

BP를 걸고 F9로 프로그램을 실행시켜 보면

프로그램이 실행되고,

이 프로그램에서 Regcode에 아무 문자열이나 입력한 후에 Registreieren을 클릭하자.

난 asdasdads를 입력했다.

 

그러면 우리가 BP를 걸었던 위치까지 프로그램이 실행된다.

 

디버깅을 할때, 혹은 어셈블리를 할때 함수를 실행할려면?

 

그 함수에 들어가는 인자값을 받아야 한다.(물론 필요없는 함수도 있지만 vbaStrCmp같은 함수는 필요함.)

 

그 함수는 보통 Stack에 들어간다.

지금 함수가 실행되기 직전의 Stack을 살펴보면(우측 하단)

맨 위에 어떤 문자열이 있고, 그 다음 줄에 내가 입력한 asdasdads 문자열이 들어가 있다.

 

바로, vbaStrCmp 함수가 함수 실행에 필요한 문자열을 인자로 저장한 과정이다.

 

여기서 우리는 2G83G35Hs2 문자열과 사용자가 입력한 문자열 두개를 비교한다는 것을 알 수 있다.

 

그럼 예상 정답을 알았으니, 디버거를 끄고 프로그램을 일반적인 상태로 실행해보자.

Regcode 부분에 예상 정답을 입력한다.

 

만약 2G83G35Hs2 문자열이 정답이라면?

1. vbaStrCmp 함수가 문자열 비교함수의 이름이 맞음.

2. 위의 함수의 비교 대상이 되는 문자열(비밀번호)은 2G83G35Hs2 이다.

 

이 두개를 알 수 있다.

Registrieren 버튼을 클릭하니, 오류 메시지 대신 정답 메시지가 출력된다.

 

궁금해서 추가로 해본 것은, RCE L02처럼 Hex Editor에서 위의 문자열을 찾아낼 수 있는가였다.

해당 프로그램을 Hex Editor로 열어보면

이렇게 문자열을 찾을 수 있다.

2번 문제와 마찬가지로 정답 시 출력하는 문자열 근처에서 찾을 수 있었다.

(Danke, das Passwort ist richtig!)

 

그리고 위처럼 Hex Editor 에서도 vbaStrCmp 함수의 존재를 찾을 수 있었다.

'보안 & 모의해킹 > CodeEngn' 카테고리의 다른 글

[CodeEngn] Basic RCE L04 풀이  (0) 2019.07.01
[CodeEngn] Basic RCE L02 풀이  (0) 2019.07.01
[CodeEngn] Basic RCE L01 풀이  (0) 2019.07.01
Posted by NDC :

문제 : 패스워드로 인증하는 실행파일이 손상되어 실행이 안되는 문제가 생겼다. 패스워드가 무엇인지 분석하시오

 

실행파일을 실행해보면

라고 뜬다.

이 말은, Olly-Dbg로 하는 동적 분석이 불가능함을 알 수 있다.(당연히 STUD_PE도 안됨)

 

동적 분석이 불가능하면 정적 분석을 해봐야 하는데 가장 대표적인 방법이

Hex Editor로 열어보는 것이다.

 

이렇게 프로그램은 16진수의 코드로 이루어져 있다.

 

이 16진수 코드들을 하나씩 살펴보자.

중간에 내리다 보면 이런 부분이 있다.

이 부분에서는 어떤 것을 찾아낼 수 없을 것 같다.

 

다시 더 내려보자.

이 부분은 왠지 프로그램이 사용한 함수들의 이름이 적혀 있는 것 같다.

1. DialogBoxParamA

2. EndDialog

3. GetDlgItemTextA

4. MessageBoxA

5. SendMessageA

 

6. USER32.dll

7. ExitProcess

8. GetModuleHandleA

9. Kernel32.dll

 

일단 더 내려보자. 위의 부분에서 뭔가를 찾을 수는 없을 것 같다.

더 내리다 보면 위와 같은 부분도 있다.

ADDialog.ArturDents CrackMe#1 : 이 부분은 해당 파일의 Authur 이름과 동일하므로 패스.

Nope, try again! : 패스워드 인증 실패 시 나오는 오류 메시지인 것으로 추정.

Yeah, you did it! : 패스워드 인증 성공 시 나오는 성공 메시지인 것으로 추정.

Crackme #1 : ??? 단순히 프로그램 이름같다.

JK3FJZh : 아마 이부분이 정답 문자열인 것으로 추정.

 

 

왜 JK3FJZh을 정답 문자열로 생각했나?

1. 우선 16진수에서 저렇게 수상하게 ASCII 타입으로 출력되는게 이상함.

2. 다른 문자열과 비교해봤을 때, 가장 패스워드 적인 문자열.

3. 다른 문자열이 다들 의미가 없음.

 

실제로 마지막 부분을 보면

마지막 부분에는 의미없는 문자열로 가득 차 있다.

 

Basic RCE L02는 Hex Editor를 통해 정적 분석을 아주 조금이라도 할 수 있는지 체크하는 문제인 것 같다.

'보안 & 모의해킹 > CodeEngn' 카테고리의 다른 글

[CodeEngn] Basic RCE L04 풀이  (0) 2019.07.01
[CodeEngn] Basic RCE L03 풀이  (0) 2019.07.01
[CodeEngn] Basic RCE L01 풀이  (0) 2019.07.01
Posted by NDC :

문제 : HDD를 CD-Rom으로 인식시키기 위해서는 GetDriveTypeA의 리턴값이 무엇이 되어야 하는가

 

간단하게 C드라이브를 CD-Rom으로 인식시키는 문제이다.

 

문제만 봤을 때는, GetDriveTypeA 함수 실행 시 해당 경로의 드라이브가 C드라이브인 경우의 RETN 값과 CD-Rom  일때의 RETN 값이 다르다는 것을 추측할 수 있다.

 

문제와 함께 제시된 실행 파일을 실행시켜 본다.

여기서 확인을 누르면

CD-Rom 드라이브가 아니라고 출력한다.

 

디버거로 분석하기 전에 프로그램의 기본 정보를 알아보기 위해 STUD_PE 프로그램으로 열어보았다.

32bit 프로그램이고,

UPX 패킹이 되어있지 않음을 알 수 있다.

 

UPX 패킹이 되어있는 프로그램의 경우

저기 CODE, DATA, .idata, .reloc 부분이 전부 UPX0, UPX1로 덮혀져 있다.

UPX로 패킹되어 있는 프로그램의 섹션 예시

이제 이 프로그램을 Olly-Dbg로 열어본다.

전체 프로그램의 어셈블리 코드이다.

 

사용되는 함수는 MessageBoxA와 GetDriveTypeA 이렇게 두 종류가 있다.

 

MessageBoxA : 개발자가 출력하고자 하는 문자열을 보여주는 함수

GetDriveTypeA : 해당 드라이브의 타입에 따라 RETN 값을 보내줌.

 

우리가 봐야할 함수는 GetDriveTypeA이다.

 

대부분의 함수의 리턴 값(RETN)은 EAX 레지스터에 저장된다.

 

그러므로 GetDriveTypeA 까지 실행시키고 그 다음의 EAX 값을 보기 위해

CALL GetDriveTypeA 함수 다음 라인에 BP(BreakPoint, F2)를 걸고 실행(Run, F9)시켜 보았다.

 

현재 CALL 이후 INC 명령어를 실행시키기 직전의 상황.

 

이 상태에서 EAX 값을 본다.

 

EAX = 00000003이다.

 

어셈블리 코드를 쭉 보면, 어디서 이 00000003이 CD-Rom이 아니라고 판별하는지 알 수 있다.

 

GetDriveTypeA 함수가 끝난 후,

 

INC ESI

DEC EAX

JMP 00401021 : 의미없는 코드

INC ESI

INC ESI

DEC EAX

CMP EAX, ESI

JE 0040103D

 

요약하면, ESI를 3번 +1 하고, EAX를 2번 -1 한다.

이후에 ESI와 EAX가 같으면(JE, Jump Equal)

0040103D("Ok, I really think that your HD is a CD-ROM! :P" - 성공 메시지)로 점프

 

즉, 우리가 CD-ROM으로 인식시키기 위해서는 CMP 위치에서 EAX와 ESI을 동일한 값으로 맞춰주면 된다.

 

그럼 다시, CMP 부분에 BP를 걸고 F9를 눌러서 BP 위치까지 실행을 시킨다.

현재 BP(CMP) 대기 중인 상황.

 

여기서 Register 값을 보자.

ESI는 3이고, EAX는 1이다.

하지만 이 값들은 ESI를 +3한 값이고, EAX를 -2한 값이다.

 

EAX와 ESI가 같아야 CD-Rom으로 인식되기 때문에,

ESI와 EAX는 현재 2만큼 값이 차이나고 있으므로 EAX에 +2를 해주어야 CMP 부분에서 3으로 같아지게 된다.

 

앞에서 GetDriveTypeA의 리턴 값이 EAX=3이었기 때문에

GetDriveTypeA의 리턴 값이 EAX=5가 된다면 프로그램은 C드라이브를 CD-Rom으로 인식할 것으로 추측할 수 있다.

저 부분에 BP를 걸고 F8로 step over한 후에,

 

EAX의 값을 5로 수정해 준다.

EAX를 더블클릭 하면 수정할 수 있음.

 

추가적으로, ESI 부분이 지금 00401000으로 잡혀있는데,

이 상태에서는 CMP EAX, ESI 부분이 우리가 원하는 상태로 제대로 실행되지 않는다.

그러므로 ESI도 0으로 바꿔준다.

최종적으로 변경된 레지스터 상태.

 

이후 실행해보면

정상적으로 C드라이브를 CD-Rom으로 인식하게 된다.

'보안 & 모의해킹 > CodeEngn' 카테고리의 다른 글

[CodeEngn] Basic RCE L04 풀이  (0) 2019.07.01
[CodeEngn] Basic RCE L03 풀이  (0) 2019.07.01
[CodeEngn] Basic RCE L02 풀이  (0) 2019.07.01
Posted by NDC :

*** Easy UnpackMe 리버싱하기 ***


2번 Easy Keygen은 패스하고... 이번엔 Reversing.kr 사이트의 3번 문제 Easy UnpackMe 문제를 풀어보겠습니다.


사실 이번 문제는 진정한 Unpack 을 한 것이 아니라, Olly Dbg 플러그인을 사용하여 언팩을 했습니다.


원래 언팩과정이라 하면,


"팩 과정을 찾아내서 그 부분에 BP를 걸고 디버깅을 한다."


로 볼 수 있지만, 저는 초보라 그 부분이 많이 어렵더라고요.


구글에서 검색한 결과,



1. PUSHAD 과정을 찾는다.

- 여기서 PUSHAD 란 PACKING 과정을 위해 모든 Register 들을 Stack 에 PUSH 하는 과정을 의미합니다.


2. 중간을 넘어서, POPAD 과정을 찾는다.

- 여기서 POPAD 란 PACKING 과정이 모두 끝난 후에,

OEP 위치 직전에서 프로그램 실행을 위해 다시 모든 Register 위치에 원래대로 값을 복원시키는 것을 말합니다.



특히 저 POPAD 부분은 EDI 레지스터를 중심으로 봐야 한다는데... 자세한건 더 공부해 보기로 하구요!


오늘은 Unpack 을 쉽게 해주는 Olly Dbg 의 플러그인을 소개하고, OEP(진짜 시작부분, Original Entry Point) 를 어떻게 찾는지 말씀드리겠습니다.



(주의 : 포스트 하단에 정답이 공개되어 있습니다!!!)


-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------


우선 첨부파일 OllyDump.dll 파일을 올려놓았습니다.


 

OllyDump.dll



위 dll 파일을 다운로드 하신 후에,


* OllyDbg 설치 경로 plugin 폴더(없을 경우 생성)


위치에 넣어주시면 됩니다.




들어간 후에...



plugin 폴더를 만들고...(저도 없어서 만들었습니다.)



해당 경로에 잘 넣어줍니다.



-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------



이렇게 플러그인을 넣으셨으면 Olly Dbg 를 들어가서 플러그인 설정을 해주어야 합니다.


디버거를 열어주세요.





들어가서 상단 툴바의 [Options] → [Appearance] 를 들어가주세요.





[Directories] 항목의 하단 [Plugin path:] 칸에 위의 OllyDump.dll 파일을 넣어 준 plugin 폴더의 경로를 넣어줍니다.





새로 입력되었으면, 디버거를 재시작하라는 창이 뜹니다.


[확인] 을 눌러서 창을 끈 후, 디버거를 재시작 해줍니다.





재시작을 하고, 상단에 [Plugins] → [OllyDump] 항목을 들어갈 수 있습니다. (1 Olly Advanced 는 제가 이전에 넣어둔 플러그인 입니다.)



-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------



자, 그러면 플러그인 세팅이 끝났습니다. 이제 이 툴을 가지고 Easy Unpack 문제를 풀어볼게요.



우선 exe 파일을 열어줍니다.





열었습니다. 우측 하단에 [Paused] 상태네요.


바로 플러그인으로 OEP 를 찾아보겠습니다.


상단에 [Plugins] → [OllyDump] → [Find OEP by Section Hop(Trace over)] 를 눌러줍시다.

(Trace into 는 한번에 OEP 까지 가지 않고 함수별로 Stop 됩니다. 분석에는 더 좋겠지만 문제를 풀기 위해서는 over로 한 번에 가는게 좋을 것 같네요.)





우측 하단에 Program entry point 에 속지마세요... 저건 Unpack 전의 EP입니다. OEP 가 아닙니다!





[Plugins] → [OllyDump] → [Find OEP by Section Hop(Trace over)] 를 누르게 되면


우측 하단에 [Paused] 상태에서 [Tracing] 상태로 변한 것을 확인할 수 있습니다.


이 상태로 기다립니다.(몇 초 만에 순식간에 끝나지 않습니다. 조금만 기다립시다.)





OEP 추적이 끝나면 저렇게 작업표시줄에서 깜박입니다.






우측 하단이 [Tracing] 에서 [Paused] 로 바뀌었고, 디버깅 위치가 자동으로 00401150 으로 세팅되어 있습니다.


OEP 추적이 끝난 상태이므로  00401150 이 OEP 라는것을 추측할 수 있습니다.



이 정보를 가지고 Readme.txt 파일을 읽어보면...





답은 저런 방식으로 입력하는 것인가 봅니다.


Reversing.kr 사이트에 가서...





찾았다고 추측하는 주소(00401150) 를 입력하고 좌측의 [AUTH] 를 입력합니다.





Easy Unpack 문제가 해결되었네요!



-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------



원래 정석대로라면 PACK 부분과 PACK 의 종료시점을 찾는게 진정한 Unpacker 이겠지만..


아직은 실력이 많이 모자르다고 생각되네요


조금 더 실력을 기르게 되면 Unpack 과정도 손쉽게 할 수 있지 않을까요?



그래도 Easy Crack 은 3956 분이 해결하셨는데 Easy Unpack 문제는 그의 절반 정도만 해결하셨네요.


아마 저처럼 툴 없이 하시려다가 못 푸신 분들이 다수 계실 것 같습니다.


저도 실력을 빨리 길러서 플러그인 없이 Unpack 하는 날이 오길 바랍니다 ㅎㅎㅎ



>>> Easy Unpack


>>> OllyDump.dll


>>> OEP


>>> Original Entry Point


'보안 & 모의해킹 > 리버싱' 카테고리의 다른 글

[Reversing.kr] - 1. Easy CrackMe  (0) 2018.07.11
Posted by NDC :

*** Easy CrackMe 리버싱하기 ***



처음으로 스스로 (구글의 도움없이) 리버싱을 해냈습니다.


물론 WinAPI 함수는 구글링을 했습니다.


처음부터 끝까지 레지스터의 스택포인터 따라가며, 헥사값을 보며, 동작의 의미를 알고,


그 느낌을 알면 아 이곳에는 이 값이겠구나 추론을 하고(물론 맞췄습니다.)


아무튼...


제가 지금까지 리버싱 공부하면서 수많은 블로그 글들을 참조하였듯이,


이 파일을 해결하지 못하신 분들도 제 블로그를 보시고 제가 따라온 루트를 밟고 꼭 푸시는데 도움이 되었으면 좋겠습니다!


(Easy_CrackMe.exe 파일의 출처는 Reversing.kr 입니다!)



(주의 : 포스트 하단에 정답이 공개되어 있습니다!!!)


-------------------------------------------------------------------------------------------------------------------------------------------------------------


일단 파일을 실행시켜 보겠습니다.



음...비밀번호를 입력해서 correct / wrong 을 맞추는 프로그램이라는 것을 추측할 수 있겠네요.


물론 그 비밀번호는 Flag 가 되겠구요.


일단 아무거나 쳐보겠습니다.



한 번에 맞추면 좋겠지만... 아쉽게도 틀렸네요.


하지만 여기서 큰 힌트를 얻을 수 있는데, 바로 문자열


"Incorrect Password"


입니다.


바로 올리디버그로 열어봅시다.



EP는 00401188이네요.


일단 처음에 생각할 수 있는 것은, 저 위에서 발견한 "Incorrect Password" 문자열을 출력하는 위치를 알아내는 것입니다.


[우클릭] > [Search for] > [All referenced text strings] 를 클릭하여 해당 문자열의 위치를 알아봐야 합니다.




위치는 0040113C 네요.


더블클릭해서 이동해봅시다.



저기 위쪽에 빨간배경(BreakPoint, BP), 브레이크포인트를 걸어놨습니다. 느낌 상 저기서 뭔가가 시작된다는 느낌이 왔습니다.


이 상태에서 F9을 누르면 저 위치까지만 실행됩니다.


저 BP는 풀지 않는 것이 좋습니다. 리버싱 하게 되면서 핵심 동작의 시작부분에 BP를 걸어두는 습관을 기릅시다.



그러면 실행파일이 뜹니다. 문자열을 입력받을 차례입니다.


아래에 보면 GetDlgItemTextA 함수가 있는데, 이는 WinAPI 함수로써, 구글에 어떤 함수인지 검색해 보았습니다.


하지만 느낌상...문자열을 입력받는 함수라는 것을 알 수 있죠?


Get + Dlg(=Dialog) + ItemText(=문자열) + A(=API)


즉, 대화상자에서 문자열을 Get 하는 API 라고 해석할 수 있습니다.




저기서 컨트롤이란...MFC에서 볼 수 있는 입력창(Edit Control)을 말하는 것 같네요.


아무튼 문자열을 입력받습니다.


어떤 동작인지 알았으니까 더 볼 필요 없겠죠? 일단 문자열을 입력하고 확인을 누릅니다.



이후에 F8로 쭉쭉 넘어갑니다.


GetDlgItemTextA 함수 아래까지 왔습니다. CMP 명령어로 어떤 두 가지를 검사하네요.


이후 ZF(제로 플래그)를 띄울지 안띄울지 판단 후, 이 ZF로 점프여부도 판단하겠네요.


왜냐면... 그 아래에 JNZ(Jump if Not Zero)가 있으니까요 ㅎㅎ


일단 두 가지가 무엇인지 볼게요.


하나는 ESP+5 의 주소의 값이고, 하나는 61입니다.


61은 딱 보니...아스키 값인 것 같네요. 61은 아스키 값으로 a를 의미합니다. (0x61 = 'a')


그럼 ESP+5의 위치에 무엇이 있는지 보겠습니다.


우선 현재의 ESP 값은



0019F6F0 입니다. 여기서 +5를 해주게되면 0019F6F5가 됩니다.



0019F6F0 의 줄에는 80 00 60 10 이 있고, 0019F6F4 줄에는 46 44 53 41 이 있네요. 그리고 그 아래 0019F6F8 줄에는 00 44 53 41 이 있습니다.


윈도우 운영체제는 Little Endian 방식을 사용합니다.


위의 값들은 실제로 이런 방식으로 들어가 있습니다.



61이 아스키 값이라고 추측해보면, 저 숫자들은 아스키 값이라는 것을 추측할 수 있습니다.


41 = A    /    53 = S    /    44 = D    /    46 = F

41 = A    /    53 = S    /    44 = D    /    00 = (NULL)


ASDFASD 가 나왔네요. 이는 저희가 저 위에서 입력한 값이네요.


즉, ESP+5 와 61을 비교한다는 것은, 두번째 자리가 a인지 아닌지를 검사한다는 것을 알 수 있습니다.


몇자리가 될 지는 모르겠지만, 하나는 알게됬습니다. 문자열의 두 번쨰 자리의 값은 'a'가 되어야 합니다.


a가 아닐 경우에는 00401135 주소로 점프하는데, 이는 Incorrect Password 문자열을 출력(틀렸음을 의미)하게 되는 함수가 있습니다.


=> @a@@@....


이번 문자열은 틀렸으니까 다시 Ctrl + F2 를 눌러서 재실행 시킨 후, 아무거나 입력합니다. 대신 두번째 자리는 a로 합니다.



대충 이렇게 입력했습니다.


다시 그 위치로 가볼까요.



두 번째 자리에 a를 입력했기 때문에 실패조건을 넘어갑니다.


이번엔 ASCII "5y"라는 힌트가 있네요.


왠지 모르겠지만 일단 2를 PUSH합니다.


그리고 LEA(MOV와 비슷한 명령어)를 하는데, ECX 레지스터에 ESP+A 위치의 값을 넣어줍니다.


현재 ESP는...



0019F98C네요.(스택포인터의 값은 실행마다 바뀝니다. 그때 항상 다르기때문에 체크하셔야 합니다.)


이 주소에 +A(10)을 해주면...


C는 12이므로, 22가 됩니다. 22는 16+6이므로,


0019F996이 되겠네요.


0019F996에 무슨값이 있나 봐야겠습니다.



끝에서부터 94 = 41 / 95 = 61(a) / 96 = 46 / 97 = 46


로 볼 수 있습니다. 즉, 0019F996 위치에는 46(='F')가 들어있습니다.


이는 저희가 입력한 문자열(AaFFFFF)에서 세번째 자리를 의미하겠네요.


아무튼 LEA명령어를 실행하면...



이렇게 주소값이 옮겨집니다. MOV는 값을 옮기지만 LEA는 주소값을 옮기는 것을 알 수 있습니다.


이 주소값은 ECX 레지스터로 이동합니다.


친절하게 저희가 입력한 문자열이 다 뜨네요.


다음 명령어는...



PUSH 406078 입니다.


4060XX 이 주소값의 영역은 바이너리 값이 들어있습니다. 바이너리 값은 올리디버그 좌측 하단에 있습니다.


볼까요?



00406078 위치에 35 가 있습니다. 35는 5이고 79는 'y'입니다.


저 헥사값 두자리가 1바이트입니다.(헥사값 1자리 = 4비트 => 0x0~0xF => 0b0000~0b1111)


PUSH 406078 명령어와 PUSH ECX 명령어를 실행했습니다.


PUSH 동작이므로 스택메모리를 봐야겠죠?




현재 스택 포인터(스택의 꼭대기)는 0019F984이고, 스택의 최상위 2개는 주소값이 있습니다.


맨 위의 주소는 유저가 입력한 문자열이 있는 스택메모리의 주소,


그 아래의 주소는 바이너리 값의 주소 입니다.


(참고로 스택 포인터는 스택을 쌓을수록 작아집니다. 스택은 아래에서 쌓는 것이 아니라 꼭대기에서 내려옵니다. 마치 종유석과 같습니다.)


왜 저 문자열을 주석으로 달아놨는지는 잘 모르겠습니다. 왜 1Byte 값만 가져오지 않을까요?


아무튼, 이 두 주소를 PUSH 한 다음, CALL 00401150 을 실행합니다.


이로써, PUSH 한 두 주소값은 함수의 파라미터라고 볼 수 있습니다.


대충 이런 함수겠네요.


func_00401150(addr& binary_addr, addr& user_input_addr)


이 함수는 그냥 넘깁시다. F8을 눌러서 Step Over 를 실행합니다.



함수 내부 동작에서 EAX 를 1로 만들었고, ECX 를 1로 만들었습니다.



다음 명령어를 보니, 또 다시 실패 창을 띄우는 명령어가 다가오네요.


일단 ADD ESP, 0C 를 합니다.



ESP 값이 0019F984 에서 C값이 더해진 0019F990 으로 바꼈습니다.


다음 명령어를 봅시다. TEST EAX, EAX 입니다.

TEST 명령어는 CMP 명령어와 다르게 AND 연산을 합니다.(CMP 명령어는 SUB연산입니다!)

즉 EAX, EAX 를 AND 연산을 하는데... 결과가 0이면 ZF가 1로 set 될 것입니다.

JNZ는 Jump if Not Zero, 즉 연산의 결과 값이 0이 아니면, 다시 말해 ZF가 0이면 점프를 합니다.

안타깝게도 EAX 가 1이므로 EAX끼리 AND를 시키면 1이 되고, 이는 ZF가 0으로 유지됩니다.

그러면 JNZ 명령어로 실패 창으로 가게 됩니다.

실행해볼게요.


안타깝게도 실패했습니다.

하지만 리버싱의 느낌으로, 왠지 5y가 들어가야 할 것 같습니다.

406078 부분에 5y 앞뒤로 NULL 값으로 도배가 되어 있기 때문에, 3번째 자리와 4번째 자리에 5y를 넣어야 할 것 같습니다.

그러면 00401135 함수는 이 두 문자열을 검사하는 함수일 것 같네요.

함수 결과가 EAX = 1로 셋 되었는데, 만약 입력값과 5y가 같으면 EAX를 0으로 클리어하고 ZF를 1로 셋 할 것 같습니다.

Ctrl + F2 를 눌러 재시작을 하고, Xa5yXXX...를 입력해봅시다.


이번엔 Aa5yFFFF를 입력했습니다.


네 성공적으로 4010CD(실패 창 이동) 을 넘었습니다.

이로써 두번째 자리는 a / 세번째 자리는 5 / 네번째 자리는 y 가 와야 함을 알 수 있습니다.

이번엔 PUSH EBX, PUSH ESI 동작이 있네요. 두개 다 실행하겠습니다.


두개 전부 스택에 올라갔습니다. (EBX = 0x1, ESI = 0xF0772)

다음 명령어를 볼까요?


4060XX 영역은 바이너리 영역이므로, 이 40606C 에서 NULL 까지 문자열을 봐야겠습니다.

일단 ESI 에는 40606C라는 값이 들어갈 것입니다.


(바뀐 ESI, 친절하게 문자열을 알려줍니다.)


0040606C(=0x52) 부터 NULL 이전까지의 문자열 ("R3versing") 을 확인할 수 있습니다.

다음 명령어는...


LEA 명령어입니다. 주소값을 옮기는 명령어입니다.

ESP+10의 위치네요. 현재 ESP의 값은


0019F6E8 입니다. 여기에 +10을 하면

0019F6F8 입니다. 명령어를 실행해 볼게요.


EAX 레지스터에 0019F6F8 주소가 옮겨졌습니다. 주석으로 ASCII "FFFF"라고 되어있네요.

왠지...FFFF랑 R3versing 이랑 같아야 할 것 같습니다.

확인해 볼게요.

다음 명령어는


하나의 Routine 으로 묶여있습니다.

=====================================================================
처음에 EAX 의 주소값(EAX 레지스터에 담겨져 있는 주소값) 위치의 값을 DL(EDX 레지스터의 하위 8비트) 에 옮깁니다.

그리고 ESI 의 주소값 위치의 값을 BL(EBX 레지스터의 하위 8비트) 에 옮깁니다.

그리고 DL의 값은 다시 CL로 옮기고, BL과 DL을 비교합니다.

CMP 명령어 이기 때문에, DL과 BL이 같으면 결과 값이 0이 될 것이고, 그러면 ZF는 1이 될 것입니다.

만약 두 값이 같지 않으면(ZF가 0이면) 00401102 로 점프합니다. (00401102 위치에는 EAX 를 -1로 만들어서 실패하게 만드는 아주 무서운 과정이 들어있습니다.)

위의 과정을 통과했으면, CL을 TEST 연산 합니다.

이는 CL이 0인지 아닌지 검사합니다. 이 말은, DL 이 NULL인지를 검사하는 것입니다. 다시 말해서 값의 여부를 판단, 즉 문자열의 끝인지를 판단 후 저 Routine 을 빠져나갑니다.

이로써 저 Routine 은 반복문이고, TEST CL, CL 은 break 조건임을 알 수 있습니다.

그 다음, EAX+1(1바이트를 의미합니다.) 위치의 값을 DL에 넣고, ESI+1 위치의 값을 BL에 넣고, 또 다시 비교를 합니다.

다르면? 00401102(실패 창) 로 점프합니다.

그리고 EAX와 ESI 에 2씩 더해줍니다. 이유는 문자(character)를 검사했는데, 2개를 검사했기 때문입니다.

2바이트를 더해주어야 그다음에 또 다시 2바이트를 검사할 수 있습니다.

그리고 TEST CL, CL 을 통해 문자열의 끝을 검사합니다.

CL 이 0이 아니면, 다시 말해 끝이 아니면 루틴의 처음(004010DA)으로 돌아갑니다.
=====================================================================

위의 과정의 루틴입니다. 당연히 실패 창으로 넘어갑니다.

왜냐하면 저희가 입력한 문자열 FFFFF는 R3versing 과 다르기 때문입니다.

이로써, a5y 다음에는 R3versing 을 입력해야 함을 알 수 있습니다.

그래도 실행은 해볼게요.


실패 창으로 가기 직전입니다.


레지스터 창입니다.

DL에는 46(='F')이 들어있고, BL에는 52(='R')가 들어있고, CL에는 DL과 같이 46이 들어있습니다.

46은 52와 다르기 때문에, JNZ 조건으로 실패 창으로 갑니다.

그러면 Ctrl + F2 를 누르고 재시작을 한 후, 이번에는


Aa5yR3versing 을 입력해보겠습니다.


해당 루틴을 5번 돈 후, 무사히 실패없이 통과했습니다.

다음 명령어는 XOR EAX, EAX 입니다.

리버싱에서 XOR EAX, EAX 는 EAX = 0 으로 만들겠다는 의미입니다.

같은 값을 Exclusive OR 하게 되면 모든 비트가 같기 때문에 전부 0이 되기 때문입니다.


예상대로 EAX가 0이 됬습니다.


다음 명령어는 JMP 00401107입니다.



EAX 를 -1로 만들어버리는 무서운 과정이 생략됬군요.


참고로 SBB는 캐리플래그도 연산에 포함시킵니다.


SUB 가 dest = dest - src 라면


SBB 는 dest = dest - (src - carry flag) 입니다.


아무튼 생략되었습니다. 이유는 아래에서 말하겠습니다.


바로 POP ESI, POP EBX를 해줍니다.



ESI에는 002D0094 라는 값이 들어갔고, EBX에는 00000001 값이 들어갔습니다.


TEST EAX, EAX 를 해줍니다.


그 아래에는



JNZ (Jump if Not Zero) 명령어가 있습니다.


이 뜻은, ZF가 0이면, 즉 EAX 가 0이 아니면 00401135(실패 창)으로 갑니다.



ZF가 1이네요.  실패하지 않습니다.


만약 SBB 과정을 거치게 되면, 이전에 TEST CL, CL과정에서 ZF가 1로 셋 되지 않고 ZF가 0으로 유지되기 때문에 실패합니다.


그 다음 명령어는



0x45(='E') 와 ESP+4위치의 값과 비교하는 것입니다.


현재 ESP는



0019F990입니다.


여기에 4를 더하면 0019F994입니다.



0x41(='A')입니다.


다르네요. A는 저희가 첫번째 자리에 입력한 문자입니다.


즉, 마지막으로 첫번째 자리에 0x45, 문자 'E'를 입력해야 함을 알아냈습니다.



실행시키면 역시 실패 창으로 갑니다.


다시 재시작 한 후, 이번에는 답으로 추정되는 Ea5yR3versing 을 입력해보겠습니다.



위의 과정을 전부 거친 후 문자열을 검사하고. 성공적으로 정답 창으로 왔습니다.


프로그램은 MessageBoxA에 정답이라는 것을 알려준 후 EndDialog를 통해 대화상자를 종료할 것으로 추측할 수 있습니다.



맞췄습니다.


정답은...


Ea5yR3versing


이었습니다.


영어로 EaSyREversing 이네요 ㅎㅎㅎ 리버싱 쉽대요


-------------------------------------------------------------------------------------------------------------------------------------------------------------


파일명 부터가 Easy_CrackMe.exe 여서 그런지 리버싱하는데 나름 쉬웠습니다.


처음으로 자력으로 해결한 프로그램이어서 더더욱 자세하게 설명한 것 같네요.


해결 못하신 분들은 제가 한 것처럼 따라서 해보시면 될 것 같습니다.


감사합니다.


>>> Reversing.kr


>>> Easy_CrackMe



'보안 & 모의해킹 > 리버싱' 카테고리의 다른 글

[Reversing.kr] - 3. Easy UnpackMe  (0) 2018.07.12
Posted by NDC :

* gdb를 사용한 실행파일 디버깅하기 *




보통 보안이나 해킹에서 보면 실행파일을 까 보는? 경우가 있습니다. 물론 제가 아직 거기까지 통달하지는 않았지만


역시나 오늘도 기록을 위해서 포스트를 작성해 보도록 하겠습니다.



일단 C언어를 사용한 코드를 하나 작성해 보겠습니다.



> touch shell.c


> nano shell.c



(source)

#include <stdio.h>


int main() {

int a = 10;

int b = 20;

int result = 0;


result = a + b;


return 0;

}



사실상 stdio.h 헤더는 사용하지 않았지만 버릇처럼 넣어버렸습니다...


아주 간단한 코드입니다.


a 에 10을 넣고 b 에 20을 넣고 그 값을 result 라는 4바이트짜리 변수에 넣고 종료


이것을 이제 gcc 를 통하여 실행파일로 컴파일 해보겠습니다.



> gcc -o shell shell.c


-o 옵션은 컴파일한 출력 파일의 이름을 지정해주는 옵션입니다.


단순히 gcc shell.c 로도 할 수 있지만, 이 결과는 a.out 리는 실행파일이 나오게 됩니다.


출력문 printf 를 쓰지 않았기 때문에 당연히 아무 반응도 없습니다.


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


이것을 이제 gdb(GNU Debugger) 를 통해 디버깅을 해서, 어셈블리 코드로 바꿔서 결과를 보겠습니다.




> gdb shell


(gdb) > disass main


disass main 의 의미는 disassemble main 의미입니다.


즉 main 메소드를 역어셈블 한다는 뜻이죠.


한줄한줄 살펴볼까요?



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



1. <+0>:    push   %rbp


이 1번줄과 2번줄은 어셈블리에서 항상 나오는 표현입니다.


레지스터 rbp를 스택에 넣어두고, rsp를 rbp에 넣어줍니다.


참고로 윈도우와는 다르게 리눅스에서는 source 와 destination 의 위치가 바뀝니다.



즉, add %r1, %r2 가 윈도우에서는


r1 = r1 + r2


이었다면, 리눅스에서는


r2 = r2 + r1


이 됩니다. 헷갈리실 수도 있으니 꼭 참고하세요.




2. <+1>:    mov    %rsp,%rbp


네, 아까 말씀드린 것처럼 어셈블리에서 항상 나오는 표현입니다.


1번줄과 2번줄은 외워두시는 것도 좋습니다. 저는 이미 외웠습니다.




3. <+4>:    movl   $0xa,-0xc(%rbp)


본격적인 main함수의 표현입니다.


movl $0xa, -0xc(%rbp)


언뜻 봐도 0xa(=dec. 10) 의 값을 %rbp 에 넣어주는 표현인 것 같습니다.


그런데 왜 -0xc 를 해줄까요? 분명히 int 는 4바이트(=0x4)인데?




어셈블리어 익숙하신 분들은 눈치채셨을 수도 있습니다.


저는 ARM M1 Cortex 시리즈를 학교에서 공부하면서 ARM 어셈블리 문법을 공부했었는데,


거기서 배운 것 하나가 여기서 도움이 되네요.



역어셈블 과정에서 디버거는 변수의 개수를 셉니다.


main 함수를 보시면 int 변수가


int a

int b

int result


이렇게 3개가 나옵니다.


즉, 4바이트 * 3 = 12바이트 (=hex. 0xc)


라는 공간을 "미리" 할당합니다.




아래 그림을 한번 봅시다. (byte address 로 하지말고 4byte 어드레스라고 가정합니다. 즉 1칸에 4바이트)


               [rbp]

4byte

4byte

4byte

 

 

 

 



이렇게 3칸(12byte)을 할당해 줍니다.


그리고 나서, 변수가 나오는 순서에 따라서 맨 아래서부터 메모리 위치를 배정해줍니다.


int a = 10;


즉, 10의 값(=hex. 0xa) 을 맨 아래인 rbp - 12byte 위치에 넣어줍니다.


이것을 어셈블리로 표현하면


movl    $0xa, -c(%rbp)


가 되는 것입니다.




4. <+11>:    movl   $0x14,-0x8(%rbp)


위의 3번줄에서 설명한 것 처럼, 다음 변수는 -12 바이트 위치가 아닌,


4바이트가 줄어든 -8 바이트 위치에 할당해줍니다.


다음 변수는


int b = 20;


입니다. 즉, 20의 값을 -0x8(%rbp) (=%rbp - 8byte) 에 넣어줍니다.


20은 hexa 값으로 0x14 입니다. (1 * 16 + 4 * 1)




5. <+18>:    movl   $0x0,-0x4(%rbp)


똑같습니다. 마지막 변수는 int result = 0; 이므로,


0의 값을 -4바이트 줄어든 위치에 넣어줍니다.




6. <+25>:    mov    -0xc(%rbp),%edx


이 것은 계산을 위해서 값을 옮기는 과정입니다.


계산 식은


result = a + b;


해당 인수들의 주소는


&a = -0xc(%rbp)


&b = -0x8(%rbp)




여기서는 %rbp - 0xc 위치에 있는 값(=10)을 edx 레지스터에 넣어주었습니다.




rbp 에서 직접적으로 계산을 하지 않고 edx 와 eax 같은 범용 레지스터에 임시적으로 옮겨놓고


계산을 한 후 그 결과 값을 다시 rbp에 가져오게 됩니다.


쉽게 생각해서, 값을 보존하기 위해서? 라고 생각하시면 되겠습니다.




ARM 어셈블리를 배울 때도, 메모리에서 직접적으로 계산을 할 수가 없기 때문에


값들은 메모리에 넣어 놓고 필요한 값만 r1, r2, r3 등의 범용 레지스터에 옮겨서 계산 후 다시 메모리에 넣었던 기억이 납니다.




7. <+28>:    mov    -0x8(%rbp),%eax


6번줄과 마찬가지로 rbp -8byte 의 위치에 있는 값(=20)을 eax 레지스터에 넣어줍니다.


8. <+31>:    add    %edx,%eax


계산입니다.


eax = eax + edx


eax 에는 20이 들어가 있었고, edx 에는 10이 들어가 있었죠.


최종적으로


eax = 20 + 10


과 동일합니다.


eax 에 결과 값 30이 들어가게 되겠군요.




9. <+33>:    mov    %eax,-0x4(%rbp)


계산이 끝났으니 값을 옮겨줘야겠죠?


eax 레지스터에 있는 값을 -0x4(%rbp), 즉 %rbp - 4byte 위치에 넣어줍니다.


저희는 이미 위의 과정들을 통하여 어떤 변수(값)가 어느 위치에 있는지 알고 있습니다.


rbp에서 4byte 줄어든 위치는 result 변수가 할당되어 있는 자리입니다. (5번줄 참고)


즉, result 변수에 eax 레지스터의 값, 30이 들어가게 됩니다.




10. <+36>:    mov    $0x0,%eax


사용한 도구는 닦아줘야죠.


eax 레지스터를 초기화 해줍시다. 다음에 있을 연산이나 작업을 위해서요.


항상 초기화는 습관을 들여줍시다.




11. <+41>:    pop    %rbp


모든 프로그램이 끝났습니다. 1번줄에서 stack memory 에 save 해 두었던 값을 rbp 에 다시 옮겨줍시다.


12. <+42>:    retq  


return 0; 의 의미입니다.





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




굉장히 쉬운 어셈블리 였습니다. 실제로 보안업무를 진행하시는 분들은 몇 백 줄의 C언어 코드를 역어셈블 해서 보실텐데, 이런 코드는 정말 맛보기일 뿐입니다.


그래도 gdb 를 사용하고, 역어셈블과 어셈블리 언어를 관찰했다는 것에 의의를 두었습니다.



>>> gdb


>>> 리눅스 어셈블리


>>> 리눅스 역어셈블


>>> GNU Debugger

Posted by NDC :