반응형

Import REConstructor 1.7c.zip
0.58MB
lordpe.zip
0.43MB

 

주제

- API Redirection

- API 리다이렉트


 

일반적인 Rebuild Imports 기능과 API Redirect 이라는 기능을 알아보는 레벨이다.

 

이전 레벨에서는 단순이 old IAT를 가리키는 Import 테이블을 수정하여 새로 생성했지만, 이번 레벨에서는 진짜로 IAT를 새로 만들어 볼 것이다.

 

API Redirection(API 리다이렉션)은 패커/프로텍터에서 IAT/Import 테이블을 부분적으로/완전히 망가뜨리는 일종의 액션이다.

 

IAT 대신에 각각의 리다이렉트된 API에 대해 커스텀 포인터를 생성한다.

 

즉, 패커가 패킹된 바이너리에서 사용하는 시스템 DLL 내에 존재하는 API 주소들을 관리하는 것이다.

 

 

Ollydbg에 파일을 열면 위와 같이 경고 창이 뜬다.

 

이 파일은 패킹된 바이너리이기 때문에 뜨는 경고창으로, 패킹된 명령어를 해석하는 것은 그리 좋은 선택은 아니기 때문에 아니오를 선택한다.

 

 

그러면 위와 같이 뜨는데 PUSHAD 명령을 통해 모든 레지스터의 값들을 저장하고 있는 것으로 보아 ESP Trick을 사용하여 언패킹을 진행할 수 있다.

 

PUSHAD 명령을 실행하면 ESP 레지스터 값이 변경된다.

 

 

 

F8 키를 한 번 눌러 PUSHAD 명령을 실행하면 ESP 레지스터 값이 변경된다.

 

ESP 값이 19FF58 주소인데 4byte 값을 드래그 하여 오른쪽 마우스 -> Breakpoint -> Hardware, on access -> Dword 선택하여 하드웨어 BP를 설치한다.

 

 

F9 키를 눌러 실행하면 44C731 주소의 pop eax 명령에서 멈추게 된다.

 

위의 사진을 보면 44C730 주소의 POPAD 명령어가 실행 되자마자 BP 조건으로 인해 멈추게 된 것이다.

 

F7 키를 눌러 44C733 주소에 있는 CALL EAX 명령어를 실행하면 

 

 

 

위와 같이 OEP 부분이 나온다.

 

이 상태에서 Ollydump 플러그인을 이용해 덤프를 뜨는데

 

 

Rebuild Import 옵션을 해제하고 덤프를 뜬다

 

덤프를 뜨고 나면 Import REC 툴을 이용해 IAT를 복구해야 한다.

 

 

Imoprt REC 툴에서 IAT를 복구 할 프로세스를 선택하고

 

 

위와 같이 OEP 값에 331B8을 입력해준 후 AutoSearch 버튼을 눌러주면 RVA와 Size 값이 자동으로 수정된다.

 

 

그리고 Get Imports 버튼을 누르면 위와 같이 dll 파일들을 불러오는데 3개의 dll 파일이 valid:NO 상태이다.

 

로그를 보면 총 17개의 미해결된 포인터가 존재한다.

 

원인은 리다이렉트된 API 주소를 제대로 찾지 못해서이다.

 

당연히 이런 것들을 고치기 전에 바이너리를 수정하는 것은 의미가 없다.

 

valid:NO 상태인 것들이 존재하지 않는 코드를 가리키고 있으면 그냥 잘라내는 방법을 통해 고칠 수도 있겠지만

 

 

Imoprt REC 툴에서 오른쪽에 있는 Show Invalid 버튼을 누르면 위와 같이 invalid 상태인 부분들을 보여주는데

 

주소를 보면 "ptr:" 옆에 실제 값들(458C35, 457BF6)을 가리키고 있다.

 

디버거에서 조금 더 분석을 해본다.

 

 

위에서 봤던 457BF6 주소는 SFX 섹션 영역에 위치한다.

 

 

또 다른 valid:NO 상태였던 458C35 주소를 디버거에서 보면 위 사진과 같은데

 

 어떤 함수의 함수 프롤로그 주소인 것을 알 수 있다.

 

위 루틴이 바로 리다이렉트 된 API(FindClose) 주소를 관리하는 부분이다.

 

 

