티스토리 툴바


2011/12/24 13:49 System

[+]Introduction


아주 간만에 포스팅입니다.

1,2주 안하다보니 한달이 되고 두달이 되고..

 

<Fig0. 하라는 공부는 안하고!>

좀 더 심도있는 내용을 올리고 싶었는데 그게 또 핑계거리가 되서 블로그에 글을 안쓰고 있네요.

그래서 간단한 내용이라도 올리고자 합니다.

Global Hooking 을 거는 손쉬운 방법으로는 SetWindowHookEx가 있죠.

Windows 에서 자동으로 Injection을 시켜주기 떄문에 Process의 생성을 catch 해서 따로 Injection 해줄필요도 없구요.

이 부분에 관해서는 Reversecore님 Blog에 아주 자세히 설명되어있기 떄문에 참조하시길 바랍니다.

제가 이번에 쓸 내용은 SetWindowsHookEX를 이용해서 Hooking 을 걸고 UnHookWindowHookEx를 이용하여 해제했는데도 

박혀있는 DLL 을 강제로 Ejection 시키는 방법입니다.

[ 추후 UnHookWindowsHookEx를 이용하지 않고 Hooking을 해제하는 방법도 올리도록 하겠습니다 :D 

Windows7 에 올라와서 물리 메모리에 접근하는게 좀 까다로워져서 고생중이네요. Driver 없이 User 모드에서 가능한지를 살펴보고 있습니다

XP는 되는데 음.. ]



[+]Problem



UnHookWindowsEx만 호출한 경우 대부분의 Process에서는 DLL이 Ejection 되지 않습니다.

[ 물론 해제가 되는 Process도 있습니다. Process 내부에서 Message 처리를 열심히 하는 녀석들이죠.

대부분 UI 가 사용자에게 보여지고 있는 상태의 녀석들이구요. ]

보통 UnHook 후에 SendMessage 를 Broadcasting 하게 뿌려서 Process에 해제하게 되죠.

이 Message는 의미있는 Message 일 필요는 없습니다. 

Message가 오면 Hook Chain을 알아서 갱신하니까요.

아무튼 이렇게 Message 를 뿌려줘도 DLL이 빠지지 않는 Process가 존재하게 됩니다.

대표적으로 AdobeARM.exe Chrome의 Rundll32.exe가 있죠.

물론 항상 빠지지 않는건 아니고 간혹 빠지지 않는 현상을 보게 됩니다.

이런 Process들은 대부분 단일 Thread라는 점을 확인할 수 있는데요.

정리하자면

1. UnHook할 때는 Message를 뿌려서 받은 Process들은 해제가 된다.

2. UnHook 되지 않는 녀석들은 대부분 단일 Thread이다.

3. 위 Process 들도 항상 DLL이 Ejection 안되는건 아니다.

이 부분에서 문제점을 유추할 수 있습니다. 

Process에 외부에서 들어온 Message를 처리할 Thread가 없으면 DLL이 Ejection 되지 않는다.

라는 점입니다.



[+]Solution


그렇다면 위 문제는 어떻게 해결해야 할까요?

많은 분들이 FreeLibrary를 CreateRemoteThread를 이용해서 삽입하는 방법을 생각하셨을겁니다.

흠, 하지만 이 방법에는 문제점이 있는데

FreeLibrary시에 해당 DLL에 접근하고 있는 Thread가 있으면 그대로 프로그램이 뻗어버린다는 것이죠.

DLL Unload 뭐 이런 Error가 났던걸로 기억합니다.

결국 DLL이 자동으로 Ejection 되게 하려면 Message를 처리하도록 만들어야한다는거죠.

Message 처리 루틴 Thread를 Process에 삽입하고 다시 Message를 보내볼까요?

너무 번거롭습니다.

생각해보면 Process 자체에서 Ejection 해주는게 아님을 파악할 수 있습니다.

빈 Message를 받았을 때 Hook Chain에 없는 DLL을 빼주는게 Process 자체 역할을 아니니까요.

Process는 단순히 Message를 받는 것이고 이 때 발생한 특정 Event를 Windows에서 받고 Ejection 해준다고 판단 내릴 수 있겠군요.

그렇다면 직접 Message를 발생시키면 어떨까요?

Code Injection을 통해서 DLL이 빠지지 않는 Process에서 Message를 발생하도록 하는거죠.

