심플스 - 프로그램과 책 이야기로 가득한 곳, (Simples.Kr)

  

유저 레벨에서 API Hook 2부

Windows Research 조회 수 13736 추천 수 0 2010.09.03 17:01:25
 

2부에서는 API Hooking을 하는 실무 기술을 다루지 않고 리버싱을 통한 Windows 내부적인 코드 설계 및 API Hooking에 문제점에 대해서 다루겠습니다.
( API Hooking을 하려는 접근방법으로 API내부에 구조를 알기 위한것이 목적이라고 볼 수 있습니다. )


이전 1부에서 보왔던 소스코드를 보면 다음과 같이 일부분에 코드가 주석처리되어 있는 것을 볼 수 있습니다.

BOOL WINAPI MyExtTextOutW( __in HDC hdc, __in int x, __in int y, __in UINT options, 
__in_opt CONST RECT * lprect, __in_ecount(c) LPCWSTR lpString,
__in UINT cbCount, __in_ecount_opt(c) CONST INT * lpDx)
{
//if (cbCount == 0)
//{
// return FALSE;
//}
/*HDC hDC;

hDC = ::GetDC(0);

::TextOutA(hDC, 0, 0, "A", 1);
//::TextOutW(hDC, 0, 0, _T("A"), 1);

::ReleaseDC(0, hDC);*/

return TRUE;
}
코드 강조가 제대로 되어 있지 않기 때문에 잘 보이지 않을 수도 있지만 return TRUE;를 제외한 모든 코드를 주석처리하였습니다.
이것은 일부로 무언가를 보여주기 위해 주석처리를 한 것이며 다음과 같이 코드를 변경합니다.
BOOL WINAPI MyExtTextOutW( __in HDC hdc, __in int x, __in int y, __in UINT options, 
__in_opt CONST RECT * lprect, __in_ecount(c) LPCWSTR lpString,
__in UINT cbCount, __in_ecount_opt(c) CONST INT * lpDx)
{
HDC hDC;

hDC = ::GetDC(0);

::TextOutA(hDC, 0, 0, "A", 1);

::ReleaseDC(0, hDC);

return TRUE;
}

그리고 이 상태에서 실행을 해봅니다. ( 주의 : 작업중인 모든 내용이 다 날아갈 수 있습니다. DLL Injection이 되었고 ExtTextOutW를 호출하는 모든 프로그램이 강제종료 및 오류를 보여줄 것입니다. )
실행을 해보시면 거의 화면상에 보이는 모든 프로그램에 오류가 발생하여 에러를 보여주는 것을 볼 수 있습니다.
그렇다면 저 위에 함수들 중에 호출한 API중 어떠한 API를 호출하지 말아야 한다는 말인데 ::TextOutA(hDC, 0, 0, "A", 1);를 주석처리하게 되면 정상적으로 실행됨을 볼 수 있습니다.

TextOutA라는 함수와 ExtTextOutW라는 함수와 도대체 무슨 연관이 있길래 오류가 발생하는지 의문이 생길 것입니다.
그렇다면 TextOutA라는 함수에 내부를 디버거로 따라가보면 왜 그러한 오류가 발생하는지 알 수 있습니다.

TextOutA에 브레이크 포인트를 걸은 후 그 부분에서 멈추게 한 다음 ExtTextOutW에 브레이크를 걸게 되면
TextOutA에 함수가 리턴하기전에 ExtTextOutW에 브레이크 포인트를 걸은 부분이 실행됨을 알 수 있습니다.


b0072382_4971b1f3078e5.png 


위에 그림에서는 Vista SP1에서의 TextOutA API 함수내부에 디스어셈블링 된 코드를 보여주며 그림에서 보다시피call 0x768efc9b함수를 호출함으로 내부적으로 ExtTextOutW를 호출하는 것을 알 수 있습니다.
즉, Microsoft에서 API를 설계할 때 유저레벨에서 동일한 성격의 API가 두개이상일 경우 이러한 API들중 확장되지 않은 API인 TextOutA는 확장된 API인 ExtTextOutW를 호출하게 됩니다.
이것은 TextOutA에만 해당되는 사항이 아니며 DrawTextA에 경우도 마찬가지로 내부적으로 ExtTextOutW를 호출하게 됩니다.
또한, 더 나아가서 xxxxx 라는 API가 있다면 xxxxxEx라는 API라는 함수가 있을 수 있는데 이름에도 보다시피 xxxxxEx API는 xxxxx API에 확장된 API이며
이러한 API일 경우 xxxxx라는 API는 내부적으로 xxxxxEx라는 API를 호출할 가능성이 높습니다.


