Cha4SEr Security Study

[레나 튜토리얼] - 1. 라이센스 루틴 지나가기 본문

Reversing/레나 튜토리얼

[레나 튜토리얼] - 1. 라이센스 루틴 지나가기

Cha4SEr 2020. 4. 19. 23:43

실습준비가 끝났으면 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입니다.

이 부분은 원래 점프하면 안되는 부분이기 때문에 아무런 명령을 실행하지 않은 것으로 바꿔줘야 합니다.

 

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

요로케

 

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

 

 

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

 

 

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

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

 

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

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

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

 

이제 1번은 다 풀었습니다! 올리디버거 사용법까지 같이 정리하다보니 내용이 길어졌네요 

2번부터는 아마 금방금방 하지 않을까 싶습니다..ㅠ

Comments