아래 Code Inejction Code는 Reversecore님의 Code를 상당부분 인용했습니다.

http://www.reversecore.com/82 

먼저 사용할 함수를 정의합니다.




다음은 실행코드입니다.

함수 호출에 필요한 String을 먼저 삽입하고 그곳을 참조하는 방법입니다.



SendMessage를 쓰기 보다 SendMssageTimeOut을 썼고

Parameter 간소화를 위해 Broadcasting을 합니다.

위와 같은 방법을 이용하면 DLL이 정상적으로 잘 Ejection되는 것을 확인하실 수 있을겁니다.

어때요 참 쉽죠?


글이 올라온 날이 12월 24일이라는 건 신경안쓰셔도 됩니다








저작자 표시
Posted by LinkC

2011/07/16 21:45 WarGame

[+] Introduction

 


 이번에 소개할 문제는 VM 관련 문제 입니다.

VM을 이용한 기술은 VCP [ Virtualized Code Protection ] 이 있으며 해당 기술은 Protector 에 많이 쓰이곤 합니다.

언뜻 봐도 " Code를 Virtualize 하여 Protect 한다 " 라는것을 의미한다는거라고 쉽게 추측 할 수 있죠.

상용 Packer 인 Themida에 실제로 쓰이고 있는 기술이기도 하구요.

물론 Themida에는 VCP 뿐만 아니라 여러가지 Anti Reversing 기술들이 들어가있습니다만..

 이 포스팅에서는 VM만을 다루도록 하겠습니다.

 * VM이 적용되면 Reversing 하는 입장에서는 상당히 귀찮아집니다.

뒤에서 설명하겠지만 Assembly Language 로 한줄씩 실행되기 떄문에 

전체적인 구조를 살펴보려면 Script를 짜거나 손으로 직접 모아야 하기 떄문이죠.

디버거로 열어봤는데...


<Fig 0. VM이 딱 ! >


 <Fig0-1. 5초후에 빡침이 딱!>



[+] Problem


<Fig1. Bin500을 실행 시켰을 때 >

 

먼저 Debugger로 문자열을 확인해보면

key File로 짐작되는 문자열 'codegate.key' 를 찾을 수 있습니다.

적당히 아무내용이나 집어넣어서 재실행 시켜보면

역시 유효하지 않은 파일이라고 하는군요.

<Fig2. 해당 파일이 올바르지 않을 때 메세지> 

이런 류의 문제라면 해당 Key 파일을 검사하는 부분을 우회해도 

암호가 나오지 않고 그 파일을 가지고 연산을 하기 떄문에

해당 파일의 내용을 정확히 복구를 해야 합니다.

 파일을 열어 Verify 하는 부분을 찾으려 해도 도통 보이지 않는군요.

이 문제의 핵심인 Verify 부분이 VM에 의해 실행되기 때문이죠.


[+] Analysis

 
그렇다면 Debugger로 확인을 해봅시다. 

<Fig3. VirtualAlloc을 통해 특정 메모리 영역을 할당받는 모습>

 해당 영역을 확인해보면 아시겠지만

애초에 VM 영역에서 시작하네요.

후에 VirtualAlloc 을 호출하고 특정 영역에  RWE 권한을 모두 줍니다.

계속 따라가보면 VirtualAlloc을 한번 더 호출하고 .. [ 같은 크기, 다른 위치에 RWE 권한을 줍니다 ] 

이 할당한 영역에 특정 명령어를 수행하는 것을 확인 할 수 있습니다.

<Fig4. VirtualAlloc에 할당된 영역에서 코드를 실행하고 있는 모습>
 
그 후에 다시 0x40ABBD로 Return해서 특정 루틴을 반복합니다.


<Fig5. Code를 가져와 Decrypt하는 부분>

0x0040A94A를 호출함으로써 실행할 Code를 복호화 하는데

해당 함수를 살펴보면 다음과 같습니다.


<Fig6. Fig5의 Decrypt함수 내부>

 

사실 위 함수가 문제를 푸는데 큰 도움이 되는 건 아닙니다.

CPU가 해석 할 수 있는 일반 기계어로 복호화 하는 과정이지

Key 파일의 내용을 확인하는 과정이 아니기 떄문이죠.

하지만 VM의 동작 방식은 확인을 하고 갑시다.