따라서 위에서 오류가 발생한 이유는 재귀호출에 의한 오류라는 것을 알 수 있습니다. 만약 API에 리버싱을 시도하지 않는다면 왜 그러한 오류가 발생했는지조차도 갈피를 못 잡게 되는 상황입니다.
우리는 API Hooking을 한 후 가로챈 API가 호출된 시점에서 새로운 API호출을 필요로 할 때 이러한 구조를 잘 알고 ( 재귀호출에 가능성 및 기타 가능성 ) 호출을 해야만 합니다.


다음으로 넘어가서 현재의 API Hooking모듈을 보면 기능상으로 한가지 문제점이 있습니다.
바로 API 는 가로챘지만 원본의 API를 호출하지 못하며 화면상에 모든 텍스트들이 출력이 되지 않는 문제점입니다.
데이터를 가로챈 후 정상적으로 API를 호출하여 훅킹을 하지 않은 것과 같은 효과를 발생시켜야 하는데 이러한 코드가 들어가 있지 않은 것 입니다.
( 사실 문제점은 이것말고도 한 두가지가 아니지만 밑에서 설명하겠습니다. )

현재로써는 원본 소스코드에 일부 변형이 일어났기 때문에 ( 함수에 앞부분에 기계어 코드가 점프 코드로 변경 되어 있음 )그냥 함수를 호출했다가는 재귀호출을 하게 됩니다.

따라서 이러한 경우 여러가지 방법이 있는데 첫 번째로 가장 쉬운 방법인 훅킹 된 함수가 호출되었을 때 원래의 원본 소스코드를 저장하여놓았다가 다시 원래대로 되돌린 후 원본을 호출하고
호출이 되었다면 훅킹을 계속 하기 위해서 다시 Jmp코드를 심는 방법입니다.

b0072382_4971b1f3078e5.png 



그러나 위에서 제시한 방법은 다음과 같은 치명적인 문제점이 있습니다.

첫 번째로 API Hooking을 하려는 대상 프로세스에 두 개의 스레드가 있고 이 스레드가 TextOutA를 같이 호출한다면
원본 API 를 원래대로 되돌리고 난 후 다시 패칭을 하는 그 중간 시점에 운영체제에 멀티 태스킹이 발생하여 다른 스레드에서 TextOutA를 호출할 경우
그 스레드에서 호출하는 API를 가로챌 수 없는 문제점이 있습니다.

두 번째로 프로세스가 실행중에 코드를 변경시킬 경우 동기화이슈가 발생하게 되는데 이러한 패칭 스타일은 동기화 문제를 유발하여 CPU가 잘 못 된 기계어 코드를 실행시킬 수 있습니다.


예를 들어, MyTextOutA에서 원본 소스코드로 되돌리는 코드가 실행 된 후 2번 스레드에서 TextOutA가 호출되었고 첫 번째 어셈블리 코드인 mov edi, edi가 호출되었으며 이 상태에서 멀티 태스킹이 발생하였고,
이에 따라 다시 1번스레드인 Injection DLL 함수가 계속 실행됨으로 인해서 API 패칭을 다시 한 상태에서 또 다시 멀티 태스킹이 발생되어 2번 스레드가 실행되면 mov edi, edi 다음 코드가
Jmp 를 하려는 대상 주소코드에 값을 가지고 있으므로 잘 못 된 기계어를 CPU에서 해석할 가능성이 있습니다.

( 말이 상당히 복잡한데 동기화 문제는 테스트가 거의 불가능하므로 항상 이러한 방식으로 추측을 하여야 합니다.
참고로 동기화 문제는 운영체제에서 멀티 태스킹을 지원하거나 CPU가 두개이상일 경우에만 발생하며 이러한 경우가 아니라면 동기화문제가 발생하지 않습니다.
또 한 mov edi, edi라는 코드는 CPU에서 실행되는 하나의 기계어 코드 단위 ( atomic 기계어 )이므로 movedi, edi라는 코드가 실행이 끝나기 전까지는 해당 코드를 해석하는 CPU에서 다른 작업을 할 수 없습니다.
설령 인터럽트가 발생하여도 mov edi, edi가 끝난후에 CPU에서 인터럽트 서비스 루틴을 호출한다는 의미입니다. )

