Kali-KM_Security Study



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

CodeEngn Advance 02  (0) 2015.07.30
CodeEngn Advance 15  (0) 2015.07.30
CodeEngn Advance 14  (0) 2015.07.24
CodeEngn Advance 12  (1) 2015.07.22
CodeEngn Advance 11  (0) 2015.07.17
CodeEngn Advance 10  (0) 2015.07.13

Comment +0

문제확인


이번 문제는 독특하게도 Unlock Code가 존재한다. 문제를 실행 시켜서 About을 통해 확인을 해보았을 때 올바른 Unlock Code를 찾은 후에 진행을 해야 하는 것 같다.

The Serial-generation code in this crackme is obfuscated and there are no signatures this time.

After the Unlock Code has been found, type it in and it will de-obfuscate the serial generation code.

Then it should be straight forward.


Rules:

- No patching

- Find the correct unlock code

- Write a keygen

- Write a tutorial explaining how you solved this one and please include your code


i really look forward to seeing your solutions!



Unpacking - 1


Ollydbg를 통해서 문제를 열자마자 난해하게 되어 있다. 패킹이 되어 있는 것 같다. 이를 해제하기 위해선 그냥 단순하게 쭉 따라서 가도 괜찮지만  나는 그렇게 시간이 오래걸리는 방법을 선호하지는 않는다. 그렇기에 PE 구조도 최근에 공부한김에 이를 통해서 해보기로 했다.


우선 현재 EIP는 바로 저 부분으로 제대로 설정이 되어있다. 아래의 사진에서 보듯이 .text 영역은 흔히 Code 영역으로 실행에 쓰이는 Opcode들이 존재하고 있다. 이러한 .text 섹션의 시작 주소는 1000이다. 즉 ImageBase 까지 더한다고 생각하면 401000으로 이동을 해보자.


그 후 이 부분에 BP를 걸고 F9를 통해 진행을 할 경우 두번째 사진과 같이 패킹이 해제 되는 것을 확인 할 수가 있다. 이러한 방법 외에도 다양한 방법이 있는 것 같으니 직접 찾아보는 것을 추천드립니다.



Unpacking -2 


프로세스를 진행하다 보면 아래와 같이 2개의 호출문을 볼 수가 있다. 첫 번째 호출문은 Unlock Code를 가지고 어떠한 연산을 진행하는 부분이고, 두 번째 호출문은 0x7B 번 어떠한 연산을 진행하는 과정이다. 하나하나 살펴보자.


호출문 - 1

이 함수 내에는 Loop가 존재하는 이러한 반복문은 항상 수상히 여겨야 한다. 바로 내가 입력한 UnlockCode를 가져와서 연산을 진행한다. 연산에 대한 과정은 아래에 직접 타이핑한 것을 보면 어떻게 Loop가 진행하는지 충분히 이해를 할 수가 있다.

여기서 중요한 것은 바로 붉은 상자에 표시한 부분인데 저 부분이 최종적으로 나온 값이 저장되는 곳이다. 이 값은 후에 쓰이게 된다.

<Loop - 1 참고>


호출문 - 2

여기에는 위에서 나온 값을 통해 연산이 진행이 된다. 우선 lstrlen을 통해 Unlock Code의 길이가 1보다 큰지를 확인하고 만약 아니라면 바로 실패문을 출력을 한다. 이 부분을 넘어가면 바로 0040111C 에서부터 0040123C까지 이미 지정된 값을 위에서 나온 값과 XOR 연산을 진행하는 작업이다.

여기서 이 XOR에 쓰일 위의 값이 중요한 이유는 바로 감춰진 명령어들을 우리가 복구해야하기 때문이다.


특정한 값과 XOR연산이 진행된 후에 해당 주소의 값은 모두 변해 있다. 아래의 Dump 부분에 드래그 한 곳부터 나중에 호출이 되므로 이후에 참고하자.




Name을 통한 값 생성


위의 XOR 과정을 통해서 생성된 4011E8부분을 호출한다. 여기서 중요한 것은 결국 위의 XOR 값이 잘못된 값일 경우에는 이상한 명령어들로 복구시키므로 인하여 제대로 동작이 하지 않을 수가 있다. 즉 UnlockCode에 어떠한 값을 넣는지에 따라 전혀 다른 진행이 이루어 진다는 것이다.

처음에 나는 JMP문을 통해 어떠한 부분으로 가는 명령어들을 생성해야하는지 알고 삽질을 하였으나, 지인의 조언을 통하여 결국 새로운 함수를 호출하는 부분이므로 결국 그 함수에 진입을 하였을땐 스택프레임을 구성하는 작업이 이루어질것이라는 말이였다. 

즉 PUSH EBP; MOV EBP,ESP 가 이루어져야 하기에 위에서 4011E8의 코드를 55 8B EC에 초점을 맞추고 XOR 연산을 진행하였다. 그 결과 모두 0x25와 XOR을 진행했을 경우에 아래와 같이 정상적인 코드가 복구되는 것을 확인 할 수가 있다.


아래는 정상적으로 복구된 코드의 부분이다. 우선 GenerateSerial 이라고는 했지만 저 부분에서 최종 키 값이 형성이 되는 것이 아니라 그 최종 키 값 형성에 쓰이는 값을 형성하는 부분이므로 중요하다.


안을 들여다 보면 아래와 같이 Name을 통해 특정한 값을 생성한다. 구성은 아래의 Loop-2를 참고하면 조금 이해하기 편할 것 같다. 입력한 Name을 한 글자씩 읽은 다음에 Count 값과 같이 연산을 진행하는 구성이다. 



Serial 생성


위의 함수를 빠져나오면 바로 다시 새로운 Loop를 만나게 된다. 여기선 위에서 최종적으로 나온 값을 통해 Count와 연산을 진행하는 작업이 진행이 된다. 이 역시 자세한 구성은 아래의 Loop - 3을 참고하는 것이 편할 것이다.