위 루틴의 아래쪽을 보면 또 다른 API 주소를 관리하는 루틴(458C5C)을 볼 수 있다.

 

또 다른 API 함수 (FindNextFileA) 주소를 관리하는 코드 루틴으로써 FindClose 부분과 명령어가 비슷하다.

 

그런데 위 사진들의 코드를 보아도 별 문제를 발견하지 못했다.

 

그냥 간단하게 모든 섹션을 덤프해버리면 위 사진에서 보여줬던 루틴이 API 함수의 리다이렉트된 주소를 관리하게 될 것이다.

 

 

프로그램을 재시작(Ctrl + F2) 한 뒤 이전에 보았던 FindClose API 함수의 주소를 관리하는 주소로 이동해보면 아무런 값이 존재하지 않는 것을 볼 수 있다.

 

리다이렉트 코드는 바이너리가 실행될 때 언패킹 루틴에서 작성되기 때문에 OEP를 찾아서 덤프해버리면 위 사진과 같이 특정 API 들의 주소가 찾아지지 않아 크래시가 발생하는 것이다.

(리다이렉트 코드를 찾을 수 없기 때문이다.)

 

 

이전에 발생했던 문제를 해결하기 위해서 다른 것들도 알아봐야 한다.

 

먼저 Import REC 툴에서 IAT 값을 추출하는데

 

IAT는 VA 438000 == RVA 38000 주소에서 시작한다.

 

 

위의 사진 주소의 API의 리다이렉트 코드는 458C35 주소에 위치하고, API 주소는 RVA 38040이다.

 

해당 주소를 디버거의 엄프 창에서 보면 위와 같다.

 

 

덤프 창에서 보여지는 값이 84B3CBD6인 것을 알 수 있다.

 

IAT가 위 사진의 주소로부터 약간 이전에서 생성되긴 하지만 분석할 때 valid:NO 상태인 17개의 API들은 그냥 바이너리에서 패커가 사용한 코드로 리다이렉트 되고 있다.

 

 

정리해보면, IAT가 언제 생성되는지 알아야 하고, 패커가 API의 원본 주소 대신 리다이렉트 시킬 주소를 작성하는 코드가 언제 어디서 위 주소에 작성하는지 알아내야 한다.

 

즉, 17개의 API 주소에 대해 조사를 해야 하므로 먼저 첫 번째 API 리다이렉트 주소 부분에  하드웨어 BP on write를 설치한다.

 

위 자신의 주소 438040에 하드웨어 BP on write를 설치하면, 어디에서 438040 주소에서 API 주소가 작성되는지 알게 된다.

 

프로그램은 바이너리에 존재하는 모든 Import들을 찾아내기 위해 딱 2개의 API 함수가 필요하다.

- LoadLibraryA

- GetProcAddress

 

각각의 API 함수에 대해서는 검색하면 나오니 자세한 설명은 생략하는데 쉽게 말하자면

LoadLibraryA  함수 - 특정 DLL을 호출하는데, 호출에 성공하면 해당 DLL의 핸들이 반환된다.

GetProcAddress 함수 - 호출하고자 하는 Import 된 각각의 API 함수 주소를 알아낸다.

 

 

 

BP 설치 후 프로그램을 재실행(Ctrl + F2) 한 뒤 몇 번 실행(F9) 해보면 위 주소 4536A6에서 멈추게 된다.

 

 

이때 덤프 창을 보면 이전 84B3CBD6 값이 새로운 76B33AF0 값으로 덮어씌워져있다.

 

해당 값이 바로 API 함수의 원본 주소이다.

 

 

F8 키를 눌러 트레이싱 하다보면 4536F5 주소에 오게 되는데 

 

4536F0 주소의 CALL 453E90 명령에 의해 453E90 주소의 함수가 실행되면 API 함수의 원본 주소가 있던 438040 주소의 값이 위와 같이 458C35로 바뀐다.

 

원본 주소 대신에 리다이렉트 된 주소가 씌여진 것인데, 그렇다면 결국 453E90 주소의 함수가 리다이렉트와 관련된 함수인 것을 알 수 있다.

 

 

453E90 주소의 함수를 분석하기 전에 위 사진을 보면 4536DF 주소의 JE 명령에 의해 리다이렉트 관련 함수(453E90)를 호출하는지 안하는지 결정한다.

 

 

