보안 공부하는 꼬깔이

- ImagePrc

 

ImagePrc를 클릭해 문제를 다운받자.

 

[ Exeinfo Pe ]

패킷여부 : Not packed

제작언어 : C++

 

 

문제 파일을 실행 시켜보면 흰 백그라운드의 창하나와 CHECK 버튼으로 구성된 창이 하나 뜬다.

흰 백그라운드의 창에는 마우스로 클릭하고 움직이면 사진과 같이 줄이 그어진다.

 

Check 버튼을 클릭하면 해당 시도가 틀렸을때 나오는 문구가 출력된다.

제출자가 원하는 그림을 그려서 제출하는 문제인가...?

 

해당 프로그램에서 사용되는 라이브러리 목록이다.

그래픽과 GUI에 관련된 작업들을 할때 사용하는 라이브러리들이 무수히 존재한다.

이번 문제에서도 역시 실패 했을때 출력되는 문구인 "Wrong" 을 찾아 분석을 시작해보자

 

Wrong 을 출력하는 어샘코드 부분이다.

004013AA 주소의 분기문으로 인해 "Wrong" 이 출력되는 코드로 분기하는 모양이다.

그렇다면 분기문을 만나기전 비교를 하는 구문이 있을것이다.

위 사진을 보면 실제로 레지스터에서 어떠한 작업이 이루어지고 그것을 CMP 레지스터로 비교하는 부분이 존재한다.

 

더욱 위로 올라오면 FindResourceA, LoadResource , LockResource 등의 함수들이 사용되는 것을 확인 할 수 있다.

해당부분은 이러한 함수들로인해 리소스를 찾고 그 리소스가 존재하면 Load를 하게끔 코드가 구성되어있다.

 

FindResourceA 함수를 call 하는 부분에 브레이크 포인트(F2)를 걸고 프로그램을 시작한 뒤 FindResourceA 함수를 사용하고 난 뒤 결과값을 확인했더니 리소스가 저장되어 있는 주소의 위치가 0x47E048 주소 라는것을 알게 되었다. 

그리고 해당 리소스를 LOAD해서 EAX에 저장시킨다.

 

리소스를 찾고 로드하는 함수들의 과정이 끝나면 해당 코드가 수행된다.

ESI 레지스터에는 사용자가 그린 그림의 영역 주소가 저장되고 EAX에는 리소스 영역의 주소가 저장된다.

결국 해당 코드는 사용자가 입력한 그림과 리소스 여역의 그림을 픽셀 단위로 0x15F90 번 만큼 반복하면서 검사를 진행한다.

검사도중 일치하지 않는 부분이 발견되면 그 즉시 "Wrong!" 문구가 출력되는 쪽으로 분기를 시켜버린다.

반대로 검사하는 모든 리소스가 일치하여 반복문이 끝나면 특정한 영역을 CALL 하게 된다.

 

그렇다면 어떤식으로 프로그램에 저장되어 있는 리소스를 알 수 있을까?

 

프로그램안에 이미지에 해당하는 데이터가 존재하면 그것을 MainFest에 저장하게 된다.

실제로 PEview를 통해 imageprc.exe 파일의 ManiFest를 확인해보니 리소스가 존재했다.

그리고 해당 리소스를 확인했을떄 256의 색을 FF이하로 표현을 하며 비트맵 방식을 사용하는 BMP 이미지 파일로 유추를 하였다.

 

해당 리소스를 resource_hacker 툴을 사용해 파싱한 후 해당 내용을 Notepad++ 에 복사 후 저장했다.

현재 리소스에는 해더가 존재하지 않기 때문에 우리가 임의로 BMP 해더를 넣어주어야 한다.

 

그리고 리소스의 본래 사이즈를 코드상에서 알아내었다. 가로의 크기는 0x96(200) 세로의 크기는 0xC8(150) 이다

 

그림판에서 가로 200, 세로 150의 크기를 가진 임의의 파일을 하나 만들고 해당 파일을 HXD 툴을 이용해 파싱한 리소스에 BMP 해더를 붙여 저장하게 되면 어떤 문구가 적힌 BMP 파일이 출력되는데 해당파일이 우리가 입력한 그림과 비교를 하던 리소스 파일이며 이 문제의 정답이다.

 

 

