API Hooking 기법 중에서 IAT Hooking 의 동작 원리를 파악합니다.
그리고 예제 파일을 이용해서 실습을 해보도록 하겠습니다.



이전 포스트에서 이어지는 내용입니다.
API Hooking – 계산기, 한글을 배우다. (1)



IAT Hooking 동작 원리



프로세스에서 IAT(Import Address Table)는 프로그램에서 호출되는 API 들의 주소가 저장되는 장소입니다.

* IAT 설명
PE(Portable Executable) File Format (6) - PE Header

IAT Hooking 은 IAT 에 저장된 API 의 주소를 바꿔 치는 후킹 기법입니다.

아래 그림을 봐주시기 바랍니다.

<Fig. 1>

이것은 정상적인 계산기(calc.exe) 프로세스에서 user32!SetWindowTextW() API 호출을 나타낸 그림입니다.

01001110 주소가 바로 IAT 영역이며, 프로그램 실행시점에 PE Loader 가 이 주소(01001110)에 user32!SetWindowTextW() API 주소(77D0960E)를 기록해 두었습니다.

01002628 주소의 CALL DWORD PTR [01001110] 명령은 01001110 주소에 저장된 값(77D0960E)을 호출하라는 뜻이므로 결국 CALL 77D0960E 명령과 같습니다.

01002628 의 CALL 명령에 의해서 제어는 user32!SetWindowTextW() 시작 주소(77D0960E)로 갔다가(1), 함수 실행이 완료되면 되돌아 옵니다.(2)

이제 IAT 가 후킹된 계산기 프로세스를 보시겠습니다.

<Fig. 2>

IAT Hooking 을 위해서 먼저 계산기 프로세스(calc.exe)에게 “hookiat.dll” 파일을 injection 시켰습니다.

* DLL Injection 설명
DLL Injection - 다른 프로세스에 침투하기 (1)

hookiat.dll 파일은 MySetWindowTextW() 라는 후킹 함수(10001000)를 제공합니다.

01002628 주소의 CALL 명령을 보시면 <Fig. 1> 의 그것과 완전히 동일합니다. 하지만 01001110 주소에 가면 값이 10001000 으로 바뀌어 있습니다. 10001000 주소는 hookiat!MySetWindowTextW() 함수의 시작주소 입니다.

즉, 실행 코드는 그대로 두고 IAT 에 저장된 API 의 시작 주소를 사용자 함수의 시작 주소로 변경하였습니다. 이것이 IAT Hooking 의 기본 동작 원리 입니다.

IAT 가 후킹된 계산기 프로세스의 실행 흐름을 보겠습니다.

01002628 주소의 CALL 명령에 의해서 제어는 hookiat!MySetWindowTextW() 시작 주소(10001000)로 갔다가(1), 뭔가 작업을 하고 1000107D 주소의 CALL 명령에 의해서 (원래 호출하려 했던) user32!SetWindowTextW() 시작주소로 갑니다.(2)

* 참고로 1000B6B8 은 hookiat.dll 의 data 섹션 영역이며 전역변수 g_pOrgFunc 의 주소입니다. DLL Injection 될 때 DllMain() 에서 원본 함수(user32!SetWindowTextW())의 시작 주소를 구해서 저장해 두었습니다.

이후 user32!SetWindowTextW() API 가 종료되면 제어는 hookiat.dll 의 1000107D 주소의 명령어 이후로 돌아오고(3), 최종적으로 calc.exe 의 코드영역인 01002628 주소의 명령어 이후로 되돌아 오게 됩니다.(4)

즉, user32!SetWindowTextW() API 를 호출하기 전에 hookiat!MySetWindowTextW() 를 호출해 준것입니다.


대상 프로세스(calc.exe)에 사용자 DLL(hookiat.dll)을 인젝션 시키고, calc.exe 프로세스의 IAT 영역에서 4 byte 크기의 주소만 변경하면 위와 같이 쉽게 API Hooking 을 할 수 있습니다. (IAT 패치를 이용한 API 후킹을 특별히 IAT Hooking 이라고도 말합니다.)

위 과정을 잘 숙지하신 후 아래 실습 예제를 따라 해보시기 바랍니다.



실습


계산기의 에디터 윈도우에 숫자 대신 한글을 표시하는 예제 파일입니다. 이 파일들은 앞에서 설명한 IAT 패치를 통한 API Hooking 기법을 사용하였습니다.


* 참고
hookiat.dll 과 InjectDll.exe 파일 모두 VC++ 2008 Expr. 로 제작되었으며, Windows XP SP3 에서 테스트 하였습니다. 혹시 실행이 안 되는 경우 연락해 주시기 바랍니다.

위에 첨부된 파일을 적당한 위치(c:\work)에 복사해주세요.

먼저 계산기(calc.exe)를 실행하신 후 Process Explorer 등으로 PID 값을 알아냅니다.

<Fig. 3>

커맨드 창에서 아래와 같이 입력해 주세요.

<Fig. 4>

Process Explorer 로 인젝션 된 hookiat.dll 을 확인해 보겠습니다.