대략적인 VM의 구현은 다음과 같습니다.

<Fig7. VM의 동작 방식>

실제 문제 파일을 Debugging 해보면 아시겠지만

text 영역과 VM 영역이 번갈아 가며 실행되는 것을 볼 수 있습니다.

Sensitive 한 곳은 VM 처리가 되어있는거죠.



즉 우리가 원하는 것은 ' Codegate.key 파일을 입력 받고 난 후 실패 메세지가 나오기 전까지의 연산 과정 ' 입니다.

이것은 1Byte 씩 검사하여 다르면 바로 실패로 return 하는 과정일수도 있고

전체 내용을 모두 검사한 후 최종적으로 실패를 판단할 수도 있습니다.

물론 전자가 훨씬 까다로워집니다. 실행중에 비교 값을 바꿔야하니까요.

ReadFile에 BP를 걸고 해당 파일을 읽어 온 후에 Buffer에 접근 하는 부분을 잡아보면

최종 Code가 실행되는 곳을 잡을 수 있습니다.

이곳을 보면 Fig4에서 할당한 곳과는 다른 공간임을 알 수 있습니다.

즉, 복호화된 Code가 실행 되는 부분은 2군데이며 

1. 일반 적인 Code가 실행되는 부분

2. Key 파일의 내용을 검사하는 부분


이 되겠습니다.

두 부분을 모두 BP 걸고 확인해보면 아시겠지만 Call , JMP 문을 찾아 볼 수 없다는걸 알 수 있습니다.

Call이나 JMP문이 가고자 하는 곳은 VM에서는 외부 , 즉 원본 프로그램의 text 영역입니다.

이 부분으로 가려면 해당 주소에 맞는 연산이 또 별도로 필요하죠.

문제 풀이엔 별로 중요한 내용이 아닙니다만,

'VM은 이런식으로 구현되고 있다' 라는 것은 염두해두시는게 좋습니다.



[+] Solution



이 다음부턴 딱히 설명 드릴게 없습니다.

Script를 짜거나 손으로 직접 코드를 바꾸시면서 진행을 하셔야 합니다.

운이 나쁘게도 앞서 설명드린 1Byte 씩 검사하고 아닐 경우 실패로 간주하는 타입이군요.

Script로 모으기 짜증 + 1  이 되었습니다.

아무튼 해당 Code를 모으면 다음과 같은 모습입니다.

Pydbg의 HardWare BP를 이용한건데 가변길이라 그런지 

다음 명령어까지 받아오더군요.. 쩝..


<Fig8. Xor, Cmp 부분 중 일부를 긁어온 모습>

이 부분을 예로 들자면

al 로 가져온 값을 xor 0x46 하고 이 값을 cmp 합니다.

xor  [ Data  ] , 0x46

cmp ( [ Data ] ^ 0x 46 )  , 0xf0

즉 Data 의 값은 0x46 과 Xor를 했을 때 0xF0 가 나오는 0xB6 이 되겠네요.

이런 식으로 Codegate.key 값을 하나씩 복원합니다.

Codegate key 값이 총 100 Byte 니 벼..별거 아니군요.

Xor 부분과 cmp 부분을 parsing 매 Byte마다 바꿔주면

다음과 같은 원본 데이터를 얻을 수 있습니다.

<Fig9. 원본 Key File >

이를 codegate.key 에 저장하면 


<Fig10. 해독된 Key File>

이게 정답은 아니겠죠?

100Byte의 16진수가 뭘 의미 할까요.

흠 분포도를 보면 문자를 의미하는건 아닌거 같습니다.

그리고 저 출력 방식도 의심해볼만 하죠

4 * 25 형식입니다.

또, 0x80 0x00 이 유독 눈에 많이 띄는걸 볼 수 있는데..

일단 이를 binary 값으로 바꿔보았습니다.


<Fig11. Binary 값으로 바꾼 모습>



<Fig11-1. !?>
왜 4 * 25 형식으로 출력했는지 알겠네요.

0x80이 binary 값으로 1000 0000 이니까

뒤의 0을 떼버리면 25 * 25 형식의 값이 완성됩니다.

여기에 모서리 부분을 보면 1과 0이 규칙적으로 나열되어있는데

전 이걸 QR 코드라고 판단했고 이를 만들어보면..



QR Code Reader로 읽어보니