ImagePrc clear!

 

 

 

 

 

 

 

 

 

- Replace

 

 

Replace 문제를 클릭해 다운받자!

 

[ Exeinfo Pe ]

패킹여부 : Not packed

제작언어 : C++

 

 

문제파일인 Replace.exe 파일을 실행하면 위 사진과 같은 사용자가 입력가능한 텍스트 박스와 그 값을 넘겨주는 버튼이 존재하고 Wrong 이라는 문구가 출력되어있다.

텍스트 박스는 오직 숫자만을 입력하게끔 되어있다.

 

임의의 숫자를 입력하고 Check 버튼을 클릭하면 작동이 중지되었다는 문구가 출력되고 프로그램이 종료된다.

현재 PC의 문제라고 생각되어 다른 PC에서 테스트 해보았는데 똑같은 현상이 나타났다.

이런 현상부터 문제의 시작이 아닌가하여 디버깅을 해보니 문제의 의도였다!

왜 이런 오류가 뜨는지 디버깅을 통해 알아보도록 하자

 

프로그램을 실행시키고 임의의 숫자를 입력 후 0040466F 주소를 보면 0x90(NOP)를 EAX에 저장하는데 여기서 문제가 발생한다.

 

현재 EAX에는 "601605CC" 라는 주소가 저장되어 있는데 해당 주소는 접근이 불가능한 주소이다.

접근이 불가능한 주소에 0x90 이라는 데이터를 저장하려고 시도하니 에러가 출력되는 것이다.

프로그램이 오류를 출력하고 뻗어버리는 이유를 알았으니 본격적인 분석을 시작해보도록 하자 .

 

프로그램 시작시 사용자가 임의의 숫자를 입력할 수 있는 부분까지 정상적으로 동작하는 것을 생각해보면 해당 기능을 수행하는 함수 역시 정상 동작을 한다는 것이다. 그렇다면 해당 기능을 하는 함수를 기준으로 분석을 해보도록 하자.

현재 프로그램에서 사용하고있는 함수 목록을 출력해주는 기능을 사용하자. (Ctrl + N)

위 사진을 보면 GetDigItemInt 함수가 보일것이다. 해당 함수는 입력된 정수형 데이터를 얻는 함수이다.

아무래도 DialogBoxParamA 함수가 사용자 입력 창을 띄워주고 사용자가 값을 입력하면 GetDigltemlnt 함수가 입력된 정수형 데이터를 가져오는 구조인듯 하다.

 

GetDigltmelnt 함수의 CALL 부분이 위치한 0040105A주소에 브레이크 포인트(F2)를 걸고 실행시켜보자.

 

해당 함수는 사용자가 입력한 숫자를 16진수로 변환해 EAX에 저장하는 모양이다.

위 사진을 보면 내가 입력한 "1234"를 "0x4D2"로 변환해 저장한 것을 알 수 있다.

함수 호출이 끝나면 EAX에 저장된 값을 4084D0 주소에 저장한다. 그 다음 0040466F 주소를 CALL 하는데 이 주소는 처음에 봤던 접근이 불가능한 주소에 NOP 주소를 저장시키려고 시도하여 오류가 출력되던 부분이다.

0040466F 주소를 CALL 해서 어떤 과정을 진행하는지 확인하기 위해 해당 주소로 진입(F7)해보자

0040466F 주소에 진입하면 위 사진의 코드가 출력되는데 0040466F 부분을 보면 0040467A 주소를 CALL 한다.

0040467A 주소는 어떤 과정을 진행하는지 살펴보기위해 주소로 진입해보자.

어샘 코드를 분석해보면 0x619060EB의 값을 406016 주소에 넣고 00404689 주소를 호출하는데 이 주소는 04084D0 주소의 DWORD 값을 1만큼 증가시키는 동작을 수행한다. 04084D0 에는 처음에 내가 입력한 "1234" 가 16진수로 변환되 저장된 값인 "0x4D2" 이 저장되있으며 1만큼 증가해 04084D0에는 "0x4D3" 이 저장된다.

