앞에서 소개해 드렸던 예제 프로그램(hookiat.dll)의 소스 코드를 상세히 분석하여 IAT Hooking 의 동작 원리를 점검하고 실제 구현 방법에 대해서 공부합니다.


<hookiat!MySetWindowTextW() 함수>

본 내용은 이전 포스트에서 이어지는 내용입니다.

API Hooking – 계산기, 한글을 배우다. (1)
API Hooking – 계산기, 한글을 배우다. (2)



소스 코드 분석



* 참고!
모든 소스 코드는 MS Visual C++ 2008 Express Edition 으로 개발 되었으며, Windows XP SP3 환경에서 테스트 되었습니다.

InjectDll.cpp 소스는 예전에 설명 드렸던 내용과 기본적인 구조는 비슷합니다. 대신 약간의 업그레이드를 하였는데요.

실행 파라미터를 늘려서 Injection 과 Ejection 을 동시에 지원하도록 수정하였습니다. 또한 권한 상승 함수를 추가시켜 (간혹 발생할 수 있는) OpenProcess() 에러를 방지 하였습니다. (관련 설명은 생략하겠습니다. 더 자세한 내용은 MSDN 을 찾아보시기 바랍니다.)

InjectDll.cpp 에 대한 더 자세한 설명은 아래 링크를 참고해 주세요.

DLL Injection – 프로세스에 침투하기
DLL Ejection - 침투시킨 DLL 빼내기


# DllMain()

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{   
    switch( fdwReason )
    {
        case DLL_PROCESS_ATTACH : 
            // original API 주소저장
            g_pOrgFunc = GetProcAddress(GetModuleHandle(L"user32.dll"), 
                                        "SetWindowTextW");

            // # hook
            //   user32!SetWindowTextW() 를 hookiat!MySetWindowText() 로 후킹
            hook_iat("user32.dll", g_pOrgFunc, (PROC)MySetWindowTextW);
            break;

        case DLL_PROCESS_DETACH :
            // # unhook
            //   calc.exe 의IAT 를원래대로복원
            hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrgFunc);
            break;
    }

 return TRUE;
}

늘 그렇듯이 예제 코드의 메인 함수는 간단합니다.

중요 코드들을 한 라인씩 살펴보겠습니다.

case DLL_PROCESS_ATTACH :
    g_pOrgFunc = GetProcAddress(GetModuleHandle(L"user32.dll"), "SetWindowTextW");


먼저 DLL_PROCESS_ATTACH 이벤트에서 user32!SetWindowTextW() API 주소를 구합니다. 이는 나중에 unhook 작업에서 사용되기 때문에 전역변수(g_pOrgFunc)에 잘 저장해 놓습니다.

hook_iat("user32.dll", g_pOrgFunc, (PROC)MySetWindowTextW);

그리고 hook_iat() 함수를 호출하여 IAT 를 후킹(hooking)합니다. (user32!SetWindowTextW() 주소를 hookiat!MySetWindowTextW() 주소로 변경)

case DLL_PROCESS_DETACH :
    hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrgFunc);


DLL_PROCESS_DETACH 이벤트에서 IAT 후킹을 풀어(unhook)줍니다. (hookiat!MySetWindowTextW() 주소를 user32!SetWindowTextW() 주소로 변경)


# MySetWindowTextW()

BOOL WINAPI MySetWindowTextW(HWND hWnd, LPWSTR lpString)
{
    wchar_t* pNum = L"영일이삼사오육칠팔구";
    wchar_t temp[2] = {0,};
    int i = 0, nLen = 0, nIndex = 0;

    nLen = wcslen(lpString);
    for(i = 0; i < nLen; i++)
    {
        // '수'문자를 '한글'문자로 변환
        //   lpString 은 wide-character (2 byte) 문자열

        if( L'0' <= lpString[i] && lpString[i] <= L'9' )
        {
            temp[0] = lpString[i];
            nIndex = _wtoi(temp);
            lpString[i] = pNum[nIndex];
        }
    }

    // user32!SetWindowTextW() API 호출
    //   (위에서 lpString 버퍼 내용을 변경하였음)

    return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString);
}


IAT 가 후킹된 계산기(calc.exe) 프로세스는 코드 내에서 user32!SetWindowTextW() 함수를 호출할 때마다 사실은 위의 hookiat!MySetWindowTextW() 함수가 호출됩니다.