그리고 성공문을 체크하는 곳으로 가면 아래와 같이 내가 입력한 Serial과 Name에 따라 생긴 Key 값이 한글자씩 비교가 되는 부분을 확인 할 수가 있다.



느낀점


이번 문제를 풀면서 역시 자살충동이 솟아났다. 도저히 혼자서 jmp문을 고집했다면 죽어도 못풀문제였다. 이번에 확실히 느낀건 여러 문제나 파일을 접하면서 직접 많이 해보는 것이 가장 좋은 방법같다. 앞으론 패킹이 되어있는 파일이 언패킹되는 과정을 좀더 유심히 보게 될것 같다.




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

CodeEngn Advance 02  (0) 2015.07.30
CodeEngn Advance 15  (0) 2015.07.30
CodeEngn Advance 14  (0) 2015.07.24
CodeEngn Advance 12  (1) 2015.07.22
CodeEngn Advance 11  (0) 2015.07.17
CodeEngn Advance 10  (0) 2015.07.13

Comment +0

문제 확인


이번문제도 시리얼이 주어지고 이에 해당하는 Name을 구하는 것이 문제이다. 이제 14번 이전까지 문제를 풀었던 사람들이라면 충분히 손쉽게 문제를 풀 수가 있는 문제이다.



분석


우선 성공분기점을 찾는다. 아래와 같이 IDA를 통해서 봤을 때 LStrCmp()를 통하여 성공의 분기를 결정하는 것을 확인 할 수가 있다. 그렇다면 저 부분을 기준으로 이전의 명령어들을 살펴보자.


우선 프로그램을 디버거로 열어보면 아래의 지점으로 EIP가 구성이 된다. 그리고 총 5개의 호출함수가 보인다. 여기서 우리가 알아야할 것은 바로 4번쨰에 있는 함수로써 프로그램의 버튼을 클릭하였을 경우 이루어질 동작에 대해 정의된 곳이다.

그 이전의 부분들은 GUI를 구성하는 작업을 하는 것이므로 굳이 힘들게 분석할 필요는 없는 것 같다.


Run() 함수로 진입하여 살펴보다보면 아래와 같이 내가 입력한 Name과 Serial들을 가지고 확인작업을 하는 것을 볼 수가 있다. 설명은 주석으로 달아놓았지만 그래도 모든 부분을 다 포함하기에는 너무나 길기 떄문에 IDA-Hexray로 대체하겠다.


밑에 보는 것과 같이 특정한 자리에 특정한 값이 오는지를 확인하기도 한다. 이러한 중간에 하나라도 조건에 부합하지 않는다면 바로 실패구문으로 넘어가게 된다. 그리고 이러한 구문을 모두 거친 뒤에서야 비교문이 나오게 된다.



풀이


위의 많은 조건들을 통과하면 아래와 같이 Create_Serial()을 호출하는 부분을 발견할 수가 있다. 이 부분에서 바로 Name에 따른 Key 값이 형성이 된다.


우선 Name을 1111로 입력하였을 경우 Name이 총 16글자로 맞추어 지는 것을 확인 할 수가 있다. 그렇게 형성된 16글자를 가지고 "NH KeyGenMe6"와 함께 한 글자씩 ADD 연산을 통하여 연산을 진행한 후에 생기는 값들을 통하여 최종적으로 키 값을 형성한다.


이러한 루프를 모두 거치면 아래와 같이 007F0079...등 많은 숫자들이 있다. 하지만 여기서 키 값에 사용되는 것은 바로 앞의 12글자이다. 따라서 우리가 필요한 글자는 3글자이며 나머지는 모두 무시된다는 것을 확인할 수가 있다.

하지만 그렇다해서 Name을 3자리만 입력하라는 것은 아니다. Create_Serial()로 오기 이전의 조건문에 의하여 name의 수는 4의 배수여야하는 것 같다.


아래는 Name 을 1234로 했을 경우에 구하는 소스이다. 이제 이렇게 구했으므로 우리는 역으로 뺄셈을 통하여 쉽게 답을 찾을 수가 있다.


Create_Serial()을 통과한 후에 lStrCmp()를 통하여 내가 입력한 시리얼과 Name에 따른 KEY값이 비교되는 것을 확인 할 수가 있다.



따라서 우리는 3글자만 알면되기에 아래와 같이 3글자를 채워주고 마지막 한글자는 아무거나로 채우면 성공문을 확인 할 수가 있을 것이다.


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

CodeEngn Advance 02  (0) 2015.07.30
CodeEngn Advance 15  (0) 2015.07.30
CodeEngn Advance 14  (0) 2015.07.24
CodeEngn Advance 12  (1) 2015.07.22
CodeEngn Advance 11  (0) 2015.07.17
CodeEngn Advance 10  (0) 2015.07.13

Comment +0

문제확인


이번문제는 참 오래걸렸다. VB 특성상 MSVBVM60.DLL에 의하여 동작이 되기 때문에 개인적으로 다른 것에 비하여 분석하기가 까다로웠다. 





풀이


우선 시작부터 너무나 많이 왔다갔다하기때문에 중요한 부분만 사진으로 보겠다. 아래의 사진을 보면 ABCD를 입력하였을 경우 그에 해당하는 10진수의 값을 하나하나 주소에 적는 과정을 나타내고 있다. 이렇게 입력한 Name에 따라서 기록이 된다.


그렇게 입력된 값을 가지고 우리는 Value[5:9] 의 값이 밑에 rtcMidCharBstr에 의하여 4글자만 가지고 올 수 있게 된다. 밑에선 65666768에서 6768이 바로 그 값이 된다. 이 값은 나중에 시리얼에서 가운데 부분을 구성하게 된다.


위에서 나온 값을 가지고 아래의 부분에서 Loop가 진행이 된다. 한글자씩 가지고 연산을 하는 것이다. 이에 대한 설명은 예를 들어서 4949 일경우에 python으로 코드가 출력되게 한것이다.