그리고 Retn을 하는데 00404684 주소에서 00404689 주소를 CALL 했기때문에 00404689 주소로 돌아가 4084D0에 저장되어 있는 "0x4D3" 에 한번더 1만큼 증가시켜준다. 그러므로 4084D0에 저장되는 값은 "0x4D4" 가 되며 다시 RETN을 수행하게 된다.

두 번째로 리턴되는 주소는 0X404674 이다.

 

404674 주소의 코드를 확인하면 4084D0 주소에 601605C7을 저장한다.  이렇게 되면 4084D0 주소에 저장되어 있는 "0x4D4"      에 "601605C7" 가 합해진 "60160A9B" 이 주소가 사용자 값이 위치한 주소가 되는것이다.

그리고 00404684 주소를 보면 아까 4084D0 주소에 저장된 값에 2를 더해준 00404689 주소를 호출하는것을 확인할 수 있는데 이렇게 되면 다시한번 4084D0 주소에 저장된 값에 2를 더해 "60160A9D" 주소가 4084D0 주소에 저장 되게 된다.

하지만 전과 달리 RETN 되는 위치는 CALL STACK 상의 0x40106A 주소로 리턴된다.

0040106A 주소에서는 EAX를 초기화 하고 404690 주소로 분기한다.

 

분기한 404690 주소를 보면 사용자 입력값이 저장된 주소를 저장하고 있는 4084D0 주소의 값을 DWORD 크기 만큼 EAX에 넣는다.

그리고 4084D0에 2를 INC 시켜주는 주소인 00404689를 한번 더 호출한다. 그래서 4084D0에 저장된 주소는 "60160A9F" 가 된다.

404689 주소로 인해 40469F 주소로 리턴하면 0xC39000C6 값을 40466F 주소에 넣은 후 40466F 주소를 호출한다.

40466F 주소는 이전에 접근 불가능한 주소에 NOP를 저장하려해 문제가 발생한 영역이다. 처음 디버깅에서는 분명 문제가 없었는데 현재는 이러한 오류가 발생했다. 이유는 40466F 의 코드가 40469F 주소의 코드에 의해 변경되었고 EAX에 4084D0 주소의 값을 넣는 코드를 실행 하는 과정에서 해당 코드를 주소로 삼아서 0x90(NOP)를 넣으려 하니 처음에 봤던 오류가 발생하게 되는 것이였다.

만약에 EAX에 정상적인 주소가 들어있는 상태라면 그 다음은 어떤동작을 수행할까?

아마 그 주소에 NOP를 생성할 것이다.

 

그리고 464672 주소의 RETN 명령어가 실행되는데 위 사진의 스택상태를 보면 리턴할 주소는 4046AE 주소이다.

 

4046AE 주소를 확인하면 정상적인 주소가 들어있다고 가정한 EAX에 1만큼 증가를 시키며 그 주소가 EAX에 저장될 것이다.

그리고 40466F 주소를 호출하는데 해당 주소로 인해 또 다시 0x90(NOP)를 저장하는 코드로 넘어가게 되며 NOP를 입력후 4046B4주소로 RETN 하게 된다.

4046B4 주소에서는 40466F 주소에 0x6E8을 저장한다.

그리고 4046BE 주소에서 POP EAX가 실행되여 남아 있던 RETN 주소인 40469F 주소가 정리되고 4046C4 주소의 분기문으로 인해 401071 주소로 분기한다.

 

분기한 401071 주소를 보면 401084 주소로 분기 시킨다. 그리고 40107E 주소를 확인하면 SetDigitemTextA 함수가 사용되며 401073 주소에는 Correct! 라는 성공을 의미하는 문구가 존재한다.

이렇게 되면 현재 이 함수는 다이얼로그 박스에 표시된 Wrong 를 Correct로 변경시켜주는 역할을 한다는 것을 알 수 있다.

그런데 401071 주소의 분기문으로 인해 "Correct!" 구문을 출력해주는 setDlgItemTextA 함수를 지나치게 된다. 결론적으로 401071주소의 코드를 우회할 방법을 강구하면 문제를 해결할 수 있을 것이다.

