본문 바로가기

My Study/Reversing

Themida 2.1.2.0 Virtual Machine

리버싱을 하는데 있어서 가장 우리를 난감하게 만드는 놈은 바로 안리 리버싱이 걸려있는 바이너리 입니다.
이러한 안티리버싱 기술을 자동적으로 바이너리에 덮어씌워주는 놈은 바로 프로텍터들입니다. 대표적인 프로텍터로는
VMProtect 나 Themida 같은 프로그램들이 있습니다.

두 프로텍터는 역시 명성에 걸맞게 강력한 안티리버싱 기술들이 구현이 되어 있는데 그 중 하나는 Virtual Machine 기술입니다. 쫌 더 직관적으로 말하면 코드 가상화 기술입니다. 사실 아직 제대로 분석을 해보진 못하였지만 차차 분석을 해봐야되므로 간략히 그에 대한 코드를 분석해보았습니다. 전 일단
Themida 2.1.2.0 버전에 있는 Virtual Machine 기능을 살펴보았습니다. 


더미다의 Virtual Machine 옵션입니다.

코드 가상화라 함은 원래의 명령어를 가상화를 시키고 가상화된 코드를 본체에 있는 하드웨어적인 CPU가 처리하는 것이 아닌 개발자가 만든 소프트웨어적인 핸들러가 처리를 하게 되는 것입니다. 그 때의 핸들러를 보통 가상화된 명령어를 처리하는 CPU라고 하는데 위 그림에서 Processor Specifications 에서 지정하는 것이 바로 그 소프트웨어적인 CPU를 어떻게 만들 것인지에 대한 옵션입니다. 일단 전 위와 같이 옵션을 지정하였고 위 옵션은 디폴트 옵션보단 조금 더 강력한 옵션입니다.

옆의 Entry Point Virtualization 옵션은 엔트리포인트로부터 몇 개의 명령어를 가상화 시킬 것인가를 지정하는 옵션입니다.
위와 같이 지정하면 2Byte가 아닌! 2개의 명령어가 가상화 되는 것입니다.

그 외로는
API 함수에 대한 가상화 레벨을 지정할수도 있고 Multi Branch Techology는 그냥 분석을 더욱 어렵게 하기 위해 분기문을 마구 꼬아놓는 그러한 옵션 같습니다.

아무튼 위와 같은 옵션으로 패킹을 진행하겠습니다. 먼저 원본 파일의 Entry Point를 봐보겠습니다. 
 

익숙한 Entry Point 죠?? 아무튼 이번엔 패킹된 파일의 Entry Point를 봐보도록 하겠습니다. 
 

옵션대로 위 2개의 명령어가 사라져있고 그 자리엔 쓰레기 값이 채워져 있습니다. 사실상 저 부분은 가상화가 되서 더이상 제어가 저 위치로 오지도 않을 뿐더러 실행도 하지 않습니다. 그렇기 때문에 그냥 저 부분엔 아무 값이나 채워놔도 무방합니다. 그 코드는 그러면 어디서 실행이 되냐구요?? 그건 가상화된 명령어를 실행해줄 소프트웨어적인 CPU가 수행해 주겠지요.

또한 가상화된 명령어는 메모리 어딘가에 마구마구 인코딩되서 짱박혀 있겠지요.

제가
간략히 분석해본 더미다 가상화 기술입니다.


 먼저 Entry Point에서 두개의 명령어는 이미 가상화가 되어서 Virtualized Code 상에 존재할 것이고 해당 위치에는 쓰레기 코드가 있습니다. 그리고 Virtualized Code 부분에는 Code Section에 있는 가상화된 명령어만 있는게 아닌 Themida의 원래 기능인 코드, 리소스 등 이런 부분들 인코딩, 압축 되어 있는 것을 디코딩, 압축 해제 시키는 코드도 있는데 그 부분 또한 전부 가상화가 되어서 Virtualized Code 부분에 있는 것 같네요.

