2017. 8. 23. 01:05ㆍ0x02 Reverse Engineer/0x01. CodeEngn
|
문제 : Find the Serial when the name is CodeEngn
바이너리 실행 :
|
[그림 1] 바이너리 실행 |
|
[그림 2] Borland Delphi |
바이너리 컴파일 한 언어는 볼랜드였다. 볼랜드도 C기반이라고 들었던 것 같은데..
검색을 해보고, 링크를 걸어두었다.
https://en.wikipedia.org/wiki/Borland_C%2B%2B
올리디버거로 까보자.
볼랜드도 엔트리포인트는 4번대 주소였다. 그럼 c언어를 모토로 삼는 언어는 다들 유저 메모리 영역이 4번대인가보다.(exe)
|
[그림 3] 처음 보는 Borland 구조 구경중~ |
win32api와 다른건 없다. 똑같이 모듈을 얻어오는 GetModuleHandle, 윈도우 창을 세팅해주는 SetWindow, GetDC, ReleaseDC 이런게 다 있다.
대신 트레이싱으로 분석한 바로는 보통 바이너리를 열면 그 위치에 윈도우 함수 세팅이 있는데, 볼랜드의 경우는 함수를 하나 더 진입해야 윈도우 관련 세팅 함수가 존재했다. 이건 최적화의 유무일수도 있지만, 이때까지 분석한 바이너리와 비교했을 때는 분명히 다른 조건이기 때문에 메모하는 차원에서 끄적여본다.
문제 크랙만 해결하는 것도 중요하지만, 한 줄 한 줄 독서하듯이 어셈을 읽어가면서 숨겨진 의미를 찾는 것도 리버스 엔지니어링이 아닐까 하고 생각해본당 ㅋㅋㅋ
그냥 내 생각임.... 어쩔? This is my way ~
0x458B14에서 Step over 하니까 바이너리가 dll을 주르륵 만나면서 풀려버린다.
그럼 0x458B14에 Step into 해서 놀아봐야지?
그럼 00458B14 . E8 1B87FEFF call 15.00441234
0x00441234가 OEP임을 알 수가 있다.
|
[그림 4] 실제 바이너리가 실행되는 위치(메인) |
모든 그림체에도 순서가 있듯이 윈도우 화면이 그려지는대도 순서가 있다는 것을 알 수 있는 아주 아주 기막힌 증상을 찍었다.
|
[그림 5] 크랙 분석과는 아무 연관이 없는 삽질 (하지만, 모르고 지나치면 아쉬울 수 있는 부분) |
|
[그림 6] GetMessage 처럼 자원이 안들어오면 계속 무한루프다.... 무한루프임 ! 데드락 아님!!!!!!!!! |
je -> jne로 패치하여 강제로 빠져나가보자. (난, 다음 흐름이 궁금하지 이 루틴에서 놀고 싶지가 않아...)
쭉쭉 흐름따라 구름따라 가게 되면 이런 구문이 나온다. 딱봐도 에러란다.
잘못된 트레이싱을 해서 벌 받았다!
004036FE |. BA 24904500 mov edx,15.00459024 ; ASCII "Runtime error at 00000000"
00403703 |. B8 18B24500 mov eax,15.0045B218
00403708 |. E8 D31C0000 call 15.004053E0
0040370D |. E8 511C0000 call 15.00405363
00403712 |. EB 13 jmp short 15.00403727
00403714 |> 6A 00 push 0x0 ; /Style = MB_OK|MB_APPLMODAL
00403716 |. 68 44904500 push 15.00459044 ; |Title = "Error"
0040371B |. 68 24904500 push 15.00459024 ; |Text = "Runtime error at 00000000"
00403720 |. 6A 00 push 0x0 ; |hOwner = NULL
00403722 |. E8 11DBFFFF call <jmp.&user32.MessageBoxA> ; \MessageBoxA
재 분석 들어간다.
해결하고 싶었는데, 너무 많은 시간을 투자하기엔 오늘 할 게 아직 많아서 패스.. (귀찮아서 일걸?)
0x00458B14에 BP걸고 Step over하니 정상적으로 바이너리가 풀린다.
난 그 풀리는 전체 과정이 궁금했는데, 시간되면 꼭 다시 할거다.
지금 Name과 Serial에 뭘 넣든 간에 더이상의 루틴을 디버깅 하면으로 보지 못하고 상태 값이 Terminated가 될 것이다. 이유는 네임과 시리얼의 정답 유무를 체크하는 루틴을 아직 찾지 않았고, 관련 사항에 bp를 걸지 않았기 때문이다.
오잉, 틀렸습니다. ㅋㅋㅋㅋ
바이너리가 GUI환경이고, Check it 이라는 button을 클릭하고 새로운 서브 윈도우가 생기기 때문에, 서브윈도우와 메인 윈도우가 꺼지지 않는다면 Terminated가 안되게 코드를 짜두었나보다.
그럼, 노답이니.... bp 찾아 떠나자.
하 여행가고싶다..비행기 1달 뒤면 타러간다 ㅋㅋㅋㅋ
다시 분석으로 돌아오자. 오늘 뭔가 기분이 좋은지 헛소리가 많은 포스팅이다.
버튼클릭 시 출력되는 메시지박스의 lptext들에 대해 구경하자.
|
[그림 7] 버튼 이벤트 발생 시 나오는 출력문들 |
저 문구들이 보이는 곳에 반드시!! 분기에 도달하기 위한 CMP가 존재할 것이다.
여윽시 존재한다...
|
[그림 8] 성공과 실패에 접근할 수 있는 cmp 구문 발견 |
상기 사진에서 EAX는 어떻게 받아올까 에 대한 답변을 해보자.
CMP위의 명령은
0045882C |. E8 43EFFAFF call 15.00407774
이것인데, CALL 0x00407774 이놈은 누가 봐도 함수다.
우리의 함수는 반환형을 가지거나, 반환형을 가지지 않거나로 나뉘어진다.
지금 eax와 [0x45B844]를 비교하는 것으로 보아
0x45B844주소 안의 값과 EAX를 비교한다는 말과 일맥상통하게 되는데
EAX가 정수이기때문에 가능한 일이다.
그렇다면 우리는 call의 함수를 다음과 같이 슈도코드로 정의할 수 있겠다.
int 00407774 ( ) // 슈도코드라서 가능하다 함수나 변수의 맨 앞에는 숫자가 올 수 없다. 하지만, 컴파일 될 때는 다음과 같은 값이 가능하다. 왜냐? 주소이기 때문이다.
우린 지금 함수명을 기입하고 있기 때문에 숫자가 되면 컴파일이 되지 않게 되는 것이다.
{
return EAX;
}
아직 우리는 0x00407774 함수에 접근하지 않았으므로 구체적인 로직은 궁금해하지 않아도 된다. 궁금해하지말아요~~ (이수 노래 좋다)
그럼 다시 코드로 돌아와서
00458831 |. 3B05 44B84500 cmp eax,dword ptr [0x45B844]
00458837 |. 75 1B jnz short 15.00458854
00458839 |. B8 88884500 mov eax,15.00458888 ; ASCII "You cracked the UBC CrackMe#1 ! Please send your solution to ubcrackers@hotmail.com !"
0045883E |. E8 29C1FEFF call 15.0044496C
00458843 |. BA E8884500 mov edx,15.004588E8 ; ASCII "CRACKED"
00458848 |. A1 3CB84500 mov eax,dword ptr [0x45B83C]
0045884D |. E8 9ECDFCFF call 15.004255F0
00458852 |. EB 0A jmp short 15.0045885E
00458854 |> B8 F8884500 mov eax,15.004588F8 ; ASCII "Try Again !"
00458859 |. E8 0EC1FEFF call 15.0044496C
cmp의 destination과 source의 차가 0이 아니면 0x00458854로 점프
자 이제 결과 루틴은 봤으니 결과를 제공하는 루틴을 살펴봐야 한다.
|
[그림 9] 분석하러 들어가자 고고 |
|
[그림 10] 분석을 위한 바이너리 값 |
00407774 /$ 55 push ebp
00407775 |. 8BEC mov ebp,esp
00407777 |. 83C4 F0 add esp,-0x10
늘상 sub esp 만 봤찌만, 이놈도 esp의 위치에서 인덱스를 정하기 때문에 프롤로그로 봐도 무방할 것 같다.
0040777E |. 8955 F8 mov [local.2],edx
00407781 |. 8BD8 mov ebx,eax
00407783 |. 33C0 xor eax,eax
eax에 내가 입력한 aaaaa가 있다.
00407794 |. 8BC3 mov eax,ebx
다시 eax로 옮긴다.
00407796 |. E8 D9B1FFFF call 15.00402974
그닥, 의미 있는 루틴이라고 생각되지는 않는다.
|
[그림 11] ... ?? |
|
[그림 12] 잘못된 값일 경우, 그 값을 lptext에 대입하여 출력시킴 |
|
[그림 13] 더 자세히 분석하기 위해 코드 패치 |
cmp eax, dword ptr 부분으로 리턴 될 수 있게 되었다.
이 문제는 특별한 루틴이 존재하지 않는걸까? 단순한 하드코딩과 주소 내의 값을 비교하는 거였는가..??
[0x45B844] 가 *flag 가 되는거고
flag를 따라간 주소 공간의 값이 6061이 되는거??
너무 허무하지않나...
고친 jmp를 je로 다시 바꾸고 루틴을 run하면 0x458831로 안가길래
이 주소를 hardware bp를 걸어보았다.
그래도 여전히 에러가 뜬다.
여기서 놓친 부분이 있다. 힌트가 존재했던 것이다!!
'aaaaa' is not a valid integer value.
이 부분은 reference text strings 에 잡히지 않았다.
너무 궁금해서 아는 형에게 물어봤는데 '유니코드' 라고 딱 말해주셨다..
오우 눈썰미를 키워야겠다 ㅋㅋ 그리고 메모리 윈도우에 들어가서 유니코드와 아스키코드를 둘 다 찾을 수 있는 법을 알려주었다
alt+m -> ctrl + b
|
[그림 14] memory window -> dump |
aaaaa는 문자열이기 때문에 10진수가 아니었단 말임.
그럼 십진수로 테스트를 진행하자.
|
[그림 15] 개발자가 요구하는 십진수를 입력했다~~~~ |
bp 걸어둔 함수로 진입해서 처음 하는 일은 test 명령어를 만나서 아스키코드 12345가 12345로 그대로 유지 되어 je를 만족하지 못하고 그 다음 명령어로 내려간다.
0040297A |. 85C0 test eax,eax
0040297C |. 74 73 je short 15.004029F1 ; test 연산 결과 0이 아니므로 점프 x
0040297E |. 31C0 xor eax,eax
00402980 |. 31DB xor ebx,ebx
여기서 다 0으로 바꿈
[esi]는 12345이다
00402987 |> /8A1E /mov bl,byte ptr [esi]
00402989 |. |46 |inc esi
0040298A |. |80FB 20 |cmp bl,0x20
0040298D |.^\74 F8 \je short 15.00402987
byte만큼 bl에 옮기므로 [0]~[4] 를 비교하는것이고,
만약 bl이 0x20 (' ')이면 00402987로 점프
0x20이 아니므로 다음 명령 ㄱㄱ
0040298F |. B5 00 mov ch,0x0
00402991 |. 80FB 2D cmp bl,0x2D
00402994 |. 74 69 je short 15.004029FF
00402996 |. 80FB 2B cmp bl,0x2B
00402999 |. 74 66 je short 15.00402A01
0040299B |. 80FB 24 cmp bl,0x24
0040299E |. 74 66 je short 15.00402A06
004029A0 |. 80FB 78 cmp bl,0x78
004029A3 |. 74 61 je short 15.00402A06
004029A5 |. 80FB 58 cmp bl,0x58
004029A8 |. 74 5C je short 15.00402A06
004029AA |. 80FB 30 cmp bl,0x30
004029AD |. 75 13 jnz short 15.004029C2
왠지
00402987 |> /8A1E /mov bl,byte ptr [esi]
00402989 |. |46 |inc esi
0040298A |. |80FB 20 |cmp bl,0x20
0040298D |.^\74 F8 \je short 15.00402987
얜 배열 초기화 였던 것 같다. 아님말구
flag[ ] = {0}; 이랄까
flag = (int *) calloc(갯수 * sizeof(int)) 이랄까
그냥 스페이스 들어있는지 확인하는거일걸?
다시 명령어로 돌아오면
내가 입력한 값 flag[0] = 0x31 ('1') 과 개발자가 세팅한 0x2d와 비교를 한다.
if (flag[0] == '-') 면 0x4029FF로 점프하는데 다르니까 다음 명령어 쭉쭉쭉 내려간다.
00402991 |. 80FB 2D cmp bl,0x2D
00402994 |. 74 69 je short 15.004029FF
00402996 |. 80FB 2B cmp bl,0x2B
00402999 |. 74 66 je short 15.00402A01
이까지는 다른 주소로 점프인데
0040299B |. 80FB 24 cmp bl,0x24
0040299E |. 74 66 je short 15.00402A06
004029A0 |. 80FB 78 cmp bl,0x78
004029A3 |. 74 61 je short 15.00402A06
004029A5 |. 80FB 58 cmp bl,0x58
004029A8 |. 74 5C je short 15.00402A06
얘는 같다.
왜 그런지 볼까?
"2D2B2478587858".decode("hex") '-+$xXxX' 부호와 특수문자 알파벳의 소문자 대문자가 보인다. 내가 찾아야 하는 건, 숫자다. 아스키 코드표를 잠시 참조 해보겠다.
|
00402A06 |> \BF FFFFFF0F mov edi,0xFFFFFFF 00402A0B |. 8A1E mov bl,byte ptr [esi] 00402A0D |. 46 inc esi 00402A0E |. 84DB test bl,bl 00402A10 |.^ 74 DF je short 15.004029F1 00402A12 |> 80FB 61 /cmp bl,0x61 00402A15 |. 72 03 |jb short 15.00402A1A 00402A17 |. 80EB 20 |sub bl,0x20 00402A1A |> 80EB 30 |sub bl,0x30 ; Switch (cases 30..46) 00402A1D |. 80FB 09 |cmp bl,0x9 00402A20 |. 76 0B |jbe short 15.00402A2D 00402A22 |. 80EB 11 |sub bl,0x11 00402A25 |. 80FB 05 |cmp bl,0x5 00402A28 |.^ 77 D0 |ja short 15.004029FA 00402A2A |. 80C3 0A |add bl,0xA ; Cases 41 ('A'),42 ('B'),43 ('C'),44 ('D'),45 ('E'),46 ('F') of switch 00402A1A 00402A2D |> 39F8 |cmp eax,edi ; Cases 30 ('0'),31 ('1'),32 ('2'),33 ('3'),34 ('4'),35 ('5'),36 ('6'),37 ('7'),38 ('8'),39 ('9') of switch 00402A1A 00402A2F |.^ 77 C9 |ja short 15.004029FA 00402A31 |. C1E0 04 |shl eax,0x4 00402A34 |. 01D8 |add eax,ebx 00402A36 |. 8A1E |mov bl,byte ptr [esi] 00402A38 |. 46 |inc esi 00402A39 |. 84DB |test bl,bl 00402A3B |.^ 75 D5 \jnz short 15.00402A12 00402A3D \.^ EB A9 jmp short 15.004029E8 00402A3F . C3 retn 여기로 점프하는데 일단 신경 쓰지 말아보자 |
저 위의 코드들은 다 je로 비교하는데 얜 왜 jnz로 비교를 하고 있을까에 대해 생각해보았다.
이게 정답이다. 6160은 HEXA DECIMAL 입력한 값 : 12345 -> 레지스터에 보이는 값 3039(HEX) 그렇다면 정답은????? ^,^ |
호기심이 안드나?? 코드엔진 사이트에서 CodeEngn이라고 친절하게 Name을 알려줬는데 다른 Name일 때도 해보면, 이게 어떤 루틴이 적용되는지 알 수 있지 않겠는가????
이제 이름부분 알아보러가자.
이 함수를 유심히 보면 된다.
00458760 /$ 55 push ebp 00458761 |. 8BEC mov ebp,esp 00458763 |. 6A 00 push 0x0 00458765 |. 53 push ebx 00458766 |. 56 push esi 00458767 |. 57 push edi 00458768 |. BB 44B84500 mov ebx,15.0045B844 ; ASCII "@=" 0045876D |. BE 48B84500 mov esi,15.0045B848 00458772 |. BF 40B84500 mov edi,15.0045B840 00458777 |. 33C0 xor eax,eax 00458779 |. 55 push ebp 0045877A |. 68 F3874500 push 15.004587F3 0045877F |. 64:FF30 push dword ptr fs:[eax] 00458782 |. 64:8920 mov dword ptr fs:[eax],esp 00458785 |. 8D55 FC lea edx,[local.1] 00458788 |. A1 3CB84500 mov eax,dword ptr [0x45B83C] 0045878D |. 8B80 CC020000 mov eax,dword ptr [eax+0x2CC] 00458793 |. E8 28CEFCFF call 15.004255C0 00458798 |. 8B55 FC mov edx,[local.1] 0045879B |. 8BC7 mov eax,edi 0045879D |. E8 9AB0FAFF call 15.0040383C 004587A2 |. 33C0 xor eax,eax 004587A4 |. 8903 mov dword ptr [ebx],eax 004587A6 |. 8B07 mov eax,dword ptr [edi] 004587A8 |. E8 B7B2FAFF call 15.00403A64 004587AD |. 85C0 test eax,eax 004587AF |. 7E 19 jle short 15.004587CA 004587B1 |. C706 01000000 mov dword ptr [esi],0x1 004587B7 |> 8B17 /mov edx,dword ptr [edi] 004587B9 |. 8B0E |mov ecx,dword ptr [esi] 004587BB |. 0FB6540A FF |movzx edx,byte ptr [edx+ecx-0x1] 004587C0 |. C1E2 03 |shl edx,0x3 004587C3 |. 0113 |add dword ptr [ebx],edx 004587C5 |. FF06 |inc dword ptr [esi] 004587C7 |. 48 |dec eax 004587C8 |.^ 75 ED \jnz short 15.004587B7 004587CA |> 8B07 mov eax,dword ptr [edi] 004587CC |. E8 93B2FAFF call 15.00403A64 004587D1 |. C1E0 03 shl eax,0x3 004587D4 |. 0103 add dword ptr [ebx],eax 004587D6 |. 8B03 mov eax,dword ptr [ebx] 004587D8 |. C1E0 02 shl eax,0x2 004587DB |. 8903 mov dword ptr [ebx],eax 004587DD |. 33C0 xor eax,eax 004587DF |. 5A pop edx 004587E0 |. 59 pop ecx 004587E1 |. 59 pop ecx 004587E2 |. 64:8910 mov dword ptr fs:[eax],edx 004587E5 |. 68 FA874500 push 15.004587FA 004587EA |> 8D45 FC lea eax,[local.1] 004587ED |. E8 F6AFFAFF call 15.004037E8 004587F2 \. C3 retn
|
'0x02 Reverse Engineer > 0x01. CodeEngn' 카테고리의 다른 글
[CodeEngn Basic 16] (0) | 2018.02.09 |
---|---|
Basic 15번. (0) | 2018.02.09 |
[CodeEngn Basic 13] Find the Answer feat. C# (0) | 2017.08.20 |
[CodeEngn Basic 12] Can you replace? (0) | 2017.08.20 |
[CodeEngn Basic 11] Do you know StolenBytes? (0) | 2017.08.20 |