I_am_in_C0d3gat3_2011!!

이라는 답이 나오는군요.















저작자 표시
Posted by LinkC

2011/05/18 16:31 System

[+] Introduction


 Pydbg. 요즘들어 주목 받고 있는 Script 언어 중 하나인 Python의 Debugging Module 입니다.

Python 언어 자체가 심플하고 이해하기 쉽기 때문에 이를 이용하나 Pydbg도 상당히 접근하기 용이합니다.

물론 Python의 고질적인 문제인 속도는 안고 가야 할 문제입니다만..

이번 포스팅에서는 Pydbg의 Snapshot 기능이 가지고 있는 문제점에 대해서 써볼까 합니다.

Snapshot 기능에 대해 간단히 설명하자면 말그대로 특정 지점의 정보를 Snapshot 찍듯이 찍고

Restore를 통해 찍은 시점으로 돌아가는 거라고 보시면 되겠네요.

예를 들어서 금요일 저녁에 Snapshot을 찍어놓고 

일요일 저녁에 저장해뒀던 Snapshot으로 Restore 하는거죠.

...


<Fig0. 울지마라>

상당히 유용한 기능입니다.

특정 루틴을 반복하는데 좋겠죠? 

아무튼 해당 기능을 사용하는데 약간 문제가 있는데요,

첫번째는 Pydbg File 자체를 수정하지 않고 일어나는 문제점을 해결 하는 방법이고

두번째는 Pydbg File 자체를 수정하는 방법입니다.

간단하기로는 후자가 간단합니다. 단 두글자만 수정하면 끝나요.

포스팅은 전자에 중점을 두고 기술하도록 하겠습니다.




[+] Problem


snapshot의 사용방법은 간단합니다.

pydbg의 객체의 process_snapshot() method를 호출해주면 되죠

복구는 process_restore() 입니다.

그리고 해당 method를 호출해주기 전에

모든 thread를 멈추고 snapshot해야 한다고 pydbg의 제작자 pedram amini 가 명시하고 있습니다

그러니까 이런 식으로 해야겠죠.

SnapShot

dbg.suspend_all_threads()

dbg.process_snapshot()

dbg.resume_all_threads()


Restore 

dbg.suspend_all_threads()

dbg.process_restore()

dbg.resume_all_threads()



예제를 통해 문제를 살펴보죠.

간단한 문자열을 출력하는 다음과 같은 코드가 있습니다.

여기서는 puts를 이용하였으나, printf 문도 동일한 문제가 일어납니다.


*해당 문제는 VS2010의 msvcr100.dll 에서는 발생하지 않습니다만

Windows의 기본 C- Runtime Libary인 msvcrt.dll 을 썼을때는 해당 문제가 발생합니다.

확인해보면  msvcr100.dll에서는 별도의 예외처리가 들어갑니다. 

해당 예외 원인은 뒤에서 설명하도록 하죠.




<Fig1.  예제 프로그램>

Friday nigth에 snapshot 을 찍고

Sunday night 에 restore를 해보면

정상적으로 작동하지 않는걸 확인해볼 수 있습니다.

<Fig2. 정상적으로 출력되지 않고 종료된 모습>
 
나머지도 출력이 되지 않는걸로 봐선 Handling 되지 않는 예외가 발생하여 프로그램이

죽었다고 보면 되겠죠.

발생하는 예외의 Call Stack을 확인해보면

EnterCriticalSection 이후에 발생하는 것을 볼 수 있습니다.

예외가 나는 경우를 확인해보면 항상 같은 곳에서 발생하지는 않습니다만

EnterCriticalSection 함수에 진입하고 나서 발생하는것은 변함이 없습니다.

EnterCriticalSection?

동기화를 위해 사용되는 방법중 임계영역[  Critical Section ] 을 이용하는데 사용되는 함수입니다.

단일 Thread 프로그램인데 무슨 동기화냐 하시는분이 계실지도 모르겠는데

기본적인 Console 출력 함수들은 모두 동기화 작업을 거칩니다. 



왜 이런 문제가 발생할까요?


[+] Analysis


예외 원인을 보면 Access Violation이 주로 일어납니다.

일단 Snapshot method가 호출되었을 때 저장되는 내용을 한번 살펴보겠습니다.


<Fig3. 각 Thread의 Context를 get_thread_context를 통해 저장한 모습>