어떤 방법을 이용하면 우회가 가능할까 한참 고민하다가 401071 주소의 코드가 "EB 11" 2byte 크기의 코드라는 것을 확인했고 동시에 0x90(NOP) 값을 두 번 저장했던것이 생각났다. 그 0X90(NOP) 코드가 401071,401072 주소에 입력된다면 기존에 있던 분기문코드는 덮어 씌워지게 되고 SetDIGitemTextA 함수가 실행되어 "Correct!" 문구가 출력될 것이다.

[ 정리 ]

처음에 접근이 불가능한 주소에 0x90(NOP)를 저장하려해 오류가 발생한 부분은 40466F 주소에서 EAX 에 401071 주소가 저장된다면 해당 주소의 2Byte 코드가 0x90(NOP)가 되는데, EAX는 사용자가 입력했던 값이 저장되는 주소인 4084D0 주소의 값에 해당함으로 코드상의 연산을 끝내고 난 후 값이 401071 주소가 저장되는 어떠한 수를 넣으면 분기문의 코드가 NOP로 덮어 씌워 질것이고 문제가 풀릴것이다.

계산식 : FFFFFFFF - 601605CB(601605C7+inc+inc+inc+inc) + 401071 = "정답(16진수로 계산하고 10진수로 AUTH창에 넘겨야 함)"

 

 

Replace Clear!

 

 

- Easy Unpack


Easy Unpack 문제를 클릭하여 다운받도록 하자.

문제를 다운받고 압축을 푸면 Easy_Unpack.exe 파일과 함께 ReadMe.txt 파일이 존재했다.


ReadMe.txt 파일안에는 OEP를 찾으라는 문구가 적혀 있었다.

Pack 되어 있는 파일들은 프로그램 시작주소가 이상한 곳을 가르킨다.

이런 상태에서 디버깅을 진행하다 보면 코드가 Unpack 되어 보여지는 순간이 있는데 이 때의 프로그램 주소들을 확인하면 정상적으로 분석이 가능하며, 이 때 디버깅을 시도 했을 때의 시작 주소가 "OEP" 이다.


프로그램을 실행하면 흰 배경의 창하나를 출력하는 것 이외에는 아무런 동작을 하지 않는다.


ollydbg로 디버깅을 시도 해보면 패킹이 되어 있기 때문에 엔트리 포인트가 정상적이지 않다.

그리고 무수히 많은 분기문과 반복을 통해 의미없는 값들을 저장한다.


무수히 존재하는 분기문의 마지막 분기점을 확인하면 "OEP" 주소를 확인 할 수 있다.

해당 부분에 브레이크 포인트(F2) 걸고 프로그램을 시작시켜 OEP 주소를 찾아내는 Manual Unpacking 방식을 사용해보자.


마지막 분기점의 OEP주소에서 다시한번 프로그램을 실행하면 이렇게 unpaking 되어 Define Double word 를 확인 할 수 있다. 여기서 ctrl+A 단축키를 이용하면 code Analyze 기능을 통해 코드를 확인 할 수 있다.


Unpack 된 코드가 출력되었다.


[ 정답 ]

OEP 주소



Easy Unpack Clear!




- Easy KeygenMe



Easy Keygen 을 클릭하여 문제를 다운받자.

문제파일 폴더를 확인해보면 Easy_keygen.exe 파일과 함께 ReadMe.txt 텍스트 파일이 존재한다.


해석해보면 시리얼이 "5B134977135E7D13" 로 나오는 NAME을 찾으라고 되어있다.


프로그램을 실행시켜보면 Name과 Serial을 입력받는다. 

Name에 임의의 값을 입력하고 Serial 역시 임의의 값을 입력해 보았는데 Wrong! 이라는 문자와 함께 프로그램이 종료가 된다. 

이것을 통해 ReadMe.txt의 내용이 의미하는 것이 "5B134977135E7D13" 시리얼을 나타내는 NAME을 입력하라는 것이 아닌가 하고 유추해보았다. 


본격적인 풀이로 들어가자.


우선 패킹의 유무와 어떤 언어로 프로그램이 제작되었는지를 확인해보자


[ Exeinfo PE 툴 결과 ]

패킹 : Not Packed