<Fig. 5>

자 이제 계산기 프로그램에 아무 숫자나 입력해서 연산을 해보시기 바랍니다.

<Fig. 6>

입력하는 모든 숫자가 한글로 변경됩니다. 이 상태에서 계산기의 연산 기능은 완벽하게 잘 동작합니다.

그러면 이번에는 Unhook 을 해볼까요?

Unhook 은 IAT 를 원래 값으로 복원시키고 삽입시킨 DLL (hookiat.dll) 을 빼내면 됩니다.

커맨드 창에서 아래와 같이 입력하세요.

<Fig. 7>

다시 계산기 프로그램에 숫자를 입력해 보시기 바랍니다.


<Fig. 8>

정상적으로 숫자가 찍혀 나타납니다. Unhook 성공입니다.


다음 포스트에는 예제 프로그램의 소스 코드를 상세히 분석하여 IAT Hooking 에 대한 구현 방법을 알아보겠습니다. 또한 디버거를 이용해서 계산기 프로세스의 가상메모리를 살펴보면서 실제로 IAT 가 어떤식으로 후킹 되었는지 확인하겠습니다.


API Hooking - 계산기, 한글을 배우다. (3)


+---+

ReverseCore


위 글이 도움이 되셨다면 추천(view on) 부탁드려요~