함수의 파라미터 lpString 은 출력할 (wide-character) 문자열 버퍼입니다. 따라서 이 lpString 을  조작하면 사용자가 원하는 문자열을 출력시킬 수 있습니다.

nLen = wcslen(lpString);
for(i = 0; i < nLen; i++)
{
    if( L'0' <= lpString[i] && lpString[i] <= L'9' )
    {
        temp[0] = lpString[i];
        nIndex = _wtoi(temp);
        lpString[i] = pNum[nIndex];
    }
}

위의 for 루프에서 lpString 에 저장된 ‘수’ 문자열을 ‘한글’ 문자열로 교체합니다.

아래는 lpString 버퍼의 변경 전/후의 그림입니다.

<Fig. 1>

숫자 "123" 에 대해서 한글은 "일이삼" 으로 표시할 수 있습니다. 즉, 숫자 1 글자당 한글 1 글자로 1:1 대응이 가능하다는 것이죠. 이런 특성은 기존 버퍼를 그대로 사용할 수 있다는 장점이 있습니다. 위 코드를 보시면 lpString 문자열 버퍼에 직접 변환된 문자열(한글)을 기록합니다.

참고로 만약 영어라면 "ONETWOTHREE" 와 같이 길게 표시해야 하기 때문에 기존 버퍼("123")에 덮어쓸 수 없고, 새롭게 버퍼를 할당 받아서 사용한 후 그 버퍼 주소를 original API 에 넘겨야 합니다.

return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString);

for 루프가 종료하면 마지막으로 함수 포인터 g_pOrgFunc 를 호출합니다.

g_pOrgFunc 는 DllMain() 에서 미리 구해놓은 user32!SetWindowTextW() API 시작 주소입니다.
즉, 원본 함수를 호출해서 계산기의 에디트 윈도우에 (변환된) ‘한글’ 문자열을 출력시킵니다.


# hook_iat()

BOOL hook_iat(LPCSTR szDllName, PROC pfnOrg, PROC pfnNew)
{
    HMODULE hMod;
    LPCSTR szLibName;
    PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
    PIMAGE_THUNK_DATA pThunk;
    DWORD dwOldProtect, dwRVA;
    PBYTE pAddr;

    // hMod, pAddr = ImageBase of calc.exe
    //             = VA to MZ signature (IMAGE_DOS_HEADER)
    hMod = GetModuleHandle(NULL);
    pAddr = (PBYTE)hMod;

    // pAddr = VA to PE signature (IMAGE_NT_HEADERS)
    pAddr += *((DWORD*)&pAddr[0x3C]);

    // dwRVA = RVA to IMAGE_IMPORT_DESCRIPTOR Table
    dwRVA = *((DWORD*)&pAddr[0x80]);

    // pImportDesc = VA to IMAGE_IMPORT_DESCRIPTOR Table
    pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);

    for( ; pImportDesc->Name; pImportDesc++ )
    {
        // szLibName = VA to IMAGE_IMPORT_DESCRIPTOR.Name
        szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);
        if( !stricmp(szLibName, szDllName) )
        {
            // pThunk = IMAGE_IMPORT_DESCRIPTOR.FirstThunk
            //        = VA to IAT(Import Address Table)
            pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod +
                                         pImportDesc->FirstThunk);

            // pThunk->u1.Function = VA to API
            for( ; pThunk->u1.Function; pThunk++ )
            {
                if( pThunk->u1.Function == (DWORD)pfnOrg )
                {
                    // 메모리 속성을 E/R/W 로변경
                     VirtualProtect((LPVOID)&pThunk->u1.Function,
                                    4,
                                    PAGE_EXECUTE_READWRITE,
                                    &dwOldProtect);

                    // IAT 값을 변경 (후킹)
                    pThunk->u1.Function = (DWORD)pfnNew;

     
                    // 메모리 속성 복원
                    VirtualProtect((LPVOID)&pThunk->u1.Function,
                                   4,
                                   dwOldProtect,
                                   &dwOldProtect);      

                    return TRUE;
                }
            }
        }
    }

    return FALSE;
}


IAT Hooking 을 수행하는 함수입니다.
코드 자체는 짧은데 주석이 많아서 전체적으로 길어 보입니다.