언어 : C++


ollydbg 로 easy_keygen.exe 파일을 디버깅 해보자.

우클릭 -> search for -> all referenced string 을 클릭하게되면 해당 프로그램에서 사용된 string을 출력시켜 준다.

해당 기능을 통해 우리는 처음에 확인한 Name과 Serial을 입력했을때 출력된 "wrong!" 문구를 확인 할 수 있었다.

그리고 string중에 눈에띄는 문구가

 있다. 바로 "Correct!" 사용자가 입력한 Name과 해당 Name에 의해 도출된 Serial이 같을때 저 문구가 뜨지 않을까 하는 생각이 들었다.


해당 위치로 이동하니 00401118 주소의 분기문을 통해 조건의 참 거짓을 판단하는 코드가 보였다.

이것을 통해 우리는 해당 분기문을 지나기 전 코드의 실행 과정에 의해 프로그램의 결과가 결정된다는 것을 알 수 있다.


Input Name을 입력받는 곳을 확인해보자.


"0040105E" 주소 부분을 확인해 보면 사용자가 입력한 문자열이 존재하는 주소를 EDI에 넣는 것을 확인 할 수 있다.


stack address=0019FE18 주소를 시작으로 내가 입력한 "aaaaa" 문자가 저장되어 있다.


해당 어샘 코드는 레지스터들을 XOR 연산을 통해 초기화 하고 0040106E 부분에서 SCAS 명령어를 통해 문자의 길이를 구한다.

실제로 내가 입력한 값은 "aaaaa" 5글자이며 그에 해당하는 길이인 "5"가 ECX에 저장된것을 확인 할 수 있다.


해당 어샘코드 부터 본격적으로 사용자가 입력한 문자를 시리얼로 만들어 낸다.


위 의 어샘코드를 설명하기 전에 사용자 입력을 받기전에 프로그램이 시리얼을 만들어 낼때 사용하기 위한 특정한 키를 저장했는데 그 부분을 살펴보도록 하자

해당 부분은 Byte 단위로 0x10, 0x20, 0x30을 각각 스택에 넣어준다.

이 부분은 40107E 주소 코드에서 [ESP+ESI+0C] 부분으로서 ECX에 저장되어 루프 연산을 시도할때 모두 사용되게 된다.

401083의 [ESP+EBP+10] 이 가르키는 주소에는 사용자가 입력한 Name값이 저장되어 있다. 사용자가 입력한 문자열을 첫번째 글자부터 순서대로 Byte단위로 EDX레지스터에 넣는다.


현재 EDX에는 내가 입력한 "aaaaa"에서 첫번째 "a"가 들어가 있다. 

EDX 에 들어있는 "a" 와 ECX에 첫번째로 저장되있는 0x10 를 XOR연산하여 "71" 이라는 결과값을 만들어낸다.

그리고 40109A 주소의 CALL 401150 부분에서 XOR 연산된 결과인 "71" 을 저장한다.

해당 부분을 자세히 살펴보자


step info(F7)을 이용해 CALL 401150 함수로 들어왔다.

00401156 주소 부분을 살펴보면 스택에 저장된 0019FE7C 주소를 EAX에 저장하는 코드를 확인할 수 있다.

0019FE7C에는 XOR 연산의 결과값인 "71"이 저장되어 있다. 


그렇게 함수를 빠져나와서 루프를 돌게되면 EDX에 저장된 "aaaaa" 중 두번째 "a"와 ECX에 두번째로 저장된 0x20 를 XOR 연산한 결과 값인 "41"를 저장하게 된다. 

0019FE7C 주소에 현재 까지 XOR 연산한 결과값이 차곡 차곡 쌓인다. 


위 사진은 "7141517141" 은 내가 입력한 값인 "aaaaa"의 길이만큼 반복하여 XOR연산한 결과이다.

결론적으로 "aaaaa"의 Serial은 "7141517141" 이 된다.


[정리]

입력값 첫번째 "a" ^ "0x10" = "71"

입력값 두번째 "a" ^ "0x20" = "41"

입력값 세번째 "a" ^ "0x30" = "51"