우리는 위에서 원본 소스코드로 되돌려서 API를 호출하는 패칭방식은 사용 할 수 없다는 것을 알았으며 ( 간혹 중국넘들이 만든 바이러스에서는 쓰이기도 합니다. )
동기화 이슈를 피하기 위해 원본 소스코드를 되돌리는 것이 아닌 원본 소스코드를 다른 곳에 저장하는 방법이 있는데 이에 대해서 설명할려고 합니다.
그림에서 보시다시피 플로우 차트는 다음과 같이 진행됩니다.


b0072382_4971b1f3078e5.png 

그러나 위에서 제시한 방법은 다음과 같은 일부 문제점이 있습니다.
( 자꾸 문제점에 대해서 말하게 되는데 사실 유저모드에서 API Hooking을 완벽하게 하는 것은 불가능합니다. 이것은 밑에서 설명하겠습니다. )

현재 API의 기계어 코드는 Windows XP SP2 이상을 대상으로 하여 위에서 보았듯이 Jmp 기계어 코드인 5Byte에 맞게
API 코드가 패칭이 되고 5Byte만큼만 트램폴라인 함수에 복사하면 되지만 Windows XP SP2 미만에 버전에 운영체제에 경우에 패칭을 할 경우 정확히 5Byte만큼
기계어 코드가 맞아 떨어지는 것이 아니라 mov edi, edi 코드가 제외됨으로 인해서 디스어셈블러가 중간에 반드시 개입되어야 하는 현상이 발생합니다. ( 기계어 코드에 길이를 계산해야 하므로.. )
즉, 트램폴라인 스타일에 API 패칭은 반드시 디스어셈블러가 동원되어야 합니다.

실제로 위에 방식은 Microsft사에서 진행되는 Detour 라는 API Hooking 프로젝트가 있는데 위와 같은 방식을 하고 있으며 디스어셈블러를 사용합니다.

( 3부에서는 Detour를 이용하는 방법에 대해서 다루려고 하고 있습니다. 이미 만들어진 프로젝트가 있고 디스어셈블러를 만드는 방법에 대해서는 추가적인 문서를 더 작성해야 하기 때문입니다. )


다음으로 또 넘어가서 API Hooking시 발생하는 DLL Attach및 DLL Detach와 관련 된 문제점에 대해서 설명하려고 합니다.
API Hooking은 특정한 API를 훅킹을 하려는 시점 또한 중요한데 만약 프로세스가 이미 떠있는 상태에서 API에 변경을 가하게 되면
프로세스가 잘 못 된 기계어 코드를 극악의 확률로 실행될 가능성이 발생됩니다.
( 사실 이러한 부분은 확률자체가 매우 낮으므로 이런 오류를 보기가 매우 힘듭니다. )

예를 들어 프로세스가 TextOutA를 호출하였고 mov edi, edi가 호출되고 멀티 태스킹이 발생하여
API Hooking을 시도하려는 코드가 실행된다면 당연히 이 부분의 코드는 Jmp코드로 변경될 것이며,
다시 멀티 태스킹이 발생하여 mov edi, edi 다음에 기계어를 실행시킬려고 하다보니 잘 못 된 코드를 CPU에서 해석하게 되는것 입니다.
CPU 자체에서 메모리에 값을 한 번에 변경시키는 atomic 기계어 코드를 제공하긴 하지만 mov edi, edi가 실행되고 멀티 태스킹이 발생하게 되면
버스 잠금에 효과가 사라지므로 이 또한 효력을 발휘하지 못 하게 됩니다.
이러한 부분은 Detach도 마찬가지로 겪게 되는 문제점입니다.
따라서 API Hooking을 시도시에는 Attach를 할 경우 문제가 생기며 프로세스가 어떠한 API를 호출하기전.. 즉, 프로세스가 처음 실행되는 시점에
API 패칭이 완료되어야 하며 프로세스가 끝나서 더 이상 API 를 호출하지 않는 시점에 API를 원래대로 복구시켜야 합니다.

지금까지 API Hooking을 하면서 한가지 의문이 들 수 있을 것 입니다. ( 의문이 들지 않는다면 ................. 낭패.. )
계속해서 mov edi, edi라는 코드가 언급되었는데 사실 edi 레지스터에 edi를 넣어도 전혀 의미가 없습니다.
즉, 이 기계어 코드는 없어도 전혀 의미가 없습니다.
( mov edi, edi 는 XP SP2이상에서 추가되었습니다. )