자세히 보면 Loop후에 NEG EDX가 나오는데 이로 인하여 0-value를 하므로 인하여 우리가 원하는 값을 얻을 수가 있다.


그리고 이 밑에 부분이 가장 중요한 부분이라고 할 수 있다. 실제로 분석을 하다보면 너무 많은 분기문으로 인하여 지치게 된다. 그래서 저도 정말 많이 포기하고 싶었습니다.. 그래서 치트엔진, VB Decompiler, IDA 까지 동원하여 최대한 찾기위해 노력하였습니다. 

항상 어떤 중요한 변동이 있을떄마다 바로 이 부분을 지났기때문이다. 주석 또한 정말 위에도 강조하고 아래에서도 강조하게 된것은 한번만이 아니라 저 부분에서 핵심적으로 동작이 이루어지기 떄문이다.


이제 계속 실행을 하다보면 레지스터에 아래와 같이 정렬이 되며 E590는 위에서 연산을 통해 나온 가운데 시리얼 값이다.  추가적으로 실행하다보면 두번쨰 사진과 같이 한 글자가 사라지기도 하고 계속 이런식으로 변동이 이루어진다.


그리고 이 밑에 부분은 시리얼의 맨 앞자리 값을 결정 짓게해주는 부분이다. 어찌보면 가장 중요한 부분인데 fld fcom 등등 모르는 부분이 너무 많았다... 확실한 것은 저 부분에 들어가기 전에 모든 ECX에 5가 존재하고 그 5에 30을 더하여 EAX로 옮기는 것 같다. 


위를 통하여 14라는 숫자가 나온다. 거기에 6768-E590에서 맨 앞자리를 지우고 14를 앞에 붙여준다. 이렇게 해서 첫번째 시리얼 부분이 형성이 된 것이다. 참고로 여기서 5번째 글자를 없애면 된다.

그리고 마지막 부분의 시리얼 값은 맨 앞자리의 '1'의 10진수 값인 '49'가 나온다.

그리고 그 뒤에 0을 붙이고 맨 마지막 자리는 Len(name)+5의 값으로 형성이 된다.

이렇게 해서 ABCD일 경우의 Serial은 아래와 같다.

아래의 사진은 AAAA로 했을 경우의 성공문이다.



느낀점


이번 문제를 풀면서 정말 많은 자괴감에 빠져버렸다. 아무리 찾아도 찾아도 나오지가 않기에 리버싱이 내 적성이 아닌가 싶기도 느낄 정도였다. 이번문제의 답을 구하긴 했지만 이것도 완벽한 분석으로 구한 것이 아니다. 가운데 시리얼을 통해서 'od'라는 글자가 나왔을때 왠지 CodeEngn일거 같아서 한건데 성공해버렸다. 

그래서 다른 블로그를 참고하면서까지 이번문제를 풀었다. 정말 문제를 풀었으면 보통 기뻐야하는데 기분이 찝찝하다...다음엔 더 열공해서 풀 수 있길..ㅠㅠ


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

CodeEngn Advance 15  (0) 2015.07.30
CodeEngn Advance 14  (0) 2015.07.24
CodeEngn Advance 12  (1) 2015.07.22
CodeEngn Advance 11  (0) 2015.07.17
CodeEngn Advance 10  (0) 2015.07.13
CodeEngn Advance 09  (0) 2015.07.12

Comment +1

  • 승훈 2017.08.12 16:51

    저는 운이 좋아서 30분만에 풀었어요. 다른 문제는 몇일 걸렸는데... 리버싱도 역시 운이 많이 작용하는 학문은 확실한것 같내요. 저는 올리디버거에서 Ctrl+N 한뒤에 &MSVBVM60.__vbaStrMove 에 bp 걸고 지나가는 문자열 분석만하면서 풀었어요. 정작 리버싱은 하나도 안하고 풀었내요. 저의 답 : d3d00000

문제확인





확인




Name에 따른 시리얼이 형성되는 부분은 쉽게 찾을 수가 있다. 내가 입력한 name은 0000 serial은 11111111로 입력하였을때 아래와 같이 시러일이 비교되는 부분이 있다. 그리고 그 위에 이러한 키 값이 형성되는 부분이 존재 한다. 



위에 있는 덤프창에 드래그 해 놓은 부분이 바로 키 값 형성에서 if 문을 결정짓게 해주는 요소들이다. 그리고 아래의 사진은 수많은 if문들이 존재한다. 실제로는 16~20개의 if 문이 존재한다. 하지만 여기서부터 문제가 시작되었다. 

이번문제의 가장 큰 시사점은 작은 그림이 아니라 큰 그림을 보아야 한다는 것이다. 저러한 if문들을 계속 하나하나 따라가다보면 정말 미궁으로 빠져버린다. 본인은 미궁에서 2일을 소비했다...

바로 위의 if문을 보는 것이 아니라 아래와 같이 Dump를 지속적으로 확인해주어야 한다. 402538의 부분을 보면서 계속 진행을 하다보면 아래와 같이 3B10  31  6D  64  38 이 존재하고 있는 것을 확인 할 수가 있다. 여기서 31은 내가 입력한 serial인 ('1111')의 한 글자이다. 


위와 같이 셋팅이 된후에는 바로 연산이 진행된다. 3B10  +  31*(64+38)과 같은 식으로 진행이 된다. 그렇게 나온 값이 Sum = CF1BC이다. 이제 이와같이 이러한 연산이 key[]에 있는 모든 배열들을 순차적으로 더할 때까지 반복이 된다.

그렇게 해서 나온 값이 바로 sum = 3315C0이다. 이제 이 값을 가지고 다른 연산이 진행이 된다. 바로 F로 나누는 것이다.


F로 나눈 후에 3315C0 / F = 367D9 ... 9가 남는다.

이제 이 9에 0x30을 더 해준 후에 키 값으로 올려버린다.

그리고 남아있던 367D9에는 2를 곱하여 준다.