Trackback Address :: http://www.reversecore.com/trackback/60 관련글 쓰기

  1. 냥냥 2009/11/13 22:27 댓글주소 | 수정 | 삭제 | 댓글

    이런...ㅋㅋㅋ
    감사합니다~ ㅎㅎㅎㅎ
    메신저 같은건... 알수없겠죠!? ㅜ

  2. 냥냥 2009/11/14 10:04 댓글주소 | 수정 | 삭제 | 댓글

    질문으로 이러한 방식 말고...
    그냥 상대방 메모리( calc.exe ) 에다가 코딩하는 방법은... 어떤가요..?
    dll이 메모리에 집적쓰는것보다 더 좋은 이유가특별히 있나요..??ㅜ

    • ReverseCore 2009/11/14 22:46 댓글주소 | 수정 | 삭제

      아마 code injection 을 말씀하시는것 같은데요...

      다음 다음 강좌가 code injection 을 이용한 API Hooking 입니다.

      Dll Injection 과는 좀 차이가 있습니다.(조금 더 어렵습니다.)

      두 강좌를 비교해보시면서 그 장단점을 느껴보시는 것도 좋을것 같습니다.

  3. pdm1n 2009/11/14 18:58 댓글주소 | 수정 | 삭제 | 댓글

    역쉬... 대단하십니다!! 언제나 신비로운 강좌 잘보았습니다^^ 프로그래밍과 리버싱의 강좌를
    묘하게 조화시켜 강좌해주시는 ㅎㅎ
    뜬금없이 리버스코어님 질문있습니다. 다른프로그램들은 주로 400100 이런식으로 4로 시작하는데
    왜 calc.exe프로그램은 주소가 01012475이런식으로 시작하는지 궁금합니다.
    스택도 0007FFE0이런식으로 시작하는지 궁금합니다. 주로 다른스택도 13FFC4이런식으로
    13으로 시작하는데 궁금합니다.
    읽어주셔서 감사합니다^^! 리버스코어님 Good :D

    • ReverseCore 2009/11/14 22:53 댓글주소 | 수정 | 삭제

      pdm1n님, 안녕하세요.
      먼저 칭찬 감사드리고요...

      * 일반적인 EXE 파일의 ImageBase 는 400000 입니다. 그건 개발도구(VC++, VB, Delphi, etc) 에서 그렇게 세팅한것이구요. 사용자가 고칠 수 도 있습니다.
      calc.exe 는 MS Windows 기본 프로그램인데요, 이러한 기본 프로그램들의 ImageBase 값을 살펴보면 말씀하신대로 조금 특이합니다.
      크게 의미있는건 아닙니다.

      * PE Header 에 보면 Stack 의 시작위치가 명시되어 있지 않습니다.(크기만 명시되어 있지요.) PE Loader 에 스택 주소가 결정되는 알고리즘이 따로 있을것 같네요.

  4. pdm1n 2009/11/14 19:54 댓글주소 | 수정 | 삭제 | 댓글

    01002621 |. FF7424 10 PUSH DWORD PTR SS:[ESP+10] ; /Text
    01002625 |. 8BF8 MOV EDI,EAX ; |
    01002627 |. 56 PUSH ESI ; |hWnd
    01002628 |. FF15 10110001 CALL DWORD PTR DS:[1001110] //SetWindowTextW



    01002628(SetWindowText)주소에 숫자 값이 들어가는데 SetwindowTextW에 브포걸고
    스텍의 ESP+10부분을보니

    ESP+10 0100490D RETURN to calc.0100490D from calc.01002604가 있었습니다.

    분명히 ESP+10부분엔 숫자부분이 있어야하는데 없는겁니다. 그래서 다시

    원래자리01002621(push dword ptr ss:[ESP+10])자리에 브포를거니

    ESP+10 자리에 숫자값이 재대로 나오더군요 그래서 왜 콜부분에 브포걸면 위치가다를까

    생각해보니 콜위에 PUSH가 2번들어갑니다. 스택주소하나당 4byte기때문에

    push 2개(4byte) -> EsP+10 + 8 = ESP+18 콜부분에 있더라구요

    많이 배웁니다.ㅎㅎ!!

    저도 응용해보았습니다.

    01002621 E8 12120100 CALL calc.01013838
    01002626 90 NOP
    01002627 90 NOP
    01002628 90 NOP
    01002629 90 NOP
    0100262A 90 NOP
    0100262B 90 NOP
    0100262C 90 NOP
    0100262D 90 NOP

    01013838 FF7424 14 PUSH DWORD PTR SS:[ESP+14] //스택의 숫자 출력값
    0101383C 8BF8 MOV EDI,EAX
    0101383E 56 PUSH ESI //Class='Edit'의 핸들
    0101383F FF15 10110001 CALL DWORD PTR DS:[1001110] USER32.SetWindowTextW //원래되로 출력후
    01013845 6A 00 PUSH 0 //메시지박스 스타일 확인버튼만
    01013847 68 10390101 PUSH calc.01013910 //메시지박스 타이틀(ASCII로하면안됨 유니코드로..)
    0101384C FF7424 20 PUSH DWORD PTR SS:[ESP+1C] //위의 숫자 출력값
    01013850 6A 00 PUSH 0 //핸들 NULL
    01013852 FF15 A8110001 CALL DWORD PTR DS:[10011A8]; USER32.MessageBoxW //메시지박스 출력
    01013858 C3 RETN // 복귀
    01013859 90 NOP

    전 메시지박스로 메시지박스의 한글 넣을려고 했는데.. 힘들꺼같아서 포기

    저장은 안되요 ㅋㅋ

    • reversecore 2009/11/14 23:14 댓글주소 | 수정 | 삭제

      스택에 대한 개념이 잘 정리되신것 같습니다.

      그리고 코드를 수정해서 MessageBox() 까지 호출하셨군요.

      그런식의 독창적인 작업을 하나씩 해나가시다보면 실력이 조금씩 (대신 확실히) 쌓여갈 것입니다.

  5. 이승철 2009/11/15 00:22 댓글주소 | 수정 | 삭제 | 댓글

    질문계시판으로 왜 안가져요 ㅜㅜ

    이상하네 ㅡㅡ;;

    • ReverseCore 2009/11/15 21:56 댓글주소 | 수정 | 삭제

      이승철님 안녕하세요.

      제가 지금 해보니까 댓글이 많아서 그런지
      디폴트로 댓글목록이 닫혀 있네요.

      '댓글' 링크를 누르시면 열리네요.

      q&a 2 를 만들때가 된것 같습니다.

      감사합니다.

  6. pdm1n 2009/11/15 18:35 댓글주소 | 수정 | 삭제 | 댓글

    77CFB8BA > 8BFF MOV EDI,EDI

    --------------
    번호-ID 스택값(ID의 스텍값)---
    0 - 124 -76
    1 - 125 -77
    2 - 126 -78
    3 - 127 -79
    4 - 128 -80
    5 - 129 -81
    6 - 130 -82
    7 - 131 -83
    8 - 132 -84
    9 - 139 -85
    --------
    ESP ==> > 77187344 /CALL to SendMessageW from comctl32.7718733E
    ESP+4 > 0022047C |hWnd = 22047C
    ESP+8 > 00000111 |Message = WM_COMMAND
    ESP+C > 00000084 |Notify = MENU/BN_CLICKED... ID = 132.
    ESP+10 > 00100680 \hControl = 00100680 ('8',class='Button',parent=0022047C)
    영일이삼사오육칠팔구십
    스트링저장해놓고 2바이트씩 출력하면 될듯한데
    ex)예를들어
    offset 400400 string:영일이삼사오육칠팔구십 11글자 한글:2byte
    eax = 400400
    cmp 버튼값,123 //0버튼이다
    add eax,1
    mov dword ptr ss byte :[40040eax] ㅡ,.ㅡ구문을 모르겠음;
    이런식으로..대충하면될듯한.. 응용~ 전역만 하면 해볼텐데..
    전역 3일남음...!!ㅋㅋ
    리버스코어님 열심히할게요
    http://pdm1n.tistory.com
    가끔식 놀러와주세요 발전하는 모습
    보여드릴게요 ㅋㄷㅋㄷ 리버스코어님 쵝오 ㅎ

  7. 늅늅 2009/11/16 10:35 댓글주소 | 수정 | 삭제 | 댓글

    아...이런 한발 늦었군요 -_-;

    오늘도 친절한 설명덕택에 잘 배우고 갑니다~


◀ PREV : [1] : ... [31] : [32] : [33] : [34] : [35] : [36] : [37] : [38] : [39] : ... [91] : NEXT ▶