hook_iat() 함수의 초반부는 PE Header 정보를 읽어서 IAT 를 따라가는 내용입니다. 위 코드를 이해하시려면 꼭 IAT 구조를 알고 계셔야 합니다.

IAT 에 대한 상세한 정보는 아래 링크를 참고해 주세요.
PE(Portable Executable) File Format (6) - PE Header

코드를 보면 PE Header 를 따라가다가 아래 코드에서 우리가 원하는 Import Directory Table 의 시작 주소를 구합니다.

pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);

Import Directory Table 은 IMAGE_IMPORT_DESCRIPTOR 구조체 배열로 이루어져 있습니다. IAT 를 구하려면 먼저 이곳을 찾아야 합니다.

위의 코드에서 pImportDesc 에는 01012B80 값이 들어옵니다. 이 주소를 PEView 로 보면 아래 그림과 같습니다.

<Fig. 2>

위 그림이 계산기 프로세스의 IMAGE_IMPORT_DESCRIPTOR 테이블(배열) 입니다. PEView 에서는 Import Directory Table 라고 표시하는 곳이죠. 우리가 목표로 하는 user32.dll 은 <Fig. 2> 의 아래쪽에 위치하는군요. 저곳을 찾아가 봅시다.

for( ; pImportDesc->Name; pImportDesc++ )
{
    // szLibName = VA to IMAGE_IMPORT_DESCRIPTOR.Name
    szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);
    if( !stricmp(szLibName, szDllName) )
    {

위의 for 루프 내에서 pImportDesc->Name 과 szDllName("user32.dll") 을 비교해서 user32.dll 의 IMAGE_IMPORT_DESCRIPTOR 구조체 주소를 찾아냅니다.

결국 pImportDesc = 01012BE4 가 됩니다. (<Fig. 2> 참고)

이제 user32.dll 에 대한 IAT 로 가야 합니다. pImportDesc->FirstThunk 멤버가 바로 IAT 를 가리키는 멤버입니다.

// pThunk = IMAGE_IMPORT_DESCRIPTOR.FirstThunk
//        = VA to IAT(Import Address Table)

   pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod + 
                                pImportDesc->FirstThunk);

위 코드에서 pThunk 가 바로 user32.dll 에 대한 IAT (010010A4) 입니다. (<Fig. 2> 참고)
이 주소를 PEView 로 보면 아래 그림과 같습니다.

<Fig. 3>

User32.dll 의 IAT 에 상당히 많은 함수들이 import 되어 있는걸 보실 수 있습니다. 우리가 찾는 SetWindowTextW 는 01001110 주소에 있으며 값은 77CF61C9 입니다.

// pThunk->u1.Function = VA to API