이렇게 더이상 F로 나누어 질 수 없을 때 까지 반복이 된다.

하지만 계속 반복이 되는 중에 만약 나머지가 0x3A~0x40일 경 밑에 2번쨰 처럼 '0~9,A~Z'가 아닌 특수문자가 나오게 되는데 이를 방지하기 위하여 A~E가 나머지로 나올 경우 +8을 해준다.


이렇게 계속 반복을 하다가 더이상 F로 나눌 수 없는 값이 될때까지 반복을 한 후에 마지막 나머지 또한 key값으로 보낸다.

위에 말이 너무 복잡하게 설명이 되어있는데 코드로 본다면 아래와 같이 간결하게 나열이 된다.




풀이


위에서 구한 방법을 통하여 94E7DB1B를 뒤에서 부터 다시 올라오면 된다. 그렇게 되면 D2A734이 나오는데 이는 맨 처음의 SUM 값인 0X3B10이 더해져있는 값이다. 따라서 KEY[]를 구하는 방법은 KEY[] = (D2A734 - 3B10) / 426C 이다. 이제 이렇게 나온 값은 0x32B이며 KEY[]의 모든 ASCII의 조합이 이렇게 나오면 된다는 것이다.

정답은 많은 경우의 수가 존재하므로 그 중에서 하나를 골라서 게시판에 비공개로 올리면 정답으로 처리가 될 것이다.


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

CodeEngn Advance 14  (0) 2015.07.24
CodeEngn Advance 12  (1) 2015.07.22
CodeEngn Advance 11  (0) 2015.07.17
CodeEngn Advance 10  (0) 2015.07.13
CodeEngn Advance 09  (0) 2015.07.12
CodeEngn Advance 08  (50) 2015.07.01

Comment +0

제 확인







기초 확인



위의 사진은 주석과 같이 name을 입력 받고 name의 길이를 확인 하는 과정입니다.


이번에는 Serial을 입력받고 그 길이를 확인하는 과정입니다. 길이는 12글자여야 합니다.


참고로 이 접어놓은 부분은 문제의 의도를 잘못파악하고 단순노동을 작업하였다. 매우 삽질스러우니 한가할떄만 열어보자.



분석


IDA를 통하여 HEX-Ray 기능을 통하여 성공 구문이 있는 곳을 확인 하였을때, 아래와 같이 check_serial 이라는 부분이 있는 것을 확인 할 수가 있다. 정말 누가 봐도 저곳을 확인해달라는 말인 것을 알 수가 있다.


Check_Serial() 내부로 들어오면 아래와 같이 확인을 할 수가 있는데, 여기서 확인을 해야 할 것은 붉게 표시한 3곳을 잘 보아야한다. stringFindSecond 함수를 두번 호출하고 그 반환 값들의 차를 구하여 그 값이 5가 넘지 않을 경우에 v28 에 0을 준다는 것이다. 이게 구체적으로 어떻게 생각해야 하는지는 아래에서 설명하겠다.


위의 부분이 바로 ida를 통하여 붉게 표시했던 3곳이다. 여기서 보아야 할 것은 첫번쨰 함수를 호출하기 전에 내가 입력한 serial 값의 한글자를 가져오는 작업이 이루어졌고, 두번째 호출을 하기 전에는 입력한 Name의 한글자를 가져오는 것을 확인 할 수가 있었다. 아래의 사진과 같이 설명을 하겠다.

만약 Name = 1111  serial = 000000000000 일경우를 생각해보자. 여기서 위의 순서대로 우선 serial 한글자인 0을 가지고 온다. 이 0을 첫번째 strFindSecond()를 통하여 호출을 하였을 경우 아래에 있는 키 순서값들중에서 0이 나올때까지 계속해서 한글자씩을 보낸다. 25번째에 '0'이라는 값이 있다. 

이번엔 두번쨰 호출에서 name의 한글자인 1을 생각해보자. 1의 경우는 30번쨰에 위치를 하고 있다. 이 둘의 범위는 5를 초과하지 않으므로 성공문이 출력이 된다. 이처럼 결국 name 1을 기준으로 한다면 '1'이 30번쨰이니 25~35번째 까지의 모든 단어가 성공문을 출력할 수 있게 해준다. 이것이 바로 CMP EAX, 5를 확인하는 이유이다. 

이제 키 값이 어떻게 형성되는지 확인 했으니 프로그래밍을 해보자.

'A.J.X.G.R.F.V.6.B.K.O.W.3.Y.9.T.M.4.S.2.Z.U. .I.7.0.H.5.Q.8.1.P.D.E.C.L.N'



프로그래밍



코드에 대한 설명은 주석에 구체적으로 해놓았다.


참고로 여기서 출력 되는 J - 'A'의 경우 위에 주석에서는 써놓았지만 a-z를 하면 소문자로 치환하고 해서 번거로우므로 그냥 대문자 자체로 출력되도록 하였다. 따라서 저기서 'A'는 'a'라고 생각하면 된다.

p.s 2일에 걸쳐서 삽질을하여 풀었다가, 우연히 카페에서 이 문제를 다시보는데 문득 생각이 들어서 풀 수가 있었다. 고로 리버싱문제는 인내를 갖고 여러 방면으로 생각을 해봐야 문제를 쉽게 풀 수 있을 것 같다.

10_Find_Key.py


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

CodeEngn Advance 12  (1) 2015.07.22
CodeEngn Advance 11  (0) 2015.07.17
CodeEngn Advance 10  (0) 2015.07.13
CodeEngn Advance 09  (0) 2015.07.12
CodeEngn Advance 08  (50) 2015.07.01
CodeEngn Advance 06  (0) 2015.06.28

Comment +0

문제 확인


문제를 확인해보면 참 설명이 간단하게 나와있다. Password는 무엇인가?이다. 그렇다면 시리얼값을 구하는 것일까했지만 여기서 문제는 우리가 직접 ID까지 알아내야한다는 것이다. 프로그램을 바로 실행시키면 아이디로 무엇을 입력해야할지 당황스럽다. 