입력값 네번쨰 "a" ^ "0x10" = "71" #0x10,0x20,0x30 까지 반복하고나면 다시 0x10와 입력값을 xor 연산한다.

입력값 다섯째 "a" ^ "0x20" = "41"

Serial = "7141517141" 


input Serial 에 위에서 도출한 시리얼을 입력하고 실행하면 "aaaaa" 를 xor 연산한 결과가 저장된 버퍼인 EAX와 시리얼 키 값이 저장된 버퍼의 주소인 ESI의 키 값을 서로 비교한다.

그렇게 반복적으로 비교해 입력한 Serial과 저장되어있는 xor 연산한 Serial의 결과 값이 같으면 Correct! 문구가 있는주소로 JMZ 하고 한 글자라도 다르면 Worong! 문구가 있는 주소로 JMZ 하게된다 

최종적으로 프로그램을 실행시키면 "Correct!" 가 출력된다.


그렇다면 ReadMe.txt 에 출력되있는 Serial 인 "5B134977135E7D13"을 반대로 0x10, 0x20, 0x30 를 반복하면서 

5B ^ 0x10 , 13 ^ 0x20, 49 ^ 0x30 이런식으로 XOR 연산하면 NAME의 값을 도출해낼수 있을 것이다.

수동으로 계산하기에는 귀찮으면서 멋이 없기 때문에 코딩을 통해 NAME을 알아내보도록 하자.


[ 코드 ]

https://github.com/leeggoggal/Python/blob/master/reversing.kr_easy_keygenme_reverse_xor.py


해당 코드를 실행하면 Serial "5B134977135E7D13" 에 대한 Name이 출력된다.


EASY KeygenMe Clear!




- Easy Crack 


[reversing.kr 링크] #문제를 풀려면 회원가입 후 로그인해야함.

http://reversing.kr


회원가입 후 로그인을 하고 Challenge 카테고리로 들어오게 되면 문제들을 확인 할 수 있다.

첫번째 문제인 Easy Crack(100pt) 문제를 풀어보도록 하자.


문제를 클릭하면 Easy_Crack.exe 파일이 다운받아진다.


해당 문제파일을 실행시키면 사용자가 입력할 수 있는 창이 출력된다.

임의의 값을 넣고 전달했더니 부정확한 패스워드라는 문구와 함께 프로그램이 종료된다.

해당 문제는 출제자가 정해놓은 패스워드를 리버싱으로 알아내고 그것을 입력하면 풀리게 되는 전형적인 리버싱 기초 문제이다. 


리버싱을 하겠다! 하면 가장먼저 확인해야하는 것은 패킹여부와 어떤 언어로 프로그램이 제작되었는지 확인이 필요하다.

패킹여부, 언어 두 가지를 확인할 수 있는 Exeinfo PE 툴을 사용해보자.


[ Exeinfo PE ] #툴 찾는게 귀찮은걸 알기에 친절하게 올리도록 한다 ㅎ

ExeinfoPe.zip


Exeinfo PE 를 실행하고 문제 파일을 드래그 앤 드롭하면 이렇게 C++로 제작 되었다는 정보와 

Not packed 즉, "패킹이 되어있지 않다" 라는 정보를 얻게 되었다


[Exeinfo 결과]

패킹여부 : Not packed

언어 : C++


이제 IDA PRO 를 이용해 코드가 어떤식으로 작성되었는지 디컴파일해서 확인해보자.

디컴파일 하면 위 사진과 같은 코드가 출력되었다.


코드에서 해당부분을 확인하면 패스워드를 검사해서 출제자가 의도한 패스워드가 맞으면 정답을 알려는 

MessageBox가 출력된다. 즉, 정확한 패스워드를 입력했는지를 확인하는 수단은 MessageBox이다.


이제 본격적으로 ollydbg를 이용해서 문제를 풀어보자.

ctrl + n을 입력하게되면 해당 프로그램에 사용한 라이브러리 import 내용을 확인 할 수 있다.

해당문제가 패스워드를 체크하는 수단은 MessageBox 이므로 해당 부분으로 가서 ctrl+r을 입력하여 

MessageBox 라이브러리를 사용하고 있는 부분을 확인하자.


두번의 MessageBox 호출이 있다. 