어쨌든 리다이렉트 관련 함수 453E90 주소로 가면 위와 같다.

(Ctrl + F2 키를 눌러 재실행 후 F9 키와 F8 키를 이용해 위치한다.)

 

 

F8 키를 눌러 453EF2 주소까지 트레이싱을 한 뒤 상태 창을 보면 위와 같은데

 

EAX 레지스터에는 첫 번째 API 함수가 저장될 438040 주소에 작성되는 리다이렉트 주소가 존재하고

 

[ECX] 레지스터의 주소값에는 원본 API 함수 주소가 존재한다.

 

 

453EF2 주소의 명령이 실행되면 위와 같이 438040 주소에는 458C35 값이 덮어씌워지게 되는데

 

이는 API 함수의 원래 주소가 새로운 주소인 리다이렉트 주소로 덮어씌워지는 것이다.

 

패커를 조금 더 분석해보면 한번에 하나의 DLL 만을 선택하고 IAT 부분에 올바른 API 주소를 작성한다.

 

작성한 다음에는 해당 주소가 리다이렉트 될 수 있는지 없는지 판단하고, 판단 여부에 따라 추가 명령어들의 실행이 끝나면 모든 Import 함수들을 불러올 때까지 다음 DLL을 선택하여 동일한 작업을 수행한다.

 

 

453EF4 주소에서 주소가 변경되는데 해커는 주소를 리다이렉트 시키기 위해 VirtualProtect 함수를 사용한다.

 

 

위의 사진을 보면 VirtualProtect 함수를 호출하면서 정상적인 실행을 위해 페이지 액세스 권한 관련 값을 세팅한다.

 

VirtualProtect 함수는 호출하는 프로세스의 가상 주소 공간 내에 커밋된 페이지의 액세스 보호 설정을 변경한다.

 

이 함수는 VirtualProtectEx 함수와는 다른 것인데, 해당 함수는 모든 프로세스의 액세스 보호 설정을 변경하는 함수이다.

 

그렇기 때문에  VirtualProtect 함수를 이용해서는 커밋된 페이지에 대해서만 설정을 변경할 수 있고, 만약 해당 페이지가 커밋되지 않았다면 함수 호출은 실패하게 된다.

 

 

변경되는 값은 VirtualProtect 함수의 목표 페이지의 BaseAddress 액세스 값인데, 현재의 경우는 438040이다.

 

 

이제 API 함수 주소들이 리다이렉트 되지 않게 패치를 해야 하는데, 패치 하는 것에 있어서 다양한 방법을 이용할 수 있다.

 

위의 사진에서 453EC8 주소의 JE 분기문이 API 주소 리다이렉트와 관련된 453E90 주소의 함수를 호출하는 곳으로 분기할지 안 할지 결정하는 분기문이였는데

 

 

위의 사진처럼 JMP 명령으로 변경해도 되지만 다른 방법이 있기 때문에 다른 방법부터 설명한 뒤 453EC8 주소를 패치한다.

 

 

453EC8 주소에 위와 같이 하드웨어 BP를 설치 후 트레이싱을 통해 몇 가지를 더 조사한다.

 

F8 키를 눌러 트레이싱을 하면서 VirtualProtect 함수가 어떻게 오리지널 액세스 캐릭터 값을 복구하는지 설명한다.

 

 

트레이싱하여 453EF7 주소까지 오면 현재 VirtualProtect 함수 대상 페이지에 적용된 기존의 액세스 설정 값은 위 사진과 같다.

 

 

F8 키를 눌러 453F02 주소까지 오면 VirtualProtect 함수 대상 페이지에 새롭게 적용할 액세스 설정 값은 40h으로 PAGE_EXECUTE_READWRITE이다.

 

그리고 453EFC 주소에서 그 다음 인자값으로 4byte가 들어가고

 

453F01 주소에서 마지막 인자값으로 VirtualProtect 함수 대상 페이지의 주소 공간이 들어가게 되는데 arg.1에는 438040 주소가 담겨있기 땜문에 438040 주소가 인자값으로 넘어가게 된다.

 

 

F8 키를 눌러 계속 트레이싱 하다 보면 위와 같이 453E90 주소의 함수가 종료되고 다른 주소로 분기되는 4536F8 주소로 오게 된다.

 

 