하지만 막상 디버거로 프로그램을 열면 바로 덤프 창에 This is the Username : DonaldDucK 이라고 적혀있는 것을 볼 수가 있다. 이것이 바로 우리의 ID 값이다. p.s 사실 굳이 이것을 발견하지 못해도 이후에 DonaldDuck과의 문자를 비교하는 Loop 구간이 있기에 처음에 발견하지 못하더라도 나중에 발견할 수가 있을 것이다.




풀이



우선 프로그램을 진행하다보면 위와 같이 내가 입력한 키값 123 (0x7B)와 실제 값인 0x88228F를 비교하는 것을 확인 할 수가 이다. 그리고 조금 더 밑에는 주석에서와 같이 DonaldDuck인지를 비교하는 Loop 구간을 확인 할 수가 있다.

키 값을 알았지만 자꾸 실패문이 뜨는 것을 확인 할 수가 있었다. 이는 성공문을 출력하기 위해서는 우리가 패치를 해야한다는 것이다. 이러한 패치는 2가지 과정으로 이루어 질 것이다. 성공구문 근처에서 틀린 키값을 가지고 수정을 하는 과정과 옳은 키 값을 가지고 수정을 하는 과정이다. 


이와 같이 틀린 키 값을 가지고 프로그램을 진행하기 위해서는 2번의 명령어를 수정해야한다. 이를 통하여 우리는 틀린 키 값으로도 성공문을 확인 할 수가 있게 되었다. 


이번에는 옳은 키값을 가지고 수정을 하였다. 이번에는 한가지의 명령어만 수정을 하면 되었다. 그렇다는 것은 바로 저 부분만 고치면 옳은 키값을 넣을 경우에는 성공문으로 가게 되고 틀린 키 값을 넣으면 실패문이 출력되도록 할 수가 있다.




키 값 찾기



CMP EAX, PTR DS:[ECX] 에서 EAX에는 우리가 입력한 값이 전달되고 PTR 부분에 키 값이 저장되어 있다. 하지만 이 PTR의 주소는 실행을 할 때마다 다른곳에 위치하고 있다는 것을 확인 할 수가 있었다. 그렇다면 이는 어떠한 것 떄문에 그런지 확인을 해보자.


바로 위의 사진에서와 보듯이 우리는 MOV DWORD PTR DS:[BC4404],EAX를 통하여 BC4404의 주소에 키 값이 저장된 공간의 위치가 전달 되는 것을 확인 할 수가 있다. 이러한 키 값이 있는 곳의 주소는 VirtualAlloc 를 통하여 형성이 된다.  VirtualAlloc를 통하여 할당된 주소를 EAX에 반환하고 그 값에 ADD EAX,2A0를 통하여 최종적으로 전달할 공간의 주소를 형성한다.


위의 사진을 보자. Address 파라미터의 값이 NULL일 경우 시스템이 주소를 결정한다는 것이다. 바로 이것이 키 값이 저장된 위치가 실행할때마다 변화했던 이유이다.


앞에서 확인 했던 것과 같이 키 값이 있는 곳의 주소는 00BC4404에 기록이 된다. 저 해당 주소로 가면 우리가 성공문을 출력하는데 필요한 키 값이 위치하여 있다. 추가적으로 아래의 사진은 이번 문제를 삽질할때 보았던 Flag Register에 관해 짧게 써있는 것이다. 


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

CodeEngn Advance 11  (0) 2015.07.17
CodeEngn Advance 10  (0) 2015.07.13
CodeEngn Advance 09  (0) 2015.07.12
CodeEngn Advance 08  (50) 2015.07.01
CodeEngn Advance 06  (0) 2015.06.28
CodeEngn Advance 05  (0) 2015.06.28

Comment +0

문제확인


이번문제는 CodeEngn Basic에서도 유사한 문제가 존재한다. 하지만 Advance인 만큼 그때와는 다르게 열정적으로 풀었다. 정말 시간이 참 빨리가는 삽질을 한 것 같다. 우선 문제를 확인해보면 참 말은 쉽다. Key 값이 주어지고 그에 맞는 값을 찾아라. 여기서 힌트는 2자리라는 것이다.


하지만 정작 2자리를 입력하면 위와 같이 더 입력해야한다고 출력이 된다. 그러므로 우리는 CMP len(name),3을 비교하는 부분을 찾을 것이다. 그 부분은 밑에 사진에서와 같이       CMP EAX,3 의 형태로 있으며 숫자 3을 2로 수정하여 2자리로도 입력이 가능하게끔 패치를 해주자. 나는 저렇게 수정을 한 후에 OllyDump를 통해서 저장을 하였다.


이렇게 패치를 한 파일을 가지고 분석을 시작할 것이다. 일단 Name 값은 AA로 시작을 하였다.

진행을 하다보면 위와 같이 성공으로 가는 분기문이 나타나며 레지스터 부분을 보면 내가 입력한 값과 해당 name에 따른 key 값이 나타나 비교될려 하는 것을 확인 할 수가 있다. 따라서 우리는 이처럼 쉽게 key 값을 확인 할 수가 있다. 문제는 key 값이 주어져 있고 우리는 name을 찾아야 한다는 것이다. 

그러므로 우리가 해야할 것은 바로 알고리즘 분석이다. 참고로 이 알고리즘 분석에서 애를 꽤 많이 먹었다. 결국 5개의 알고리즘에서 한개는 끝까지 풀지 못하였다. 하지만 그렇다 해도 문제를 푸는데에 있어서는 지장이 없다.



알고리즘 분석



첫번째 알고리즘이다. 위의 주석과 같이 name[i]를 통한 연산이 진행된 후에 레지스터를 보는 것과 같이 ESI에 저장되어있는 값의 앞자리 4자리가 첫번째부분의 키 값으로 사용이 된다. 이처럼 첫번째 알고리즘은 쉽게 분석을 하여 끝낼 수가 있다.