for( ; pThunk->u1.Function; pThunk++ )
{
    if( pThunk->u1.Function == (DWORD)pfnOrg )
    {

위의 for 루프에서 pThunk->u1.Function 과 pfnOrg (77CF61C9 <– SetWindowTextW 시작주소) 를 비교하여 정확히 SetWindowTextW 의 IAT 주소(01001110)를 찾아 냅니다. (이제 pThunk = 01001110 이고 pThunk->u1.Function = 77CF61C9 입니다.)

여기까지의 코드가 계산기 프로세스의 ImageBase 에서부터 user32!SetWindowTextW 의 IAT 주소를 찾아가는 과정입니다.

이후부터는 위에서 구한 IAT 주소의 값을 바꿔 치는 (후킹) 코드가 이어집니다.

// IAT 값을 변경
pThunk->u1.Function = (DWORD)pfnNew;
     
pThunk->u1.Funcion 에는 원래 77CF61C9 (SetWindowTextW 주소) 값이 있었지만, pfnNew (10001000 <- hookiat!MySetWindowTextW 주소) 값으로 변경합니다.

이제 계산기 코드에서 user32!SetWindowTextW() API 를 호출하면 hook_iat!MySetWindowTextW() 가 호출되는 것입니다.


* 참고!
저 위쪽의 hook_iat() 함수 코드를 보시면 후킹 하기 전에 VirtualProtect() 함수를 이용해서 해당 IAT 메모리 영역 을 "읽기|쓰기" 모드로 변경하고, 후킹 한 후에 다시 원래 모드로 되돌리는 코드가 존재합니다. 계산기 프로세스의 IAT 메모리 영역이 "읽기" 전용이기 때문에 미리 이러한 작업을 해주는 것입니다.


이상으로 hook_iat.cpp 의 코드 설명을 마치겠습니다.

다음 포스트에서는 OllyDbg 를 이용하여 후킹 코드를 디버깅하면서 후킹된 IAT 메모리 영역을 확인해 보겠습니다.

많이 기대해 주세요~


API Hooking – 계산기, 한글을 배우다. (4)


+---+


ReverseCore

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

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

  1. 오호^^ 2009/11/21 01:25 댓글주소 | 수정 | 삭제 | 댓글

    드디어 저도 눈팅만 하다 1등으로 댓글 남기네요

  2. 냥냥 2009/11/21 03:38 댓글주소 | 수정 | 삭제 | 댓글

    아니되!!! 이럴수가!! ㅋㅋ

  3. Pdm1n 2009/11/21 18:29 댓글주소 | 수정 | 삭제 | 댓글

    감사합니다 잘보았습니다. 사랑합니다.

  4. 늅늅 2009/11/23 09:00 댓글주소 | 수정 | 삭제 | 댓글

    출근과 동시에 왔건만...오늘 오전도 이거 보면서..하하;;

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

      늅늅님, 안녕하세요.
      출근과 동시에 제 블로그에 들러주시다니...
      정말 감사합니다. ^^
      좋은 하루 되세요~

  5. nettok 2009/11/24 09:41 댓글주소 | 수정 | 삭제 | 댓글

    항상잘 보고있습니다^-^ 나중에 출판하셔도 될듯

  6. 돈 없는 1人 2009/11/25 08:28 댓글주소 | 수정 | 삭제 | 댓글

    책 내셔도 이 홈페이지 닫으시면 안되욥 ㅠㅠ (ㅋㅋ)

  7. 쵸밥 2009/11/26 04:01 댓글주소 | 수정 | 삭제 | 댓글

    어제도 밤새서 아침 8시까지 읽다 잤는데 ㄷㄷ;;
    PE header 읽다보니 벌서 오늘 새벽 4시네요 ;;

    어제 pm4시 부터 하루종일 PE header .. 페이징 .. 가상주소 .. 이리저리 싸우는중 . . ㅋㅋ
    영어실력이 안되서 구글링은 못하고 이런 한글사이트 돌아다니고 책뒤지고 늅늅 ㅠㅠ
    학교 다닐때 영어 안한게 MSDN 부터 구글링 까지 이리 치명적으로 발목을 잡을 줄이야 늅늅 ㅠㅠ

    늅늅님 아이디 우는모양 같네요 ㅋㅋ

    아 댓글중에 cOrE 님 이라고 써놓은거 Hellow World! 보다가 궁금해져서 질문 써놓은건데 ㄷㄷ;;

    • reversecore 2010/02/02 00:23 댓글주소 | 수정 | 삭제

      쵸밥님, 안녕하세요.

      제 블로그 글들을 정리하는 중에 제가 미처 읽지 못한 댓글이 있었네요.

      밤새서 리버싱 공부를 하셨나 보네요.
      열정이 정말로 대단하세요~

      자주 들러주세요~

      감사합니다.

  8. 뭉이 2010/01/04 22:29 댓글주소 | 수정 | 삭제 | 댓글

    좋은글 잘 읽고 있습니다. 설명도 쉽게 잘해주시고 깔끔하고 머리에 속속 들어옵니다..^^

    그런데 소스가...예외처리가 미흡한것 같습니다.
    dll인젝션 소스가 적어도 try catch정도는 묶어줘여 하지 않을까 생각이 듭니다.
    드라이버단 후킹도 아니고 인젝션이라 많이 크리티컬하지는 않습니다만...;
    처음 공부하는 사람입장에서.. 소스 참고하여 사용하다가 작은 실수로 프로그램 뻑날 수도..???

    심플하게 소스 이해하는데는 어려움이 없지만..그래도 기본적이고 필요한건 빠지지 않았으면 하는 생각이 듭니다.

    그리고 한가지 더...
    OS 테스트 환경에 vista, win7도 했으면 하는 바램입니다....

    바쁘실텐데...이런 좋은 내용 써주셔서 너무너무 감사합니다..^^;;

    • reversecore 2010/02/02 00:31 댓글주소 | 수정 | 삭제

      뭉이님, 안녕하세요.

      제 블로그 글들을 정리하는 중에 제가 미처 읽지 못한 댓글이 있었네요.

      제 예제 소스에는 리턴값 체크, 예외 처리 등이 미흡한게 사실입니다. 코드의 간결성을 위해서 그렇게 한것인데요.

      말씀하신대로 초보자 분들께서는 이를 활용할 때 분명 어려움을 겪으실 것 같네요. 좀 더 방법을 보완해 보도록 하겠습니다.

      가령 설명에 쓰이는 코드는 간단히 개념위주로... 제공되는 소스 파일에는 완벽한 에러처리 코드를 추가하는 방법으로 말이죠.

      그리고 최근에 저도 Windows 7 환경을 갖췄습니다. 앞으로는 XP 와 7 에서 동시에 테스트해서 제공할 예정입니다.

      좋은 의견 주셔서 감사합니다.

      또 한번 제가 성장할 수 있는 소중한 기회가 되었습니다.

      감사합니다.

  9. 비셔스 2010/02/06 22:33 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요~ 공부하던 중에 궁금한게 생겨서 질문을 남깁니다.

    GetModuleHandle(NULL)은 결국 프로세스의 핸들값을 가져오는것이던데.

    (pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA); 을 보면
    hMod는 ImageBase값 같은데 맞나요?

    그렇다면 프로세스의 핸들값이라는게 ImageBase의 값을 말하는건가요?

    ImageBase의 값이 맞다면 PE파일에 적힌것하고는 틀리게 되는데 왜 그런지 궁금합니다.

    • reversecore 2010/02/08 23:48 댓글주소 | 수정 | 삭제

      비셔스님, 안녕하세요.

      1)
      GetModuleHandle(NULL) => ImageBase 맞습니다. ^^

      이 값은 '프로세스 핸들' 과는 다릅니다.
      (용어가 헷갈리긴 합니다만...)

      보통 프로세스 핸들이라 하면, CreateProcess(), OpenProcess() 의 리턴값을 말하지요.

      2)
      실제 프로세스 메모리에 로딩된 base 주소가 파일의 ImageBase 주소와 틀리다면, VISTA 이상의 OS 에서 ASLR 기능이 동작한 것입니다.

      아래 글을 참고하시기 바랍니다.

      http://www.reversecore.com/69

      다른 질문 있으시면 올려주세요~ ^^

      감사합니다.

  10. graythief 2010/03/11 21:18 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요~~지금 IAT HOOK를 공부하고 있습니다 그런데
    제가 나름 책이나 인터넷 보고 짜고 있는데요
    pThunk->u1.Function = (DWORD)pfnNew;
    저 위에 있는 붉은 부분 형변환이 정말 되는지 알고 싶습니다 proc 형인 pfnNew형이 DWORD로 변환 시키려고하면 cannot convert from 'unsigned long' to 'unsigned long *' 이런 오류가 생기는데요 혹시나 알고 계신가 해서 여쭈어봅니다
    이런식으로 (DWORD*)pfnNewProc 하면 당연히 가능하긴 합니다만 아직 코드가 완성되지 않는상태에서 에러를잡다보니 제값이 들어가는지도 확인이 불가능해서요..
    물론 저코드는 아니구요 그냥 제가 짜고 필요한 코드는 찾아서 짜는 -_-;;코드입니다 쿨럭;;;

    • reversecore 2010/03/11 22:12 댓글주소 | 수정 | 삭제

      graythief님, 안녕하세요.

      pThunk->u1.Function = (DWORD)pfnNew;
      형변환을 문의하셨네요.

      형변환 잘 됩니다. (VC++ 2008 기준)

      (DWORD*)pfnNewProc 으로 하면 오히려 에러가 납니다.
      L-value 가 DWORD 타입이거든요.

      혹시 자꾸 에러가 발생하면 그 부분의 소스를 올려주시면 제가 봐드릴께요~

      감사합니다.

  11. 롤키 2010/03/24 13:36 댓글주소 | 수정 | 삭제 | 댓글

    맨 마지막에 함수로 복귀할때
    캐스팅을 pfsetwindowtextW로 하셨는데.. 무슨타입인지 궁금합니다.

    • reversecore 2010/03/25 23:36 댓글주소 | 수정 | 삭제

      롤키님, 안녕하세요~

      혹시 아래 코드를 말씀하시는 건지요?
      return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString);

      본문에 첨부된 hookiat.cpp 에 보시면 아래와 같이 함수 포인터를 정의하였습니다.

      typedef BOOL (WINAPI *PFSETWINDOWTEXTW)(HWND hWnd, LPWSTR lpString);

      즉, 원본 SetWindowTextW() 함수입니다.

      감사합니다.

  12. Ezbeat 2010/04/19 11:09 댓글주소 | 수정 | 삭제 | 댓글

    소스코드를 보다가 궁금한 점이 있는데요.
    __cdecl, __stdcall 이 두개의 차이점은 알고 있는데요.
    MySetWindowTextW 함수를 __stdcall로 하는지 이유를 모르겠어서요 ㅜㅜ

    • reversecore 2010/04/19 23:26 댓글주소 | 수정 | 삭제

      calling convention 에 대해서 질문을 하셨네요.

      마침 제 블로그에 그에 대해 설명한 글이 있습니다.

      http://www.reversecore.com/13
      아마 여기에 원하시는 설명이 있을것입니다.

      또한 Win32 API 는 모두 __stdcall 형식이므로 후킹함수인 MySetWindowTextW 또한 __stdcall 형식으로 해줘야 하는것입니다.

      감사합니다.

  13. 보름♬달★콤 2010/06/13 22:46 댓글주소 | 수정 | 삭제 | 댓글

    계속 질문 드리게 되네요.

    IAT의 함수 주소를 바꿔치기할 함수 주소로 바꾸기 전에
    VirtualProtect를 통해 속성을 변경하는 코드가 있는데요,

    "Windows 시스템 실행파일의 구조와 원리/이호동(한빛미디어)" 책에 보면 위의 과정이 빠져있습니다.
    실제로 제가 책대로 테스트를 해보니 자꾸 실패하더라고요.

    그래서 reversecore님 코드를 따라서 VirtualProtect 코드를 삽입하니까 되네요.
    원래 IAT의 경우 r/w속성 아닌가요? 그런데 왜 VIrtualProtect로 속성 변경하는 과정이 필요한가요?

    제가 테스트 하는 환경은 windows7에서 notepad.exe인데요,
    windows7에 포함된 notepad.exe PE파일 자체가 IAT가 포함된 섹션의 write속성을 빼버린 건가요?

    • reversecore 2010/06/15 00:25 댓글주소 | 수정 | 삭제

      안녕하세요.

      VirtualProtect() 로 메모리 속성을 변경해주는 것이 맞습니다.

      calc.exe 또는 notepad.exe 의 예를 들면 IAT 는 ".text" 섹션에 있는데, 이 ".text" 섹션은 PAGE_EXECUTE_READ (0x20) 속성입니다. 이를 PAGE_EXECUTE_READWRITE(0x40) 속성으로 변경해야 IAT 에 값을 쓸 수 있습니다.

      그렇다면 시스템은 어떻게 IAT 에 정확한 API 의 주소를 써넣을 수 있었을까 하는 질문이 남습니다.

      이 질문은 더 나아가 ".text" 섹션의 메모리 영역이 읽기 속성인데 어떻게 코드를 써 넣을수 있었을까 하는 질문으로 확장시킬 수 있습니다.

      시스템은 프로세스를 생성할때 필요한 메모리를 확보하고, 파일 이미지를 메모리에 로딩시킨 후, IAT 를 세팅한 다음, 각 섹션의 메모리 속성을 세팅하는 것입니다. 그 이후부터는 함부로 쓸 수 없지요.

      감사합니다.

  14. Usemap 2010/10/04 16:52 댓글주소 | 수정 | 삭제 | 댓글

    글 감사히 읽었습니다.

    그런데 저기 위에 있는 DllMain 프로시저는 Dll이 인젝션 되었을 그 때부터 실행되는데..

    그런데 비주얼베이직 6.0 에서는 클래스 모듈로 dll을 컴파일 했습니다.

    그런데 DllMain 이라고 프로시저 이름을 설정하거나, Class_Initialize 라는 이벤트를 넣어도 인젝션

    되었을 그 시점에서도 실행이 안되도라구요. c++ 에서는 자동으로 DllMain이 실행되도록 설정하

    는건가요?

    • reversecore 2010/10/05 21:06 댓글주소 | 수정 | 삭제

      안녕하세요.

      Win32 API 로 제작하는 DLL 파일은 로딩되면 무조건 DllMain() 이 실행됩니다.

      VB 로 만드는 DLL 이라고 해서 구조가 다르지 않을것 같은데요...
      해당 파일을 제 이메일로 보내주시면 봐드리겠습니다.

      파일 확장자를 dllxx 등으로 변경하신 후 아래 계정으로 보내주세요.

      reversecore@gmail.com

      감사합니다.

  15. Usemap 2010/11/13 11:36 댓글주소 | 수정 | 삭제 | 댓글

    으음... 아무리 DLLMain을 찾아봐도 없네요.

    Win32 API 는 VB에서 선언해야 쓸 수 있는데

    DllMain 은 선언형도 없는데 이걸 어떻게 쓸지 고민이네요.

    VB 6.0에서는 어떻게 DllMain을 쓸수 있는거죠?

    • reversecore 2010/11/15 02:52 댓글주소 | 수정 | 삭제

      안녕하세요.

      제가 VB 개발 경험이 없어서 큰 도움이 못되어 드려 죄송합니다.

      아마 VB 에서 DLL 프로젝트 생성하는 법이 따로 있을 것입니다.

      * DllMain() 이 겉으로 드러나지는 않고 VB 내부에서 사용하겠지요.

      감사합니다.

  16. 이형우 2010/12/23 10:29 댓글주소 | 수정 | 삭제 | 댓글

    DLL 소스를 VS 6.0 에ㅓ 컴파일 하려고하니

    f( pThunk->u1.Function == (DWORD)pfnOrg )
    : no conversion from 'unsigned long' to 'unsigned long * 언사인 필요라고 나오네요
    형을 제맘대로 바꾸고싶지만 에러날까봐 여쭙습니다

    • reversecore 2010/12/23 15:41 댓글주소 | 수정 | 삭제

      안녕하세요.

      구조체 정의에 따르면 pThunk->u1.Function 는 DWORD 타입입니다.

      typedef struct _IMAGE_THUNK_DATA32 {
      union {
      DWORD ForwarderString; // PBYTE
      DWORD Function; // PDWORD
      DWORD Ordinal;
      DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME
      } u1;
      } IMAGE_THUNK_DATA32;
      typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

      만약에 잘 안되시면 컴파일러가 원하는 대로 타입케스팅 해서 컴파일 해보시기 바랍니다.

      메일 남겨주시면 VC++ 6.0 프로젝트 파일을 보내드리겠습니다.

      감사합니다.

  17. 팔극진 2011/12/14 11:33 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요~

    강의 보면서 궁금한 부분이 있어서 질문드립니다.

    아래 보면은 hmod 인 image base 주소를 dwRVA에 더해서 pImportDesc에 넣어줬는데요~

    그 아래 table찾아가는 부분에서 왜 또 hMod를 더해 주는건가요?

    이미 pImportDesc 여기에는 image base + dwRVA해서 import address table까지 찾아간거 아닌가요? 또 더해주면 이상한 주소로 가야되는게 정상 아닌가요?

    아직 허접해서 이해가 안되네요 ㅜㅠ

    보시고 답변 부탁드립니다. 수고하세요~!!

    pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);
    ....
    ....
    szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);

    • reversecore 2011/12/21 01:28 댓글주소 | 수정 | 삭제

      안녕하세요.

      szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);

      위 코드 말씀이시죠?

      szLibName 에 VA(Virtual Address = 32bit 가상 메모리 주소) 저장하기 위해서 인데요.

      pImportDesc->Name 의 값이 실제로는 RVA 이기 때문에 hMod(ImageBase) 를 더해줘야 제대로 VA 값이 되는 것입니다.

      감사합니다.

  18. 팔극진 2012/01/16 15:46 댓글주소 | 수정 | 삭제 | 댓글

    언제나 친절한 답변 감사드립니다. ^^
    이해가 쉽게 되었습니다.~!


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