F8 키를 눌러 분기하고 트레이싱을 계속 하다보면 위와 같이 GetProcAddress 함수를 호출하는 것을 볼 수 있는데, 다음 API 함수로 lstrcmpiA 함수를 호출하고 있다.

 

 

F8 키를 눌러 트레이싱 하다 보면 위와 같이 4536DF 주소의 JE 명령을 만나게 되는데 이 분기문도 리다이렉트 함수(453E90)와 관련된 부분으로 점프한다.

 

 

이어서 F8 키를 이용해 트레이싱 하면 4536F0 주소의 함수를 호출하는데 해당 함수를 호출하면

이전에 API 함수 주소(458C35)는 리다이렉트 주소가 들어갔지만 방금 들어간 lstrcmpiA 함수의 주소는 원래 주소가 들어간다.

 

그렇다면 결국 리다이렉트와 관련된 함수 453E90 주소의 함수만 호출하지 않으면 전부 원본 주소가 세팅될 것을 예상할 수 있는데

 

즉, 정리하자면 패치를 하기 위해서는 크게 2가지 방법이 존재한다.

1. 리다이렉트 함수 호출과 관련된 분기문(이전에 봤던 453EC8 주소의 JE 명령)을 패치하는 것

2. 리다이렉트 관련 함수(453E90) 호출하는 4536F0 주소의 call 명령을 NOP 처리해서 그냥 막아버리는 것이다.

 

 

쉽게 첫 번째 방법을 따라 Ctrl + F2 키를 눌러 재실행 후 F9 키와 F7 키를 이용해 453E90 주소로 온 뒤

 

453EC8 주소의 JE 명령을 위와 같이 JMP 명령으로 바꾸고, F9 키를 누르면 위와 같이 438000 주소부터의 값들이 모두 원본 주소로 세팅된다.

 

 

Import REC 툴을 이용해 IAT를 수정하는데 대상 바이너리를 다시 열고 OEP 값을 이전에 알아냈던 값 331B8로 변경 후 AutoSearch 버튼을 누르면 RVA와 Size 값이 자동으로 설정되는데 이 상태에서 Get Imports 버튼을 누른다.

 

 

그러면 디버거에서 리다이렉트 부분을 패치했기 때문에 Import 되는 모든 DLL 모듈들이 전부 valid : YES 상태가 되어야 하는데 여전히 Valid:NO 상태들이 보인다.

 

Window10 환경에서 해서 그런가 싶어 Windows XP 환경에서 위의 내용들을 토대로 다시 해봤다.

 

 

Windows XP 환경에서 두 가지 패치 방법 중 첫 번째 패치 방법을 통해 했을 때 위와 같이 438000 주소부터의 값들이 모두 원본 주소로 세팅되었고

 

 

Import REC 툴에서도 모두 Valid:YES 상태가 되었다.

 

 

Fix Dump 버튼을 눌러 덤프 떴던 파일을 선택하면

 

 

위와 같이 성공했다는 로그가 뜨고

 

 

덤프 뜬 파일을 실행했을 때 위와 같이 잘 실행된다.

 

 

Lord PE에서 PE Editor 기능을 이용해 IAT 복구가 적용된 덤프 파일의 섹션을 정리한다.

 

PE Editor에서 위와 같이 파일을 선택해주면 위와 같이 Header 내용이 창이 뜬다.

 

 Sections 버튼을 누른다.

 

 

그러면 위와 같이 섹션 창이 뜨는데 맨 마지막에 .mackt 섹션이 새로운 IAT를 포함하고 있기 때문에 기존의 Name 값이 4, 5 ,6번인 섹션은 삭제해도 무방하다.

(섹션 선택 -> 우클릭 -> wipe section header)

 

 

그러면 위와 같이 총 5개의 섹션만 남게 되는데 창을 닫고 OK 버튼을 누른 후 Rebuild PE 버튼을 누른다.

 

 

그리고 위와 같이 IAT 복구가 적용된 덤프 파일을 선택하여 적용하면

 

 

72%나 사이즈가 줄어들었다고 한다.

 

 

사이즈가 줄어든 프로그램을 실행하면 위와 같이 잘 실행이 된다.

 

 

반응형

+ Recent posts