Context에는 어떤 내용이 저장되는고 하니

<Fig4. WinNT.h에 있는 CONTEXT Structure의 구조>
 

해당 함수에서는 CPU Register를 저장합니다. 

이후 메모리를 돌며 쓰기가 불가능한 곳과 실행파일 이미지를 제외한 영역을 저장합니다.


 <Fig5. 쓰기 가능한 메모리 영역을 저장하는 부분>

 바로 이곳이 문제입니다.

 stack 값이나 Register 영역 값은 모두 정상적으로 복구되는데 실행 파일 이미지에 저장되는 변수는 그대로 남아있는 것입니다.

 즉, data 영역에 저장되는 전역변수가 문제가 될 수 있다는 거죠.

Critical Section의 구조를 살펴봅시다.

<Fig6. Critical Section 구조체의 모습>

첫번째 member를 보면  _RTL_CRITICAL_SECTION_DEBUG 이라는 Linked List 형태의 구조체를 가리키고 있는

포인터 임을 확인 할 수 있습니다.

자 그렇다면 정리해봅시다.

전역변수로 지정된 Critical Section 구조체는 Restore 되어도 변하지 않습니다.

그대로 남아있다는 소리죠.

그런데 해당 구조체가 가리키고 있는 _RTL_CRITICAL_SECTION_DEBUG 구조체는 

Restore 되면 Snapshot 될 때의 상태로 돌아가게 됩니다.

즉 , InitializeCriticalSection이 호출되기 전 상태로 돌아간다는 말입니다.

여기서 문제가 일어납니다.

전역 변수를 참조해보았을때 , 이미 Initialize 된 Critical Section 이 있는 것을 확인됩니다.

이를 사용했더니 실제로는 Initialize 되지 않는 Critical Section 이라는 거죠.

그 포인터가 가리키는 쓰레기 값을 가지고 연산을 했으니 Error가 나는게 당연했겠죠?

여기서 프로그램마다 메모리 값이 다르니 예외가 발생하는 곳도 달랐을 겁니다.

정리하자면, 해당 변수는 정상적으로 Restore 되지 않았는데 가리키는 영역은 Restore 되었다는 거죠.

물론 EnterCritialSection 안에서 나는건 공통적입니다만..

위 사항은 Console 출력 함수에서 적용되는 문제입니다.

Image 영역에 있는 메모리에 있는 변수에 접근하는 모든 것이 문제가 될 수 있습니다.

예를 들면 File Pointer 연산이 있겠죠




 [+] Solution  

 
1.  Snapshot을 찍을때 Image 영역까지 합니다.
 
가장 심플하고 간단하죠.

모든 전역변수도 정상적으로 변경이 되구요

Pedram Amini가 왜 Image 영역을 막아뒀는지는 모르겠습니다만

pydbg를 보면 필요하면 해제하라고 명시되어있습니다.

흠.. 다른 문제가 생길 수 있을지는 좀 더 봐야 할거 같네요.


2. 해당 변수의 snapshot 상태로 직접 돌려준다.

Console 출력 함수들의 경우 해당 Critical Section 영역을 0으로 덮어버리면 알아서 다시 할당하기 때문에

문제가 일어나지 않습니다.

File Pointer의 경우 현재 위치를 가리키고 있는 포인터를 원하는 위치로 돌려주는 식으로 우회가 가능하겠습니다.

좀 번거롭죠. 사용하는 함수마다 이렇게 해 줘야 하는 불편함이 있구요.



3. Image 영역에 쓰는 변수에 접근하지 않는다.

.... 

네 피하면 되는겁니다.

핵심적인 부분만 Snapshot , Restore 합니다. 

지역변수 연산만 한다면 이 방법이 먹히겠죠.

어떠한 수정도 없지만 그만큼 한계가 있는 방식입니다.



<Fig7. 이게 꿈이라고 말하지 말아주세요>

 


저작자 표시
Posted by LinkC
이전버튼 1 2 3 4 5 ... 54 이전버튼

블로그 이미지
LinkC

태그목록

Tistory Cumulus Flash tag cloud by BLUEnLIVE requires Flash Player 9 or better.

공지사항

Yesterday75
Today23
Total63,625

달력

 « |  » 2012.05
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    

최근에 받은 트랙백

글 보관함


. .