두번째 알고리즘이다. 이번에도 위와 같이 쉬운 연산으로 진행이 되지만 가령 Name이 'AB'일 경우 뒷자리인 'B'에서부터 연산을 시작한다. 뿐만 아니라 여기서는 Local.4가 있는 것을 확인 할 수가 있는데, 연산을 통한 값이 바로 이 Local.4에 저장이 되어 나중에 최종 키 완성에 전달이 된다. 이 값이 두번째 키 값이 된다.


세번째 알고리즘이다. 이 부분도 주석과 같이 쉽게 설명이 되어 있고 레지스터에서 보는 것과 같이 EDI의 앞자리 4자리가 키값의 4번째 값에 쓰인다.


네번째 알고리즘이다. 이 부분은 괜히 쓸때 없이 길다. EBX의 값이 마지막 키 값으로 사용이 된다. 



삽질 - 실패


위에서 확인 한 것과 같이 4개의 알고리즘을 통해 Key1 - Key2 - 빈부분 - Key3 - Key4 로 진행이 되어 있는 것을 확인 할 수가 있다. 그렇다 바로 저 빈부분이 내가 정말 삽질을 해도 나의 실력으로는 더이상 하지 못할 그것이였다. 분석하면서도 너무 복잡하여 결국 포기하여 버렸다.

위와 같이 FILD 부분과 RETN의 두칸 위에인 FISTP Cases 9,A,B....이 부분을 통해서 FILD에서 읽을 부분을 FISTP에서는 저장할 공간을 나타내어 값을 옮기는 형식으로 진행이 되는 것 같다. 이를 통해서 덤프창에서와 같이 가운데 키 값의 값이 형성이 된다. 이 키 값은 결국 밑의 연산을 통해서 나타난다.


위와 같이 ADD 연산을 통해서 덤프창에 있는 PTR:EAX의 값에 EDX를 더하므로 인하여 밑에와 같이 키 값이 형성이 된다. 이 키 값을 통하여 위의 사진처럼 FILD를 통하여 키 값이 형성이 된다. ...내가 말했지만 참 모르겠다. 정말 이 부분은 내 멘탈이 찢겨진 곳이라 할 수 있다.




프로그래밍


위의 알고리즘들을 통하여 우리는 프로그램을 만들 수가 있다. 브루트포스와 같이 모든 값을 출력하는 형식으로 프로그래밍을 해보았다. 여기서 내가 잊지 않기 위한 필기로서 파이썬에선 배열을 선언하고자 할 때 tmp=[0]*n 을 통하여 해당 크기 만큼의 값을 선언을 해 놓아야 한다. 그리고 아래의 5~7번줄과 같이 값을 선언해주면 된다.

위와 같이 코드를 짜준 다음에 실행을 하면 아래와 같이 텍스트파일이 생성이 되어있다. 이를 통하여 우리는 모든 경우의 값을 확인 할 수가 있으며, 주어진 문제의 키값을 통하여 Ctrl+F를 통하여 찾으면 문제를 풀 수가 있다. 


텍스트파일에서 찾은 값을 통하여 입력을 해주어 답이 맞는지를 확인하는 과정이다. 솔직히 쉽게 풀 수 있었는데, 모든 알고리즘을 분석하려다 보니 시간이 오래걸려 버렸다. 그리고 아래의 코드는 위와 같이 할 수 있는 알고리즘을 모두 출력하는 것이 아니라 입력한 키 값에 해당하는 부분을 출력해주는 프로그램을 별도로 만들어 보았다. 

아무래도 위의 프로그램보단 아래의 프로그램이 문제를 푸는데에는 시간이 훨씬 절약이 된다. 이렇게 코드를 짜 놓으면 주어진 문제의 Name 값이 길어지더라도 쉽게 문제를 풀 수가 있을 것이다. 

아래의 사진은 위의 프로그램을 통하여 답을 찾은 결과이다. 새삼스럽지만 답은 블럭처리하였다. 글을 읽는 여러분도 쉽게 문제를 풀 수가 있을 것이다. 나처럼 저 늪에 빠지지 않는다면..

by Kali-KM


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

CodeEngn Advance 10  (0) 2015.07.13
CodeEngn Advance 09  (0) 2015.07.12
CodeEngn Advance 08  (50) 2015.07.01
CodeEngn Advance 06  (0) 2015.06.28
CodeEngn Advance 05  (0) 2015.06.28
CodeEngn Advance 04  (0) 2015.06.28