또한 다른 API들을 보아도 모두다 똑같이 mov edi, edi가 앞에 들어가 있습니다.
또한 더 조사해보면 mov edi, edi 바로 위에 nop가 깔려있는 것을 볼 수 있습니다.
다음 그림에서 이에 대한 의문을 풀어줍니다.


b0072382_4971b1f3078e5.png 


바로 Microsoft에서 런타임 핫패칭을 수행하기 위해서 이러한 코드들이 삽입되어 있는 것 입니다.
런타임 핫패칭이라고 해도 API Hooking과는 크게 다른 부분이 없고 특정 함수에 문제가 발생하였을 때 특정 함수를 핫패칭하여 그 함수가 호출되면
Jmp코드에 의해 원래의 함수가 호출되는것이 아닌 버그가 수정 된 함수가 호출되도록 하는 것 입니다.

이것을 이용하게 되면 이전에 말하였던 프로세스가 실행중일 때 중간에 Attach를 시키는 문제점을 완벽한 동기화를 통해 해결 할 수 있습니다.
이러한 Microsoft 핫패칭 스타일을 이용한 API Hooking은 다음과 같이 구현이 될 것 입니다.


b0072382_4971b1f3078e5.png 

위에서 설명하는 플로우 차트가 Microsoft에서 사용하는 핫패칭하는 방식이며 아무래도 리버서들을 통해서 알려지는 방식이기 때문에
실제 Microsoft에서 하는 방식과 다를 수 있습니다. ( 위에서 설명하는 Microsoft핫패칭을 이용한 Hooking방식은 검증이 되지 않았으며 XP SP2 이상에서만 사용이 가능하다는 제약이 발생합니다. )


profile

esniper

2010.09.05 21:16:02

이렇게 보니 이해가 빨리 되네요~

3부도 기대가 많이 되네요 ^^

profile

lain

2010.09.06 09:21:16

3부 귀찮아서 안 쓰기로 했어요 .ㅋㅋ

이미 할말은 다 한 것 같고 Detour 라이브러리를 쓰는 방법이 이제 많이 퍼져서 구글에서 검색하면 나오고..

mov edi, edi도 설명했으니 기본적인 설명은 다한 것 같습니다.emoticon

List of Articles
번호 제목 글쓴이 날짜 조회 수
163 Linux Tip 리눅스 기반 오픈 소스 사이트 모음 삽질신 2009-11-16 71398
162 Linux Tip VirtualBox에 우분투 설치 후에 내부 네트워크 접속 설정하기 file esniper 2010-09-10 35321
161 Windows Research IE Cache 경로 변경하기 lain 2011-06-09 30724
160 Windows Research VMWare 탐지 기법 우회 - 2 [2] lain 2011-04-04 28859
159 Windows Research 화면캡쳐방지는 어떻게 구현될까? [3] lain 2010-09-04 28686
158 PHP Tip php 파싱 라이브러리 - snoopy esniper 2009-03-26 20731
157 Linux Tip Directory index forbidden by Options directive 삽질신 2009-11-16 20266
156 Linux Tip VirtualBox에서 오른쪽 CTRL키 사용하기(VitualBox 호스트키) file [2] esniper 2010-09-10 19653
155 Windows Tip SSH 터널링 - putty의 plink 사용예 esniper 2009-03-26 19589
154 문서자료 고품질의 무료 아이콘들 [3] esniper 2010-09-08 19441
153 문서자료 json 문법 체크 사이트 Lyn 2012-02-28 18893
152 PHP Tip mysql_insert_id() - 마지막 insert 된 아이디 값 esniper 2009-03-26 18451
151 Linux Tip 우분투 설치 후 putty에서 한글 안 깨지도록 설정하기 file esniper 2010-09-10 17076
150 Windows Research 커널레벨에서 IAT Hook lain 2011-06-11 16545
149 Linux Tip 우분투 ssh or mysql 서버 접속지연이 있는 경우 해결책 esniper 2010-09-10 16447
148 Linux Tip 우분투에 IRC서버 설치후 닉네임 길이와 동접자 조절. [1] 오랑캐꽃 2010-11-26 16014
147 Windows Research 유저 레벨에서 API Hook 1부 file [1] lain 2010-09-03 15846
146 Windows Research SetEvent 함수는 언제 실패할까? lain 2010-09-04 15822
145 Windows Research Sysinternals - Filemon source code file [5] lain 2010-09-04 15440
144 Windows Research Sysinternals - Regmon source code file [1] lain 2010-09-04 14990

  • 이용약관
  • 개인정보취급방침
  • 사이트맵