패스워드가 맞을 경우와 틀렸을 경우 이렇게 두번이다.


실제로 확인해보니 00401121 주소에서 호출한 MessageBox는 성공했을 경우를 출력하고

00401142 주소에서 호출한 MessageBox는 패스워드가 틀렸을 경우를 출력한다.


그렇다면 패스워드가 틀렸을때 출력되는 MessageBox로 JMP 하게 만드는 분기문이 어디에 있는지 확인해보자

00401135 주소를 확인하면 [ 4010B5, 4010CD, 40110B, 401112 ] 4개의 분기문에서 패스워드를 검사한다는 것을 알 수 있다.


004010B5 분기문을 확인해보면 ESP+5 와 , 61(a) 를 비교하는 것을 확인할 수 있다.

ESP+5에 우리가 입력하는 값이 들어가 있는지 그리고 들어가 있다면 패스워드의 몇번째 자리를 의미한지를 확인해보자

먼저 브레이크 포인트(F2)를 004010B0 주소에 걸어주자 그리고 프로그램을 실행해 임의의 값을 입력하자.


"abcdef" 를 입력하고 프로그램을 실행했다.

위 그림을 보면 입력한 "abcdef" 의 값중에서 b와 61(a)를 비교한다. 이것을 통해 패스워드의 두번째 자리는 "a" 가 된다는 것을 확인 할 수 있다.

더욱 정확히 확인하기 위해 addressing 부분을 확인 했다. 결과는 정확히 esp+5는 내가 입력한 패스워드의 두번째 자리인 b를 담고 있다.


첫번째 분기문을 넘어가기 위해 입력값 두번째에 a를 넣고 다시 실행해보자.

입력값은 "pabcedf"

이번에는 특정 함수가 3개의 인자를 받는다. arg3=2 arg2= "5y" arg1="bcedf"

그리고 3개의 인자를 00401150의 주소에 해당하는 함수를 호출해 넘겨준다.

3개의 인자가 해당 함수에서 어떻게 사용되는지 그리고 패스워드의 몇번째 부분을 체크하는지 확인하기 위해

Step info(F7)을 통해 함수내부의 동작 과정을 살펴보자.


해당함수의 동작 과정을 확인해보면 순서대로 arg3 인자는 ECX 에 저장하고 arg1 인자는 EDI에 , arg2 인자는 ESI에 저장된다.

arg1에 우리가 입력한 값 "pabcedf" 중 앞 2자리가 없는 "bcedf"가 저장되는 것을 보아 arg3의 인자가 

함수내에서 하는 역할은 사용자가 입력한 값중 앞자리를 arg3에 저장된 값 만큼 자르는것 같다.


[정리]

Arg1 = 우리가 입력한 값

Arg2 = 출제자가 정의해둔 특정 부분의 패스워드인 "5y"

Arg3 = 사용자가 입력한 값을 앞에서부터 몇개를 잘라낼것를 전달하는 인자


그리고 함수내에 존재하는 첫번째 분기문을 확인해보면 패스워드의 3번째 자리인 b와 "5"를 비교한다.

그러므로 패스워드의 3번째 자리는 "5"라는 것을 알 수 있다.


패스워드 3번째에 5를 입력하고 다시한번 실행해보면 두번째 분기문에서 패스워드의 4번째 자리와 "y"를 비교한다.

그러므로 패스워드의 4번째 자리는 "y" 라는것을 알 수 있다. 


이번에는 사용자가 입력한 패스워드의 5번째 자리를 시작으로 출제자가 정의해둔 "R3versing" 라는 문자를 반복적으로 비교한다. 즉, 입력한 패스워드의 5번째 이후부터 패스워드는 "R3versing"가 되어야 한다.


마지막 분기문에서는 간단하게 esp+4와 45("E")를 비교한다.

출제자가 정의한 패스워드의 첫번째 자리는 "E" 가 된다.


[ 패스워드 ]

첫번째 자리 : "E"

두번째 자리 : "a"

세번째,네번째 자리 : "5y"

다섯번째 이후 자리 : "R3versing"

최종적인 패스워드 : "조합하도록 하자"



#Easy Crack Clear!