Comment +50

  • 이전 댓글 더보기
  • cococo 2015.09.26 14:18

    이미지 출력을 하고싶은데 댓글이라그런지 이미지는안되네여 음.. 코드는 정상적이게 되긴하는데 입력만되고 출력은 안나오네요 혹시 실행방법이 따로잇나요?

  • cococo 2015.09.26 14:34

    도와주세영 엉엉

  • http://fedorax.tistory.com/ 여기에 이미지 올렸어요 1. 입력은 돼요 2. 그런데 출력이안돼요

  • 혹시 정답이 C6맞나요??

  • 정말 감사합니다. 정말정말 감사합니다. 그리고 저 티스토리 아이디있었는데 모르고 CoCoCo로 임시로 썻네요..

  • 간디 2015.11.02 00:28

    if (hex(esi)[-9:-5])==key: 이렇게 하신이유가 상세히 뭐죠?? 알고싶어서..

    • 원래는 연산을 진행할때 정해진 자릿수가 넘으면 그걸 없애야 하는건데 그렇게 까지는 프로그래밍할 실력이 되지 않아서 자릿수 초과한부분을 제외한 첫 4글자(첫 시리얼 키값)을 뽑아내려한거에요

  • Cococo 2015.11.02 01:17

    [-9:-5]이 슬라이싱은 print hex (esi)로 일일이 보며 자르신건가여 아니면 어떤방법으로 키값위치를알아내고자르신거죠?

    • 음 따로 무슨 방법이라기 보다는 그냥 알고리즘에 있는 값이 연산된 후에 출력될 때 xxxx00001111bbbbcccc와 같이 불필요한 부분이 있어서 거기서 필요한 값만 추출할 수 있도록 문자열 출력 범위를 지정해준거에요. 어떻게 그 위치를 아는지는 연산을 진행하다보면 어느 부분이 필요한지 알 수가 있으니 그 값만 추출해서 비교하도록 하는 것입니다.

    • cococo 2015.11.02 01:33

      그러면 그 알고리즘에있는값이 연산된 후에 출력된값이 파이썬으로 코딩하고 나서의 상황인가요 아니면 올리디버거로 분석했을경우의 상황인가요

    • 네네 해당 프로그램이 가지는 알고리즘을 OllyDBG와 같은 디버거를 통해서 리버싱을 해서 그 알고리즘을 알아낸 후 그걸 알고리즘을 제가 다시 프로그래밍을 통해 구현한거니 '올리디버거로 연산 루틴을 거치고 나서의 출력값인가요?'라는 질문에 '예'라고 대답할 수 있겠네요. 근데 이 과정에서 프로그래밍 실력이 부족해 추후에 직접 제거해야하는 부분이 있어서 그걸 [-9:-5]를 통해서 필터링한 것이구요

  • cococo 2015.11.02 01:50

    네 감사합니다. 저는 그 추후에 직접제거해야되는 부분들을 어떻게알았고 (예: 연산 끝난 뒤 print hex(esi) ) , 어디서부터 어디까지가 키값길이인지 어떻게 아신건지.. 알고싶어서요 저는 리버싱하면서 브루트포싱이란자체에서 완전 탁막혀서 어떻게해야될지 정말모르겠네요 ㅠㅠ

    • 저도 저 문제를 만진지 오래되서 정밀한 답변은 못드리지만, 브루트포싱 자체가 알고리즘을 모르고 무차별로 아무 값이나 넣어서 답을 찾는 것에 반해 이 경우에는 디버거를 통해서 알고리즘을 알아낼 수 있기 때문에 그 알고리즘을 이해하면 왜 제가 저렇게 코드를 짠 건지 충분히 알 수 있을거에요

  • cococo 2015.11.02 02:14

    어떻게 그 위치를 아는지는 연산을 진행하다보면 어느 부분이 필요한지 알 수가 있으니 그 값만 추출해서 비교하도록 하는 것입니다. 이 답변을 해주셨는데 ㅠㅠ 연산을 진행하다보면 어느부분이 필요한지 알수있다고하셨는데 예시를 들어주실수있나요 ㅠㅠ 정말 제가 못해서.. 그러네요.. 죄송합니다..

    • 죄송해할 필요없어요ㅎㅎ, 필요한 부분을 어떻게 아냐고 질문하셨는데, 보통 키 값을 구하는 문제의 경우 어셈블리어로 CMP 명령어를 통해서 값이 노출되는 경우가 많죠. 그럼 그 CMP 명령어에 올라오는 값이 어느 부분에서 생성이 된건지, 어느 부분에서 온건지 계속 역으로 추적(노가다)하다보면 어느 곳이 필요한지 알게 될거에요. 그 프로그램에 대한 이해도가 높아진다고 하는 것도 비슷한 표현이겠네요

  • cococo 2015.11.02 02:57

    감사합니다. ㅠㅠ 그.. "음 따로 무슨 방법이라기 보다는 그냥 알고리즘에 있는 값이 연산된 후에 출력될 때 xxxx00001111bbbbcccc와 같이 불필요한 부분이 있어서 거기서 필요한 값만 추출할 수 있도록 문자열 출력 범위를 지정해준거에요." 하셨는데요 불필요한부분인지 필요한부분인지 어떻게 아셧죠..

    • 원래 어셈블리의 경우 DWORD일 때 4바이트만을 담고 그 4바이트를 넘어가는(자릿수가넘는) 경우에는 넘어간 부분을 담지 못하고 남아있는 부분만 담고 있어요. 그래서 그 4바이트의 값만을 끌어다 비교하도록 프로그래밍 해야하기 때문에 [-9:-5]를 넣은 것입니다. 나머지 부분은 해당 프로그램에서 초과한 값이여서 갖지 않는 값이기때문이죠

  • http://fedorax.tistory.com/ 여기 사진올렸는데 한번봐주실래여..??

  • 저 사진에서 [-9:-5]하면 5d88이나오는데.. 어떻게 [-9:-5]를 할수있는지,, 처음하는사람에게는 [ ? : ? ] 가될텐데 어떻게 알수있는지..

  • Cococo 2015.11.02 09:49

    문제 키값 알고리즘연산출력값들이
    2118885d8844366
    9358538528844253이런식으로 나오는데 어떻게 -9:-5위치에 있는걸 아셨는지 그게궁금해요

  • Cococo 2015.11.02 09:49

    문제 키값 알고리즘연산출력값들이
    2118885d8844366
    9358538528844253이런식으로 나오는데 어떻게 -9:-5위치에 있는걸 아셨는지 그게궁금해요

    • 아 이거는 그냥 글자 2개를 넣어서 디버거를 통해 비교문에 쓰이는 값들을 기억해둔 후, 프로그래밍을 통해 또 같은 값을 넣은 후에 나온 값을 비교해서 어느 위치에 있는지를 비교했죠

  • Cococo 2015.11.02 09:52

    -9:-5 필터링을 했다면 그곳에있다는걸 알기위한 과정을 설명해주세요 다른건다알것같아요.. 제가 필력이딸려서.. 전달력이 부족했나보네요..

  • Cococo 2015.11.02 11:34

    간절하게 저 슬라이싱부분에대해서만
    쉽게풀어서 설명해주신다면 정말 .. 감사합니다
    고맙습니다 죄송합니다 절드립니다..

    • 예를 들어 0xFFFFFFFF + 0xFFFFFFFF = 1FFFFFFFE가 되는데 이 경우 4바이트를 초과하기 떄문에 맨 앞의 1이라는 자릿수가 잘려나가요. DWORD라는 자료형이 그 크기를 다 담을 수 없기때문이죠. 어셈블리에서는 잘려나가는게 저절로 되지만, 위의 코드에서는 슬라이싱부분에서 그 작업도 진행하고, 나온 값에서 앞의 4글자만 키 값으로 추후에 쓰이기에 4글자만 자른거구요

  • Cococo 2015.11.02 11:57

    글자 2개를 넣어서 디버거를 통해 비교문에 쓰이는 값들을 기억해둔 후,
    그 비교문이 키값4바이트인가요?
    프로그래밍을 통해 또 같은 값을 넣은 후에 나온 값을 비교해서 어느 위치에 있는지를 비교했죠
    브루트포스코드에 넣나요 그리고 어떻게비교한거죠??
    쉽게풀어서설명해주시면ㅠㅠ

  • Cococo 2015.11.02 12:19

    그부분을알아내는게 쉬운일인가요?

    • 음 해당 부분을 찾는거는 리버싱 실력에 따라 차이가 커서 알아내는데 쉽고 어려움은 말씀드리기가 어렵네요.

  • Cococo 2015.11.04 08:38

    감사합니다 ㅠㅠ 말씀해주신답들이 다 맞는내용이네요 ㅠㅠ 제가 경험이부족해서 이해를잘못했네요 .. 친절한 답변감사합니다.ㅠㅠ

  • dext 2017.09.12 18:29

    전...복호화하기 귀찮아서 분기 싹다 패치하고 무차별로 답을 구했는데.. 이 방법이 정도인것 같내요.. 블로그 출처 표시하고 참조좀 해도될까요.