그리고 빨간색 화살표는 진행 방향인데요. Themida는 EntryPoint에 있는 CALL 명령어 부분만 가상화 했지 CALL 내부는 가상화 하지 않았습니다. 그렇기 때문에 가상화된 코드를 Virtual CPU가 돌리는 동안 Code Section부분으로 넘어와 Normal Code를 실행 후 다시 가상화된 코드를 실행시키는 것입니다. 이 때 CPU 제어 이동이 있겠지요. 

 가상화된 코드는 Virtual CPU가 실행시키고 Normal Code는 하드웨어적인 제 컴퓨터론 Intel CPU가 처리를 하게됩니다. 

간단하게 실제 코드 상으로 한번 봐볼까요?? 
 
더미다 패킹 된 프로그램의 EntryPoint입니다. 이제부터 쭉쭉 인코딩된 코드를 디코딩 시킵니다...

계속 실행하다보면 무한반복하는 부분이 생기는데 아마 명령어를 fetch해서 해석하는 부분이겠지요.


저 부분에서 Virtual CPU의 Instruction Handler를 호출합니다.. EAX를 Index로 사용하네요.
( EDI : 0x0044A5E8, EAX : 0x00000057 )
EDI를 봐보겠습니다.

빨간색 네모친 부분이 Virtual CPU에서 사용하는 레지스터 입니다.
또한 그 아래 노란색 네모친 부분이 Instruction Handlers 입니다.
EAX가 Index가 되서 저 Handler 들 중 하나로 점프를 하게 되는 것입니다.
개수는 676개입니다.

하지만 Themida Virtual Machine 옵션을 낮게 주고도 해보았는데 차이점이 있었습니다.
위 가상화 기술 그림은 옵션을 약하게 줬을 경우이고 높게 주었을 경우에는 아래와 같이 됩니다.
두 번째로 실행되는 Virtual CPU에서 실질적인 Code 섹션에 있는 가상화된 코드를 실행해줍니다.

뭐 더욱 분석이 복잡해 졌네요.

아직 각각의 Instruction Handler 들이 어떤 식으로 작동하는지는 분석을 안해봐서 모르겠네요. 하지만 분명히 하나의 Intel 명령어를 처리하기 위해서 여러개의 Virtualized Code가 수행되는 것은 분명합니다.

JMP xxxxxxxx
와 같은 명령어 하나만 처리를 하더라도 
 
이 부분이 무려 116번이나 호출된 것을 보면 알 수 있겠지요.
아마 코드를 가상화 시키면서 까지 거기에 쓰레기 코드 또한 많이 넣어놨을 것입니다.

또한! Instruction Handler들 내부에도 전부 쓰레기 코드왕창에 난독화도 무지되어있습니다.
또한! 더미다 패킹을 하게되면 생성되는 난독화 방법이나 쓰레기 코드들도 달라지며 opcode 순서도 바뀌게 되며 레지스터 순서도 바뀌고 아무튼 싹다 바뀌더군요. 하나를 제대로 분석했다 하더라도 다른 파일 분석이 힘든.. ㅠ_ㅠ 

이번 글에서는 Themida 가상화 기술 관련해서 살짝만 맛 보았습니다.
왠지 제가 원하는 방법으로 구현이 되어 있는것 같아서 마음에 드네요.

나중엔 더욱 깊이 분석해보고 다른 패커에 쓰인 가상화 기술도 분석해봐야겠습니다. 

=========== 2월 7일 추가 분석 ============
 VM CPU로 제어가 넘어오는 부분이 있네요
 
일단 이 부분이 VM Entry로 들어오는 루틴입니다. 빨간색으로 표시된걸로 봐서 앞 코드에 의해 디코딩이 되었나봅니다.
그리고 저 루틴을 들어오기 전에  


이러한 부분도 있는데 잘 보시면 0x0041E7D2로 VM Entry로 점프하는 루틴이 많은것을 볼 수 있습니다.
그리고 각 JMP하기전에 어떠한 값을 push를 하는데 push 하는 값의 용도는 확실히는 모르겠습니다.
예상으론 외부로 나갔다가 들어오면서 다시 가상화된 코드 부분을 돌리는데 해당 부분을 뜻하는 것 같습니다. 

각 push에 bp를 걸고 trace를 해본 결과 5번 정도 LoadLibrary를 호출하고 GetLocalTime 함수도 호출을 하더군요.
아래서 다섯번째 JMP 문이 code영역에 nop처리 된 부분에 원래의 api함수로 채워넣고 안티 디버깅 기술이 걸렸다면 여기서 걸릴 뿐더러 안걸릴 경우 code 영역으로 들어가 정상적인 코드 실행까지 하게 됩니다. 저 JMP문으로 들어갔을 때의 동작방식을 알아봐야겠군요. :-)

그리고....
VM Entry를 들어왔을 때 

이 부분 저 부분에서 ESI가 가리키는 메모리 영역에서 1Byte씩 al로 가져옵니다. 또한..


이 부분하고 1:1로 한번씩 번갈아가면서 실행... 핸들러 수행이 끝나면 LODS 명령어로 점프를 하게 됩니다.
JMP문은 그 가상화된 명령어를 수행하기 위한 핸들러를 호출하는 부분이구요.

그러면 저기서 LODS했을 때의 AL(1)과 JMP문의 EAX(2)가 같냐? 그건 아닙니다. 뭐 같은 필요는 없겠죠. 꼬아놨다면요.
하지만...!! 값이 다르더라도 1:1 매치가 되면 좋지만 1:1 매치가 아닙니다. 다대다 관계 더군요. 즉..!

(1)이 2c,f9,f6이 나왔을 때
(2)는 62 만 계속 나올 때도 있습니다.

그 반대의 경우도 마찬가지구요.

(2)가 62일 경우 (1) 값을 계속 뽑아 50개를 뽑아봤는데.. 규칙이 없습니다.

그래서 트레이스해본 결과 수행되는 루틴이 다를줄 알았는데 (1)에서 (2)로 가는데 까지의 루틴은 항상 같습니다.
중간에 메모리에 있는 값을 가져와 연산을 수행하는 부분에서 달라지겠군요. 이 부분도 자세히 분석을 해봐야겠군요.

조금 더 자세히 분석해본 결과
(1) 의 값은 도데체 어디에 영향을 미치는지는 잘 모르겠습니다.
(1) 값은 핸들러 Index를 만드는 과정에 영향을 끼치지는 않더군요. 

그러면 핸들러 인덱스를 누가 만드느냐??
위위 그림에서 PUSH EBX를 하고 있는데 저 EBX의 BL 값에 의해 인덱스가 만들어집니다.
그러면 BL값 또한 무작위인데 핸들러 인덱스가 같은 경우도 있지 않냐?? .. 라고 의문을..?

바로
BL + Handler Index 
의 계산으로 인해 만들어집니다...만;;
이게 전부는 아닌 것 같군요. 특정 핸들러가 수행되면 EBX값이 BL부분만 바뀌는게 아닌 EBX 전체 값이 바뀌어버리게됩니다.
더 이상은 너무 시간이 걸려 ....

아래는 분석 결과..
a = push ebx할 때의 bl 값
b = jmp할 때 handler index
c = jmp루틴 왔을 때 바뀐 bl값

전부다는 아니지만
c == a + b
가 성립하고 있는 것을 알 수 있습니다.

BL(a) AL(b) BL(c)
--------------------------
8E A9 37
37 B5 EC
EF 62 51
51 55 A6
A6 39 DF
C8 B5 7D
83 62 E5
E5 3D 22
22 B5 D7
DD 62 3F
3F 60 9F
9F 8A 29
2C 62 8E
8E A9 37
37 B5 EC
EC 62 4E
4E 60 AE
AE 62 10
10 55 65
65 A9 0E
0E 39 47 

'My Study > Reversing' 카테고리의 다른 글

백신 Avast의 Anti-attach 우회하기  (11) 2012.04.19
NtSetInformationThread 를 이용한 Anti-Debugging  (0) 2012.04.17
ARM CPU 공부시작.. ㅠ_ㅠ  (6) 2012.01.25
Code Virtualized  (9) 2011.06.27
ZwSetSystemInformation  (2) 2011.01.11