문제확인


우선 문제를 확인하면 이전에도 풀려고 했지만 결국 풀지 못했던 문제로 기억을 한다. 하지만 그 때에는 분명 리버싱을 처음 접했을때로 기억을 하기에 당시보다는 잘 풀 수 있겠지라는 자신감으로 리버싱을 시작하였다. 

우선 UPX 패킹을 해제하고 리버싱을 계속 진행하면 된다.




풀이


사실 이 문제는 정말 많이 삽질을 한것 같다. 출력되는 남은 군생활의 값을 증가시켜주는 부분을 찾았고 그 주위에서 CMP를 통한 비교문을 찾기 위하여 많은 노가다를 하였지만 결국 삽질도 여러번 하다가 문득 생각 난 것이 바로 치트엔진이였다. 

사실 치트엔진을 통한 문제 풀이가 과연 리버싱에 도움이 되는 것인지는 모르겠지만 이참에 치트엔진 공부도 새롭게 같이 시작을 해보려한다. 우선 아래와 같이 치트엔진을 통하여 변화하는 값을 캐치한다.

위의 사진과 같이 치트엔진을 통하여 우선 해당 N번째의 값인 10을 입력해주고 그 다음은 Increased value를 통하여 두개의 값만 남았다. 여기서 첫번째 주소로 이동을 하여 확인을 해볼 것이다. 주소는 02583DB0로 이동을 한다. 그러면 위의 덤프창에서와 같이 0xA의 값이 존재하는 것을 확인 할 수가 있다.


위의 사진과 같이 해당 덤프에 BP를 건다. 위와 같이 ON ACCESS를 하는 것이 적당할 것이다. 그 후 BP를 통하여 계속 진행을 하다보면 아래와 같은 지점이 나타난다. 이 지점에서 우리가 현재 N번쨰인 B와 316을 비교를 한다. 이를 통해 우리는 남은 군 생활 수가 0x316 일수도 있다는 것을 확인 할 수가 있다. 이제 확인을 위하여 명령어를 바꾸어 보자.


0x316보다 클 경우에는 해당지점으로 점프를 한다. 그렇다면 우리는 확인을 위하여 JL을 JMP로 바꾸어 볼 것이다. 아래와 같이 바꾼 후에 진행을 할 경우 결국 RETN 8을 끝으로 프로세스가 종료 되는 것을 확인 할 수가 있다.

이러한 작업을 통하여 우리가 찾은 비교문이 올바른 지점이라는 것을 확인 할 수가 있었다. 이러한 방법을 통하여 문제를 풀 수가 있다. 치트엔진을 이용해서 더 쉽게 풀 수가 있었던 것 같다. 하지만 치트엔진보다 더 쉬운 방법이 있는데 바로 아래와 같은 디컴파일러를 이용하는 것이다. 디컴파일러를 이용하면 바로 1 TO 790(0x316)을 확인 할 수가 있다. 하지만 이는 실력 향상에는 안좋을 것 같다. 이러한 방법이 있다는 것만 잊지 말자.


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

CodeEngn Advance 09  (0) 2015.07.12
CodeEngn Advance 08  (50) 2015.07.01
CodeEngn Advance 06  (0) 2015.06.28
CodeEngn Advance 05  (0) 2015.06.28
CodeEngn Advance 04  (0) 2015.06.28
CodeEngn Advance 03  (0) 2015.06.28

Comment +0

문제확인


이번 문제는 너무 설명 할 것도 없는 것같다.




풀이


아래와 같이 vbaStrCmp를 찾으면 쉽게 시리얼을 찾을 수가 있다. Name에 따른 알고리즘 형성으로 인하여 키 값이 형성 되는 것이 아니라 그냥 시리얼 값을 찾는 것이기에 너무 간단히 끝나버렸다.



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

CodeEngn Advance 08  (50) 2015.07.01
CodeEngn Advance 06  (0) 2015.06.28
CodeEngn Advance 05  (0) 2015.06.28
CodeEngn Advance 04  (0) 2015.06.28
CodeEngn Advance 03  (0) 2015.06.28
CodeEngn Advance 01  (0) 2015.06.27

Comment +0