지금까지 우리가 원하는 DLL 을 "실행중인" 프로세스에 강제로 Injection 시키는 방법에 대해서 알아봤습니다.

이번에는 접근 방법을 바꿔서 아예 대상 프로그램 "파일을 직접 수정"하여 DLL 을 강제로 로딩하도록 해보겠습니다.
이 방법은 한 번 적용해 놓으면 (별도의 Injection 과정없이) 프로세스가 시작할 때마다 원하는 DLL 을 로딩하게 만들 수 있습니다. 일종의 크랙이라고 생각하시면 됩니다.

목표는 notepad.exe 파일을 직접 수정하여 실행 시 myhack3.dll 을 로딩하도록 만드는 것입니다.
이를 위해서는 PE Header 를 자유자재로 다룰 수 있는 실력이 필요합니다.

IDT(Import Directory Table)와 기타 PE File Format 에 대해서는 아래의 글을 참조하시기 바랍니다.

- PE(Portable Executable) File Format (6) - PE Header



myhack3.dll


먼저 로딩 시킬 DLL 의 소스를 보겠습니다.

// myhack2.cpp
// ReverseCore

include "windows.h"

#define DEF_CMD  "c:\\Program Files\\Internet Explorer\\iexplore.exe"
#define DEF_ADDR "www.naver.com"

#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) BOOL start()
{
    char szCmd[MAX_PATH] = {0,};
    STARTUPINFO si = {0,};
    PROCESS_INFORMATION pi = {0,};

    si.cb = sizeof(STARTUPINFO);
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;

    // IE 를 hidden window 로 실행시켜 naver 에 접속합니다.
    wsprintf(szCmd, "%s %s", DEF_CMD, DEF_ADDR);
    if( !CreateProcess(NULL, (LPTSTR)(LPCTSTR)szCmd, 
                       NULL, NULL, FALSE,
                       NORMAL_PRIORITY_CLASS,
                       NULL, NULL, &si, &pi) )
        return FALSE;

    if( pi.hProcess != NULL )
        CloseHandle(pi.hProcess);

    return TRUE;
}
#ifdef __cplusplus
}
#endif

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    switch( fdwReason )
    {
        case DLL_PROCESS_ATTACH :
            start();
            break;
    }
  
    return TRUE;
}

myhack3.dll


기능은 지난번과 마찬가지로 IE 를 hidden 속성으로 실행시켜 Naver 초기화면에 접속시키는 것입니다.

가장 눈에 띄는 것은 start() EXPORT 함수입니다.

PE 파일에서 어떤 DLL 을 IMPORT 한다는 것은 코드내에서 그 DLL 의 EXPORT 함수를 호출한다는 의미입니다. 그래서 myhack3.dll 파일은 형식적인 완전성을 위해 EXPORT 함수를 제공해야 합니다. (실제로 notepad.exe 내부에는 myhack3.dll!start() 함수를 호출하는 코드 같은건 존재하지 않습니다.)

프로그래밍에서 DLL 의 IMPORT 는 컴파일러/링커가 해주지만, 저는 이 작업을 PE View 와 Hex Editor 만을 가지고 수동으로 해보겠습니다. (제 환경은 WinXP SP3 KOR 버전입니다. OS 와 SP 버전에 따라서 방법이 약간 틀려질 수 있습니다.)



파일 변경 아이디어 

notepad.exe 파일이 실행될 때 자동으로 myhack3.dll 파일을 로딩하기 위해서는 PE Header 의 IMPORT DIRECTORY TABLE 에 myhack3.dll 을 (IMPORT 하도록) 추가해야 합니다.

notepad.exe 원본과 제가 수정한 notepad_hack.exe 를 첨부합니다.

notepad.exe

notepad_hack.exe


PE View 를 이용하여 notepad.exe 의 IDT 의 주소를 확인해 보겠습니다. IMAGE_OPTIONAL_HEADER 의 IMPORT Table RVA 값이 바로 IDT 의 RVA 입니다.


<Fig. 1>

위 그림에서 IDT 주소는 RVA 로 7604 입니다. 이를 PE View 로 따라가 보겠습니다. (PE View 의 주소 보기 옵션을 "RVA" 로 해주세요.)



<Fig. 2>

IDT 는 <Fig. 2> 와 같이 IMAGE_IMPORT_DESCRIPTOR(이하 IID) 구조체 배열로 되어 있으며, 마지막은 NULL 구조체로 끝납니다. IMPORT 하는 DLL 파일 하나당 IID 구조체 하나가 필요합니다. (IID 구조체 하나의 크기는 14h bytes)

PE View 의 주소 보기 옵션을 "File Offset" 으로 변환하면 IDT 의 file offset 은 6A04 입니다.
Hex editor 를 이용해 6A04 주소를 찾으면 아래 그림과 같습니다.


<Fig. 3>

IDT 는 file offset 으로 6A04 ~ 6ACC 범위에 있으며, 전체 크기는 C8 입니다.
마지막 NULL 구조체를 포함하여 총 10개의 IID 구조체가 있습니다. 

처음에 전 IDT 마지막에 myhack3.dll 을 위한 IID 구조체를 추가하려 했습니다만, 더 이상 추가할 공간이 없습니다. (마지막 NULL 구조체는 꼭 필요하거든요.)

그래서 Hex editor 로 notepad.exe 의 빈 영역(사용되지 않는 영역)을 찾아 보았습니다.
<Fig. 4> 에서 보이는 것처럼 마침 파일 끝부분에 알맞은 크기가 비어 있네요.

* 만약 파일에 빈 영역이 보이지 않는다면 파일 끝에 새로운 섹션을 추가하거나, 또는 마지막 섹션의 크기를 늘리는 방법을 사용해야 합니다.


<Fig. 4>

바로 이 위치(file offset = 10710, RVA = 13310)에 새로운 IID 를 만들것입니다.

주의 하실 점은 저 영역(file:10710 ~ 107FF, RVA:13310 ~ 133FF)이 과연 메모리에 로딩되는 영역인지 확인하는 것입니다.
파일에 존재한다고 해서 반드시 메모리에 로딩되는 것은 아닙니다. 섹션 헤더에 명시된 영역만 메모리에 로딩됩니다.


notepad 의 마지막 섹션 헤더(".rsrc") 를 살펴보겠습니다.


<Fig. 5>

RVA = B000 이고 VirtualSize = 8304 이므로 메모리에 로딩되는 영역은 RVA = 13304 까지입니다.
하지만 <Fig. 4> 의 파란색 영역(RVA:13310 ~ 133FF)은 포함되지 않는 영역입니다.

그렇다면 이부분은 메모리에 로딩되지 않을까요?
실제로는 메모리에 로딩됩니다. 이유는 notepad 에서 IMAGE_OPTIONAL_HEADER 의 SectionAlignment 값이 1000 이기 때문에 마지막 섹션(".rsrc")헤더에 명시된 위치(RVA=13304)는 실제로 14000 까지 확장됩니다. (기본 단위의 배수)

디버거에서 확인해 보겠습니다.

<Fig. 6>

위 그림을 보시면 notepad 프로세스 메모리에서 마지막 섹션(".rsrc")의 VirtualSize 값이 9000 으로 확장된 것을 보실 수 있습니다. <Fig. 5> 의 실제 VirtualSize 값은 8304 였으나 SectionAlignment (1000) 의 배수인 9000 으로 자동 확장된 것입니다.

따라서 RVA = 13310 위치에 새로운 IID 를 만드는 것은 문제가 없습니다.



IDT(Import Directory Table) 변경


notepad.exe 를 복사하셔서 이름을 notepad_hack.exe 로 변경해주세요.
이제부터 notepad_hack.exe 파일을 가지고 변경 작업을 진행합니다.

#1. IMPORT Table 의 RVA 값 변경

PE View 를 기준으로 설명드리겠습니다.
IMAGE_OPTIONAL_HEADER 의 IMPORT Table 구조체 멤버는 IDT 의 위치와 크기를 알려줍니다. <Fig. 1> 을 보시면 현재 IMPORT Table 의 RVA 값은 7604 입니다. Hex editor 에서 이 값을 새로운 IDT 의 RVA 값인 13310 으로 변경합니다. 그리고 Size 값을 기존 C8 에 14 (IID 구조체 크기) 를 더한 값인 DC 로 변경합니다. (Size 멤버는 수정 하지 않아도 됩니다.)


<Fig. 7>

PE View 로 확인하면 아래 그림과 같습니다.

<Fig. 8>

이제 notepad 실행 시 PE Loader 는 IDT 가 RVA = 13310 에 존재한다고 간주합니다.

#2. BOUND IMPORT TABLE 사용 안함


PE HEADER 에서 BOUND IMPORT TABLE 은 DLL 로딩 속도를 조금 향상시킬 수 있는 기법입니다. 이 값을 그냥 놔둬도 문제 없는 경우가 많지만, 제가 테스트할 때는 myhack3.dll 이 정상동작하지 않았습니다. (편하게 작업하시려면 이 값을 무조건 0 으로 밀어버리시기 바랍니다.)

Hex editor 를 이용해서 BOUND IMPORT Table 영역(RVA, Size)을 0 으로 밀어버립니다.

<Fig. 9>

PE View 로 확인해 볼까요?

<Fig. 10>

#3. 새로운 IDT 생성

기존 IDT (file = 6A04 ~ 6ACB, RVA = 7604 ~ 76CB) 를 복사하여 새로운 위치(file = 10710, RVA = 13310)에 붙여넣고, 나머지 지저분한 데이타들은 깔끔하게 NULL 로 밀어버립니다.

<Fig. 11>

이 상태에서 아래와 같이 myhack3.dll 을 위한 IID 를 구성하여 새로운 IDT 끝에 추가시킵니다.
(각 멤버의 데이타에 대해서는 아래에서 따로 설명드리겠습니다.)

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            
        DWORD   OriginalFirstThunk;       // INT(Import Name Table) => 8750
    };

    DWORD   TimeDateStamp;                // => FFFFFFFF

    DWORD   ForwarderChain;               // => FFFFFFFF
    DWORD   Name;                         // library name => 8760
    DWORD   FirstThunk;                   // IAT(Import Address Table) => 1348
} IMAGE_IMPORT_DESCRIPTOR;


아래는 Hex editor 로 작업한 그림입니다. (IDT 마지막에 myhack3.dll 을 위한 IID 를 추가)

<Fig. 12>

#4. IID(IMAGE_IMPORT_DESCRIPTOR) 구성

앞 단계에서 추가한 IID 구조체 멤버들이 정상적으로 동작하기 위해서 적절한 세팅이 필요합니다.

먼저 INT(Import Name Table) 와 Name 멤버 설명입니다. 
이 값들은 각각 RVA = 8750 (file = 7B50), RVA = 8760 (file = 7B60) 입니다.

참고로 이 위치는 첫번째 섹션(".text")의 끝부분으로써 빈 영역입니다. 제가 편의상 이 영역을 선정한 것이며 다른 위치에 선정하셔도 상관 없습니다.

일단 Hex Editor 로 RVA = 8750 (file = 7B50) 위치로 가서 아래 그림과 같이 값을 입력합니다.

<Fig. 13>

위 그림에 나타난 값들의 의미를 설명드리겠습니다.

INT 는 쉽게 말해서 RVA 배열인데요, 배열의 각 원소는 Import 하는 함수의 Ordinal(2 bytes) + Func Name String 구조체의 주소(RVA) 를 나타내며 배열의 끝은 NULL 입니다. 

<Fig. 13> 에서 INT 에는 1 개의 원소가 있고 그 값은 RVA 8770 입니다. RVA 8770 으로 가보면 Import 하려는 함수의 Ordinal (2 bytes) 와 함수 이름 문자열이 나타납니다.

Name 은 Import 하는 함수를 제공하는 DLL 파일의 이름 문자열 입니다. "myhack3.dll" 이라고 보이시죠?

마지막으로 IID 의 IAT(Import Address Table) 멤버에 대해 설명드리겠습니다.

#3 에서 IAT 값을 RVA = 1348 (file = 748) 로 세팅하였습니다. 참고로 이 위치는 원래 notepad 의 IAT 영역의 끝 부분입니다.
(INT 데이타와 IAT 데이타는 각각 뭉쳐서 존재합니다.)


<Fig. 14>

IAT 역시 RVA 배열입니다. 각 원소는 INT 와 같은 값을 가져도 좋고 NULL 을 가져도 좋습니다. INT 가 정확하다면 IAT 는 다른 값을 가져도 상관없습니다. 어차피 실행 시에 PE Loader 에 의해 메모리 상에서 실제 함수 주소로 덮어써집니다. (간혹 NULL 을 넣었을때 에러는 발생하지 않지만 DLL 이 정상 동작하지 않을 때가 있습니다. 확실한 건 INT 와 같은 값을 써주면 정확히 동작합니다.)

* 반드시 IAT 를 기존 IAT 영역에 쓸 필요는 없습니다만, 프로세스에 따라서 혹은 DLL 에 따라서 정상 동작하지 않는 경우가 있습니다. 세심한 테스트가 필요한 부분입니다.

여기까지 제대로 따라 하셨으면 작업끝입니다.



검증 (Test)


새로 만든 notepad_hack.exe 파일을 PE View 로 열어보겠습니다.


<Fig. 15>

마지막 섹션(".rsrc")로 IDT 가 옮겨졌으며, myhack3.dll 을 IMPORT 시키기 위한 IID 구조체가 정상적으로 세팅된 것을 확인 하실 수 있습니다.


<Fig. 16>

<Fig. 16> 에서 추가된 INT 도 확인 하실 수 있습니다.

notepad_hack.exe 와 myhack3.dll 파일을 같은 폴더에 넣고 notepad_hack.exe 를 실행해 보겠습니다.
Process Explorer 로 확인하면 아래 그림과 같습니다.


<Fig. 17>

notepad_hack.exe 에 myhack3.dll 이 로딩되어서 IE 가 정확하게 실행되는 화면을 보실 수 있습니다.

성공입니다!



Epilogue


설명이 길어서 자칫 복잡하게 느끼실 수 있습니다.

Import Directory Table 에 내가 원하는 dll 을 추가시켜서 실행 시 자동으로 로딩하게 한다는 기본 원리만 확실히 이해하시고, 관련된 PE Header 를 참고하시면 쉽게 하실 수 있으실 겁니다.

단, OS 와 SP 버전에 따라서 PE Loader 의 구현이 조금씩 다를 수 있으니 처음 하시는 분들께서는 약간의 시행 착오를 거치셔야 합니다. 또한 대상 프로세스와 DLL 파일의 프로그래밍에 따라서 약간씩 방법을 달리 적용해야 하는 경우도 있습니다.

그러나 위의 원리를 이해하신다면 어렵지 않게 해내실 걸로 생각합니다.


+---+


다음번에는 프로세스에 Injection 된 DLL 을 강제로 꺼내는 기법인 DLL Ejection 에 대해서 알아보도록 하겠습니다.


ReverseCore



저작자 표시 비영리 변경 금지
신고
  1. NIKA 2009.08.14 14:27 신고 댓글주소 | 수정 | 삭제 | 댓글

    멋진글 잘보고 있습니다 ^^

  2. lscpjyoon 2009.09.17 21:52 신고 댓글주소 | 수정 | 삭제 | 댓글

    정말로 명 강의다.

    이 글을 읽으면서 문득 한가지 궁금한것이 있다면

    무슨일을 하시는분 인지 갑자기 궁금해 지네요 ;

    • reversecore 2009.09.17 22:46 신고 댓글주소 | 수정 | 삭제

      lscpjyoon님, 칭찬 감사합니다. ^^
      저는 Reverse Code Engineer 입니다.
      국내의 리버싱 기술 향상, 교류, 전파에 관심이 많습니다.

  3. juhens 2010.01.20 23:10 신고 댓글주소 | 수정 | 삭제 | 댓글

    아우..제가 궁금해했던것입니다...
    고등학생때 거진 3년정도......전공이 이쪽계열이 아니라
    네이버에 질문을해봐도 대답을 해주는사람은 없고........
    우연히 여기서 궁금증을 해결하네요... 정말 감사합니다.

    • reversecore 2010.01.21 01:28 신고 댓글주소 | 수정 | 삭제

      juhens님, 안녕하세요.

      고등학교 때부터 벌써 리버싱에 관심이 있으셨군요.

      도움이 되셨다니 제가 기쁘네요~ ^^

      감사합니다.

  4. LHS 2010.04.04 17:51 신고 댓글주소 | 수정 | 삭제 | 댓글

    와... 현재 리버싱에 관심이 많은 고2 학생에게는 단비와 같은 곳이네요...

    그런데 질문 하나 드려도 될까요?? 제가 이 글을 읽고 적당한 프로그램을 고르고 직접 시도해 보려고 했는데 위의 사진과 좀 다른 점이 있어서요...

    제가 DLL Injection을 시도한 프로그램에서는 모든 DLL의 IMPORT Directory Table에서 Import Name Table RVA, Time Data Stamp, Forwarder Chain이 모두 00000000이네요... 이 글을 읽고 따라해 봤지만 계속 오류 메시지만 뜨고... 어떻게 해야 하나요??
    (아, 또 이상한 점이 있네요... 제가 시도한 프로그램에서는 IMPORT Directory Table, IMPORT Address Table, IMPORT DLL Names, IMPORTS Hints/Names 항목(?)이 모두 rdata 섹션에 있네요... 원래 rdata섹션은 리소스가 있는 곳 아닌가요??

    • reversecore 2010.04.05 23:04 신고 댓글주소 | 수정 | 삭제

      LHS님, 안녕하세요.
      반갑습니다. ^^

      PE 파일에 따라서는 지적하신 3 가지 항목이 0 인 파일도 있을 수 있습니다. 보통 packer 류에 많지요.

      하지만 그런 파일도 Import Address Table RVA 항목은 값이 있을테니, 그걸 따라가면 Import Name Table RVA 와 같은 역할을 합니다.

      그리고 섹션 이름은 개발 도구에 따라서 달라지고요,
      packer 를 사용하면 변경할 수 있으며, 사용자가 수동으로 조작할 수 도 있습니다. 그리 중요한 의미는 없지요.

      또한 Import Directory Table 이 딱히 어느 섹션에 있어야 한다는 규칙같은건 없습니다. 그리고 그 섹션의 이름도 정해진건 없구요.

      리소스는 보통 .rsrc 섹션에 있는 경우도 많지만 위에서 설명드린바와 같이 다른 섹션 이름을 가지기도 합니다.

      PE File Format 은 상당히 느슨한 편이라서 어떤 절대적인 규칙이라고 할 만한 내용은 몇 개 되지 않습니다. 또한 그 규칙들 또한 OS 버전에 따라서 조금씩 다르게 적용되는 현실입니다.

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

      감사합니다.

  5. gamjadory 2010.04.11 00:56 신고 댓글주소 | 수정 | 삭제 | 댓글

    잘 보고 갑니다. 혹 dll 파일을 이용한 메모리 영역 변환에 대한 강좌는 없는지요... ㅠㅠ

    • reversecore 2010.04.11 23:04 신고 댓글주소 | 수정 | 삭제

      gamjadory님, 안녕하세요.

      메모리 영역 변환이란게 어떤 의미인지요?

      일단 프로세스에 침투하고 나면 프로세스 메모리의 내용 혹은 메모리 영역의 속성등은 전부 변경 가능해 집니다.

      혹시 질문하신 내용이 이와 다른 것인가요?

      감사합니다.

  6. 리버싱나그네 2010.08.06 12:58 신고 댓글주소 | 수정 | 삭제 | 댓글

    정말 열심히 강좌를 따라가고 있는데..ㅡㅜ 그림처럼 되지 않는 부분이 있어서요 ㅜㅜ 노트패드의 빈영역 그러니까 사용하지 않는 영역을 어떻게 찾죠? ㅜㅜ 그냥 00000 ~~ 이렇게 쭉~ 되어있는 곳에 복사해서 넣고 import table 값을 복사해서 넣은 주소로 바꿧는데, 자꾸 오류가 나구.. 그림처럼 되지를 않아요 ㅜㅜ 설명을 보면, DINGPADDINGXXPAD 라고 써져있는 곳이 있던데 저는 헥사로 봐두 그런 영역은 없어요 ㅜㅜ 문제가 뭘까요 리버스 코어님 ㅜㅜ(환경 : 윈도우 7)

  7. 리버싱나그네 2010.08.09 20:36 신고 댓글주소 | 수정 | 삭제 | 댓글

    감사합니다 ^^

    • reversecore 2010.08.10 12:39 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      제가 마침 예전에 월간 마소에 기고한 글이 있는데,
      바로 위 글의 내용을 Windows 7 으로 변경한 것입니다.

      아마 원하시는 내용이 이곳에 있을 것 같네요.

      제가 댓글로 따로 써드릴려고 보니... 아무래도 그림과 같이 보시는게 이해가 쉬우실것 같습니다...

      메일 주소를 알려주시면 해당 문서를 보내드리겠습니다. (잡지사와 협업한 문서라 원본 전체를 보내드릴 수 없음을 양해해 주시기 바랍니다.)

      감사합니다.

  8. 2010.08.10 21:02 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

  9. BlueH4G 2010.08.13 21:52 신고 댓글주소 | 수정 | 삭제 | 댓글

    헛,, 이번 글 보고 괜찮은 워게임이 떠올라서, 하나 만들어보네요 ㅋㅋ

    항상 리버스코어에서 많은 정보 얻어갑니다. ㅋ 앞으로도 좋은 글 많이 부탁드려요 xD

  10. binish 2010.08.17 11:47 신고 댓글주소 | 수정 | 삭제 | 댓글

    정말 모든 글에 정성이 깃들여져 있고 내공이 느껴집니다.
    훌륭한 강의에 감사드립니다.

  11. jaew 2011.02.15 16:50 신고 댓글주소 | 수정 | 삭제 | 댓글

    우연히 들어와서 그동안 궁금했던 것들이 해소되네요. 좋은 강의 잘 보았습니다. 감사합니다. !

  12. LinkC 2011.02.16 09:02 신고 댓글주소 | 수정 | 삭제 | 댓글

    IDT를 다른 section 으로 옮길 때 간혹 기존에 IDT가 있던 section 에

    쓰기 속성이 없으면 Access Violation 이 나는 경우가 있더군요.

    좀 더 조사해보면 나올거 같긴 한데 혹시 이 점에 관해 알고 계신가요?

    • reversecore 2011.02.18 22:41 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      수작업으로 IDT 를 변경하고 계신가요?

      ACCESS_VIOLATION 은 아마 IAT 때문일 것으로 생각됩니다. IAT 를 수작업으로 재작성 할때는 쓰기 속성을 주시는 것이 안전합니다. (IAT 는 로딩시에 로더가 실제 주소값을 채워주기 때문입니다.)

      혹시 다른 이유 때문이라면 해당 파일을 간략한 설명과 함께 저에게 보내주시면 제가 한번 봐드리겠습니다.

      reversecore@gmail.com

      감사합니다.

  13. Rony 2011.03.30 11:17 신고 댓글주소 | 수정 | 삭제 | 댓글

    글읽다가 질문이 있어서 글 남깁니다..

    optional_hearer의 import_table 주소를 7604에서 13310으로 변경 한 후에

    왜 size값을 dc로 변경하는지 모르겠습니다. 7606 rva가 가르키는 곳은 import table이고 이곳은

    IDT, 즉 IID구조체의 배열일 탠데요.. 새로 IID를 하나 더 작성한 것도 아닌데 14h를 왜 더해주는거죠?

    • reversecore 2011.04.13 00:11 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      myhack3.dll 을 import 시키기 위해서 실제로 IID 구조체 하나를 추가하였습니다. 그래서 구조체 크기(0x14) 만큼 늘려준 것입니다.

      감사합니다.

  14. SmartSnake 2011.10.27 17:42 신고 댓글주소 | 수정 | 삭제 | 댓글

    위에 virtualAddress는 어디에 명시되있어서 b0000 - (virtualaddress) + 8400 해야되는데
    버추얼 어드레스는어떻게 아셧나요.. ㅠㅠ;;

  15. 알려주세요 2012.06.30 19:06 신고 댓글주소 | 수정 | 삭제 | 댓글

    myhack.dll을 보면은 CreateThread를 통해 ThreadProc을

    호출했는데 이런거를 제외하면 전부 export를 해줘야 하나

    요?

  16. louboutin uk 2013.04.21 00:11 신고 댓글주소 | 수정 | 삭제 | 댓글

    착한 아내와 건강은 남자의 가장 훌륭 한재산이다.

  17. chlehd 2014.01.10 18:17 신고 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요. 내주신 책 읽고 열심히 공부하는 한 뉴비입니다.
    제가 어떤 패커로 packing된 프로그램을 뚫어보려고 하는데요,
    ollydbg로 하나하나 진행하려다 안되서 dll injection으로 뚫을 수 없을까 하는데,
    아무래도 그 프로그램이 CreateRemoteThread() 함수를 감지하는 것 같아서 이전 방법들은 아예
    쓸 수가 없어서 이 방법으로 시도해보려고 합니다.
    만약 패킹이 다 풀린 그 시점에서 jmp하고 난 바로 그때를 감지하는 방법이 있을까요?

  18. yoo870701 2014.08.16 16:15 신고 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요
    잘 안되는 것이 있어 질문드립니다.
    책 내용대로 INT, IAT 모두 셋팅해서 PeView 로 올바르게 셋팅된것을 확인후
    TestView_Patched.exe 를 실행하니
    프로시저 시작지점 을 DLL myhack3.dll 에서 찾을수 없습니다.
    라는 에러를 뿜어냅니다.

    환경은 xp sp3, visual c++ 2010 express입니다.
    myhack3.dll과 TestView_Patched.exe은 당연히 같은폴더에 있습니다.
    뭐가 문제일까요

  19. ㅇㅇ 2015.05.25 02:39 댓글주소 | 수정 | 삭제 | 댓글

    관리자의 승인을 기다리고 있는 댓글입니다

  20. 리버싱스터디 2017.03.31 18:43 댓글주소 | 수정 | 삭제 | 댓글

    관리자의 승인을 기다리고 있는 댓글입니다




IAT (Import Address Table)


PE Header 를 처음 배울때 최대 장벽은 IAT(Import Address Table) 입니다.

IAT 에는 Windows 운영체제의 핵심 개념인 process, memory, DLL 구조 등에 대한 내용이 함축되어 있습니다.
즉, IAT 만 잘 이해해도 Windows 운영체제의 근간을 이해한다고 할 수 있습니다.

IAT 란 쉽게 말해서 프로그램이 어떤 라이브러리에서 어떤 함수를 사용하고 있는지를 기술한 테이블 입니다.



DLL (Dynamic Linked Library)


IAT 를 설명하기 앞서 Windows OS 의 근간을 이루는 DLL(Dynamic Linked Library) 개념을 짚고 넘어가야 합니다.
(뭐든지 이유를 알면 이해하기 쉬운 법이지요...)

DLL 을 우리말로 '동적 연결 라이브러리' 라고 하는데요, 그 이유를 알아 보겠습니다.

16 bit DOS 시절에는 DLL 개념이 없었습니다. 그냥 'Library' 만 존재하였습니다.

예를 들면 C 언어에서 printf() 함수를 사용할 때 컴파일러는 C 라이브러리에서
해당 함수의 binary 코드를 그대로 가져와서 프로그램에 삽입(포함)시켜 버렸습니다.
즉, 실행 파일내에 printf() 함수의 바이너리 코드를 가지고 있는 것입니다.

Windows OS 에서는 Multi-Tasking 을 지원하기 때문에 이러한 라이브러리 포함 방식이 비효율적이 되어 버렸습니다.

32 bit Windows 환경을 제대로 지원하기 위해 기본적으로 매우 많은 라이브러리 함수(process, memory, window, message, etc)를 사용해야 합니다.

여러 프로그램이 동시에 실행되야 하는 상황에서 모든 프로그램마다 위와 같이 동일한 라이브러리가 포함되어서 실행된다면
심각한 메모리 낭비를 불러오게 됩니다. (물론 디스크 공간의 낭비도 무시할 수 없지요.)

그래서 Windows OS 설계자들은 (필요에 의해) 아래와 같은 DLL 개념을 고안해 내었습니다.

"프로그램내에 라이브러리를 포함시키지 말고 별도의 파일(DLL)로 구성하여 필요할 때마다 불러쓰자."
"일단 한번 로딩된 DLL 의 코드, 리소스는 Memory Mapping 기술로 여러 Process 에서 공유해 쓰자."
"라이브러리가 업데이트 되었을때 해당 DLL 파일만 교체하면 되니 쉽고 편해서 좋다."


실제 DLL 로딩 방식은 2가지 입니다.
프로그램내에서 사용되는 순간에 로딩하고 사용이 끝나면 메모리에서 해제 시키는 방법(Explicit Linking)
프로그램 시작할 때 같이 로딩되어 프로그램 종료 할 때 메모리에서 해제되는 방법(Implicit Linking)이 있습니다.

IAT 는 바로 Implicit Linking 에 대한 매카니즘을 제공하는 역할을 합니다.

IAT 의 확인을 위해 OllyDbg notepad.exe 를 열어보겠습니다.
아래 그림은 kernel32.dll CreateFileW 를 호출하는 코드입니다.


CreateFileW 를 호출할 때 직접 호출하지 않고 01001104 주소에 있는 값을 가져와서 호출합니다.
(모든 API 호출은 이런 방식으로 되어 있습니다.)

01001104 주소는 notepad.exe 의 ".text" 섹션 메모리 영역입니다. (더 정확히는 IAT 메모리 영역입니다.)
01001104 주소의 값은 7C8107F0 이며, 
7C8107F0 주소가 바로 notepad.exe 프로세스 메모리에 로딩된 kernel32.dll 의 CreateFileW 함수 주소입니다.

여기서 한가지 의문이 생깁니다.
"그냥 CALL 7C8107F0 이라고 하면 더 편하고 좋지 않나요?"
컴파일러가 CALL 7C8107F0 이라고 정확히 써줬다면 더 좋지 않냐는 의문이 들 수 있습니다만,
그건 바로 위에서 설명 드렸던 DOS 시절의 방식입니다.

notepad.exe 제작자가 프로그램을 컴파일(생성)하는 순간에는 이 notepad.exe 프로그램이
어떤 Windows(9X, 2K, XP, Vista, etc), 어떤 언어(KOR, ENG, JPN, etc), 어떤 Service Pack 에서
실행 될 지 도저히 알 수 없습니다.

위에서 열거한 모든 환경에서 kernel32.dll 의 버전이 틀려지고, CreateFileW 함수의 위치(주소)가 틀려집니다.
모든 환경에서 CreateFileW 함수 호출을 보장하기 위해서 컴파일러는 CreateFileW 의 실제 주소가 저장될 위치(01001104)를
준비하고 CALL DWORD PTR DS:[1001104] 형식의 명령어를 적어두기만 합니다.


파일이 실행되는 순간 PE Loader 가 01001104 의 위치에 CreateFileW 의 주소를 입력해줍니다.

또 다른 이유는 DLL Relocation 때문입니다.
일반적인 DLL 파일의 ImageBase 값은 10000000h 입니다.

예를 들어 어떤 프로그램이 a.dll 과 b.dll 을 사용한다고 했을때,
PE Loader는 먼저 a.dll 을 ImageBase 값인 메모리 10000000h 에 잘 로딩합니다.
그 다음 b.dll 을 ImageBase 값인 메모리 10000000h 에 로딩하려고 봤더니, 이미 그 주소는 a.dll 이 사용하고 있었습니다.
그래서 PE Loader 는 다른 비어있는 메모리 공간(ex:3E000000h) 을 찾아서 b.dll 을 로딩시켜 줍니다.

이것이 DLL Relocation 이며 실제 주소를 하드코딩 할 수 없는 이유입니다.
또한 PE Header 에서 주소를 나타낼때 VA 를 쓰지 못하고 RVA 를 쓰는 이유이기도 합니다.

* DLL 은 PE Header 에 명시된 ImageBase 에 로딩된다고 보장할 수 없습니다.
 
반면에 process 생성 주체가 되는 EXE 파일은 자신의 ImageBase 에 정확히 로딩되지요. 
  (자신만의 가상 메모리 공간을 가지기 때문입니다.)


이것은 매우 중요한 설명입니다. 다시 한번 잘 읽어보시기 바랍니다.

이제 IAT 의 역할을 이해할 수 있으실 겁니다.
(아래에서 설명드릴 IAT 구조가 왜 이리 복잡해야 하는지에 대해서도 약간 이해가 되실 겁니다.)



IMAGE_IMPORT_DESCRIPTOR


PE 파일은 자신이 어떤 라이브러리를 Import 하고 있는지 IMAGE_IMPORT_DESCRIPTOR 구조체에 명시하고 있습니다.

* Import : library 한테서 서비스(함수)를 제공 받는 일
* Export : library 입장에서 다른 PE 파일에게 서비스(함수)를 제공 하는 일


IMAGE_IMPORT_DESCRIPTOR 구조체는 아래와 같습니다.

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            
        DWORD   OriginalFirstThunk;       // INT(Import Name Table) address (RVA)
    };
    DWORD   TimeDateStamp;
    DWORD   ForwarderChain; 
    DWORD   Name;                         // library name string address (RVA)
    DWORD   FirstThunk;                   // IAT(Import Address Table) address (RVA)
} IMAGE_IMPORT_DESCRIPTOR;

typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;                         // ordinal
    BYTE    Name[1];                      // function name string

} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

* 출처 : Microsoft 의 Visual C++ 에서 제공하는 winnt.h

일반적인 프로그램에서는 여러 개의 Library 를 Import 하기 때문에 
Library 의 갯수 만큼 위 구조체의 배열 형식으로 존재하게 되며, 구조체 배열의 마지막은 NULL 구조체로 끝나게 됩니다.

IMAGE_IMPORT_DESCRIPTOR 구조체에서 중요한 멤버는 아래와 같습니다. (전부 RVA 값을 가집니다.)

  • OriginalFirstThunk : INT(Import Name Table) 의 주소(RVA)
  • Name : Library 이름 문자열의 주소(RVA)
  • FirstThunk : IAT(Import Address Table) 의 주소(RVA)

* PE Header 에서 'Table' 이라고 하면 '배열' 을 뜻합니다.
* INT 와 IAT 는 long type (4 byte 자료형) 배열이고 NULL 로 끝납니다. (크기가 따로 명시되어 있지 않습니다.)
* INT 에서 각 원소의 값은 IMAGE_IMPORT_BY_NAME 구조체 주소값을 가지고 있습니다.
   (IAT 도 같은 값을 가지는 경우가 있습니다.)
* INT 와 IAT 의 크기는 같아야 합니다.


아래 그림은 notepad.exe 의 kernel32.dll 에 대한 IMAGE_IMPORT_DESCRIPTOR 구조를 표시하고 있습니다.


<Fig. IAT 구조>

PE Loader
가 Import 함수 주소를 IAT 에 입력하는 기본적인 순서를 설명드리겠습니다.

1. IID 의 Name 멤버를 읽어서 라이브러리의 이름 문자열("kernel32.dll")을 얻습니다.
2. 해당 라이브러리("kernel32.dll")를 로딩합니다.
3. IID 의 OriginalFirstThunk 멤버를 읽어서 INT 주소를 얻습니다.
4. INT 에서 배열의 값을 하나씩 읽어 해당 IMAGE_IMPORT_BY_NAME 주소(RVA)를 얻습니다.
5. IMAGE_IMPORT_BY_NAME 의 Hint(ordinal) 또는 Name 항목을 이용하여 해당 함수("GetCurrentThreadId")의 시작 주소를 얻습니다.
6. IID 의 FirstThunk(IAT) 멤버를 읽어서 IAT 주소를 얻습니다.
7. 해당 IAT 배열 값에 위에서 구한 함수 주소를 입력합니다.
8. INT 가 끝날때까지 (NULL 을 만날때까지) 위 4 ~ 7 과정을 반복합니다.


위 그림에서는 INT 와 IAT 의 각 원소가 동시에 같은 주소를 가리키고 있지만 그렇지 않은 경우도 많습니다.
(변칙적인 PE 파일에 대해서는 향후 많은 파일을 접해보면서 하나씩 배워 나가야 합니다.)



notepad.exe 를 이용한 실습


실제로 notepad.exe 를 대상으로 하나씩 살펴 보겠습니다.

그런데 실제 IMAGE_IMPORT_DESCRIPTOR 구조체 배열은 PE 파일의 어느 곳에 존재할까요?
PE Header 가 아닌 PE Body 에 위치합니다.

그곳을 찾아가기 위한 정보는 역시 PE Header 에 있습니다.
바로 IMAGE_OPTIONAL_HEADER32.DataDirectory[1].VirtualAddress 값이 
실제 IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 시작 주소 입니다. (RVA 값입니다.)

IMAGE_IMPORT_DESCRIPTOR 구조체 배열을 다른 용어로는 IMPORT Directory Table 이라고도 합니다.
(위 용어를 전부 알아두셔야 남들과 의사소통이 원활해 집니다.)

IMAGE_OPTIONAL_HEADER32.DataDirectory[1] 구조체 값은 아래와 같습니다.
(첫번째 4 byte 가 VirtualAddress, 두번째 4 byte 가 Size 멤버입니다.)


 offset   value   description
----------------------------------------------
...
00000158 00000000 RVA  of EXPORT Directory

0000015C 00000000 size of EXPORT Directory

00000160 00007604 RVA  of IMPORT Directory
00000164 000000C8 size of IMPORT Directory

00000168 0000B000 RVA  of RESOURCE Directory
0000016C 00008304 size of RESOURCE Directory
...

* 위 구조체에 대해 궁금하신 분들께서는 IMAGE_OPTIONAL_HEADER 설명 을 참고하시기 바랍니다.
* DataDirectory 구조체에서 Size 멤버는 중요하지 않습니다. (PE Loader 에서 사용되지 않는 값입니다.)

위 그림에서 보듯이 RVA 가 7604h 이니까 File Offset 은 6A04h 입니다.
파일에서 6A04h 를 보면 아래 그림과 같습니다.


그림에서 파란색으로 표시된 부분이 전부 IMAGE_IMPORT_DESCRIPTOR 구조체 배열이고,
빨간 테두리로 되어 있는 부분은 구조체 배열의 첫번째 원소입니다. 
(참고로 배열의 마지막은 NULL 구조체로 되어 있는 것도 확인 할 수 있습니다.)

빨간 테두리의 IMAGE_IMPORT_DESCRIPTOR 구조체를 각 멤버별로 살펴보겠습니다.

OriginalFirstThunk (INT) = 7990h (file offset : 6D90h)
TimeDateStamp            = FFFFFFFFh
ForwarderChain           = FFFFFFFFh

Name                     = 7AACh (file offset : 6EACh)
FirstThunk (IAT)         = 12C4h (file offset : 6C4h)

우리는 IAT 를 공부하는 입장이기 때문에 hex editor 를 이용하여 하나하나 따라가도록 하겠습니다.
(편의를 위해 위 구조체 값(RVA) 를 미리 file offset 으로 변환해 놓았습니다.)

* RVA 를 file offset 으로 변환하는 방법에 대해서는 IMAGE_SECTION_HEADER 설명을 참고하세요.

그럼 순서대로 진행해 볼까요?


1. 라이브러리 이름 (Name)

Name 멤버를 따라가면 쉽게 구할 수 있습니다. (RVA : 7AACh -> file offset : 6EACh)


2. OriginalFirstThunk - INT(Import Name Table)

OriginalFirstThunk 멤버를 따라 갑니다. (RVA : 7990h -> file offset : 6D90h)


위 그림이 INT 입니다. 주소 배열 형태로 되어 있습니다. (배열의 끝은 NULL 로 되어 있습니다.)
주소값 하나 하나가 각각의 IMAGE_IMPORT_BY_NAME 구조체를 가리키고 있습니다. (<Fig. IAT 구조> 참고)

배열의 첫번째 값인 7A7Ah (RVA) 를 따라가 볼까요?

3. IMAGE_IMPORT_BY_NAME

RVA 값 7A7Ah 는 file offset 으로 6E7Ah 입니다.


앞에 2 byte 는 Hint (ordinal) 로써 라이브러리에서 함수의 고유번호 입니다.
ordinal 뒤로 "PageSetupDlgW" 함수 이름 문자열이 보이시죠? (문자열 마지막은 '\0' - C 언어와 동일)

여기까지 정리하면 INT 는 "함수 이름 주소 배열" 인데 첫번째 원소가 가리키는 함수 이름은 "PageSetupDlgW" 였습니다.

이제 IAT 에 해당 함수가 실제 메모리에 매핑된 주소를 얻어서 (GetProcAddress API 참고) IAT 에 입력하면 됩니다.

4. FirstThunk - IAT (Import Address Table)

IAT 의 RVA 값은 12C4h 이고 file offset 으로는 6C4h 입니다.


위 그림이 "comdlg32.dll" 라이브러리에 해당하는 IAT 입니다.
INT 와 마찬가지로 주소 배열 형태로 되어 있으며 배열의 끝은 NULL 입니다.

IAT 의 첫번째 원소값은 이미 76324906h 로 하드 코딩되어 있습니다.
notepad.exe 파일이 메모리에 로딩될 때 이 값은 위 3번에서 구한 정확한 주소값으로 대체 됩니다.

* 사실 제 시스템(Windows XP SP3) 에서 76324906h 주소는 comdlg32.dll!PageSetupDlgW 함수의 정확한 주소값입니다.
* MS 가 서비스팩을 배포하면서 관련 시스템 파일을 재빌드 할때 이미 정확한 주소를 하드 코딩 한것입니다.
  (일반적인 DLL 은 IAT 에 실제 주소가 하드 코딩되어 있지 않고, INT 와 같은 값을 가지는 경우가 많습니다.)
* 참고로 일반적인 DLL 파일은 ImageBase 가 10000000h 으로 되어 있어서 보통 DLL relocation 이 발생하지만,
   Windows 시스템 DLL 파일들(kernel32, user32, gdi32, etc)은 고유의 ImageBase 가 있어서 
   DLL relocation 이 발생하지 않습니다.


OllyDbg 를 이용해서 notepad.exe 의 IAT 를 확인해 보겠습니다.


notepad.exe 의 ImageBase 값은 01000000h 입니다.
따라서 comdlg32.dll!PageSetupDlgW 함수의 IAT 주소는 010012C4h 이며 76324906h 로 정확한 값이 들어와 있습니다.

* XP SP3 notepad.exe 를 다른 OS (2000, Vista, etc) 혹은 다른 ServicePack(SP1, SP2) 에서 실행하면,
  010012C4h 주소에는 다른 값이 세팅됩니다. (그 OS 혹은 ServicePack 에 있는 comdlg32.dll!PageSetupDlgW 의 주소)


해당 주소(76324906h)로 가면 아래와 같이 comdlg32.dll 의 PageSetupDlgW 함수 시작이 나타납니다.




이상으로 IAT(Import Address Table) 에 대한 기본 설명을 마치겠습니다.

IAT 는 Windows 리버싱에서 중요한 개념이기 때문에 반드시 잘 익혀두셔야 합니다.

향후 변칙적인 IAT 를 가지는 PE Patch 을 볼 때 IAT 를 다시 한번 살펴볼 기회가 있을것입니다.

다음번에는 PE Header 설명의 마지막으로 EAT(Export Address Table) 에 대해서 공부해 보겠습니다.



저작자 표시 비영리 변경 금지
신고
    이전 댓글 더보기
  1. 안녕하세요 2010.08.12 19:37 신고 댓글주소 | 수정 | 삭제 | 댓글

    dll 첫 설명부분에 dll(dynamic linked list)로 되어있네요 linked library가 맞겠죠 ㅎㅎ

  2. Acaicat 2010.08.23 20:38 신고 댓글주소 | 수정 | 삭제 | 댓글

    notepade.exe의 hex edit 내용을 제 컴에서 따라가려고 했는데 맞질 않는데
    제가 윈7 32bit을 쓰는데 그 RVA를 File offset으로 변환하는 방법이 다를 수도 있나요?
    처음에 DataDirectory[1]을 찾아서 변환을 하고 찾아 가잖아요?
    저 같은 경우 RVA가 A048이 나왔거든요?
    그러면 .data 섹션에 있기 때문에 A048 - 9000 + 7C00이 되는데
    그래서 8C48이 나왔어요.
    그런데 이 주소로 이동을 한 후에 그 배열을 보면 NULL로 끝나질 않습니다.

    이건 제가 잘못 이해하고 잘못 따라간 탓일까요?
    혹시나 해서인데 그 PE와 관련된 그림에서 .text .data .rsrc가 있는데,
    제 메모장을 hec edit으로 열어본 내용에는 끝에 .reloc이 더 있는데 이것 때문일까요?

    • reversecore 2010.08.24 22:18 신고 댓글주소 | 수정 | 삭제

      Acaicat 님, 안녕하세요.

      OS 별로 notepad.exe 의 파일 모양은 틀립니다만, RVA <-> RAW 변환 방법은 모두 동일합니다.

      제가 Win7 의 notepad.exe 를 확인해보니...
      A048 은 맞습니다. 다만 이 RVA 는 .data 섹션이 아니라 .text 섹션에 위치합니다.

      따라서 공식은 아래와 같습니다.
      a048 - 1000 + 400 = 9448

      IAT 를 따라 갈때 처음에 좀 어렵다고 느껴지시면 PEView 를 사용해서 값을 확인하면서 진행하셔도 좋습니다.

      감사합니다.

  3. Ezbeat 2010.09.02 13:55 신고 댓글주소 | 수정 | 삭제 | 댓글

    으음.. 예전 내용을 까먹어.. 다시 봐보던중 궁금한 점이 생겨서 질문드려보겠습니다.
    전 실행파일을 notepad.exe로 하지 않고 그냥 VS에서 직접 만든 파일로 했습니다.

    환경 : Windows 7, Visual Studio 2008 ( Release, /MT )

    직접 만든 파일의 IAT를 따라가본 결과 OrigianlFirstThunk에는 아무런 값이 없었습니다.
    하지만 FirstThunk를 따라가보니 사용될 함수명들이 쭉있더군요.
    ( 하드코딩된 API함수의 주소가 아닌 단순 문자열 )

    그리고 메모리에 실행파일을 올린 후 해당 테이블을 보니 실제 API주소가 올라와 있었습니다.
    (FirstThunk에 있던 문자열 주소 부분에 실제 주소로 바뀌어 있었음)

    위 설명에선 OriginalFirstThunk에 있던 문자열을 가지고 실제 주소를 얻어와 FirstThunk가 가리키는 테이블에 실제주소를 넣는다는 말과는 쫌 다르길래 이렇게 질문을 드려봅니다 ;;

    컴파일 환경에 따라 이렇게 달라질수도 있는건가요?? XD

    • ReverseCore 2010.09.04 16:48 신고 댓글주소 | 수정 | 삭제

      네, 그런식으로 만들어진 PE 파일도 많습니다.

      OriginalFirstThunk(INT), FirstThunk(IAT) 에서 보듯이 둘 다 FirstThunk 가 맞습니다.

      PE Loader 는 OriginalFirstThunk 로 API 이름 문자열을 찾을 수 없다면 FirstThunk 로 시도를 합니다.

      둘 중 어디서 이름 문자열을 찾았다 하더라도 그 API 의 실제 로딩된 주소는 (로딩시점에) IAT 에 세팅됩니다.

      실행 압축 파일들을 다양하게 보신다면 별의별 특이한 구조의 IAT 를 볼 수 있습니다.

      감사합니다.

  4. Ezbeat 2010.09.08 08:37 신고 댓글주소 | 수정 | 삭제 | 댓글

    오호.. 그렇군요.. :-) 이해가 잘 되었습니다.
    항상 친절한 답변 감사합니다~!

  5. kyungchan 2010.10.30 01:41 신고 댓글주소 | 수정 | 삭제 | 댓글

    머리에 쏙쏙 들어오네요ㅎㅎ 정말 감사합니다^^;

  6. 리버스코어님... 2011.01.11 21:37 신고 댓글주소 | 수정 | 삭제 | 댓글

    죄송한데 라이브러리가 무엇인지...?

    • reversecore 2011.03.16 21:41 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      쉽게 얘기해서 OS 에서 제공된 함수 모음이라고 생각하시면 됩니다. Win32 API(Application Programming Interface)라고 합니다.

      어떤 OS 던지 라이브러리를 제공해주지 않으면 개발자가 응용프로그램을 개발할 수 없습니다.

      예를 들어서 C 언어의 printf() 함수를 생각해 보겠습니다. 콘솔창에 문자열을 표시하는 라이브러리 함수인데요. 저 함수가 없다면... 제가 직접 화면출력, 키보드입력, 메모리제어, 콘솔창 생성 등을 전부 만들어야 합니다. 거의 미니 OS 를 만드는 셈이지요. ^^

      감사합니다.

  7. 내이름조용범 2011.01.12 10:18 신고 댓글주소 | 수정 | 삭제 | 댓글

    ===================내용정리=======================
    옛날 도스시적때에는 라이브러리 함수를 호출할때 컴파일러가 호출할려는 함수 코드를 그대로 삽입하였다.windows는 멀티캐스팅(동시에 여러가지일)이 가능해서 도스시절의 방식은 비효율적[그래서 windows에선 함수를 그대로 삽입하지않는다는말인가요?]또한 도스때에는 함수주소를 직접사용했는데 윈도우에서는(윈도우 종류가 많으니까...2000/xp 등등)각각 다르기때문에 주소를 그대로 사용하지 않는다.[그러면 라이브러리함수는 모두 주소가 정해져있단 말인가요?,단지 운영체제 종류에 따라 설계가 다를뿐?]그래서 윈도우계열은 어떠한주소에 담아서 쓴다
    =================맞나요?======================

    • reversecore 2011.03.16 22:21 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      제가 답변이 너무 늦었네요~ 죄송합니다.

      네, 개념을 비슷하게 잡으신것 같습니다.

      그런데 약간 거칠은 그 개념을 확실히 자기것으로 만드시려면 PE 파일 포멧의 IAT 와 EAT 를 실제로 따라가 보셔야 합니다. 그 과정을 통해서 개념이 점점 정교해 지는 것이지요.

      감사합니다.

  8. vio 2011.03.14 22:38 신고 댓글주소 | 수정 | 삭제 | 댓글

    번외질문 같은데요
    IMAGE_IMPORT_BY_NAME 구조체에서
    BYTE NAME[1]
    이 변수에 들어있는 값은 함수 이름의 첫번째 글자인가요?

    • reversecore 2011.03.16 21:45 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      그것은 가변적인 크기의 문자열을 표시할 때 사용되는 C 언어만의 기법이라고 보시면 됩니다.

      문자열이 오긴 오는데... 크기는 가변적이기 때문에 배열의 크기를 명확히 지정할 수 는 없습니다.

      사용할 때는 먼저 문자열 크기만큼 메모리를 할당 받아서 사용합니다. 그때 NAME 은 그 문자열의 시작 주소가 됩니다.

      따라서 문의하신 내용에 대한 답변은 첫번째 글자도 맞는 말이지만, 좀 더 기술적인 표현은 문자열의 시작주소, 즉 문자열이란 얘기지요. ^^

      감사합니다.

    • vio 2011.03.18 00:11 신고 댓글주소 | 수정 | 삭제

      어떻게 구현이 되는건지 감이안잡히는군요..
      저도 처음에 그 추축을 해보긴 했지만
      포인터변수도 아닌 배열이기때문에,,
      char asdf[10];
      asdf=0x1234;//불가
      배열의 이름이 나타내는 주소값은 변경할 수 없잖아요
      포인터변수가 아닌 저렇게 표현한건 합당한 이유가있을거같은데 전 모르겠군요 ㅠㅠ..
      코딩하는게 아니라 시스템이 돌아가는 것이기 때문에 가능한건가..? 신기하네요

    • vio 2011.03.25 16:38 신고 댓글주소 | 수정 | 삭제

      제가 너무 코딩을 하는쪽의 입장에서 생각한거같기도하네요
      어차피 컴파일러가 올바를 위치에 주소값을 써주는 방식일텐데요
      그런데 다른 주소값음 DWORD로 표시하고 저부분은 BYTE 배열로 한 이유는..궁금하네요ㅎ

    • reversecore 2011.04.13 10:35 신고 댓글주소 | 수정 | 삭제

      일종의 C 언어의 프로그래밍의 기법인데요...

      아래와 같은 코드로 동작한다고 이해하시면 될 것 같습니다.

      BYTE *pBuf = new BYTE[1024];
      memset(pBuf, 0, 1024);

      PIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME)pBuf;

      ...

      // Name 세팅
      // 구조체 정의에 따르면 Name 은 1 바이트 크기의 배열 이지만...
      // 현재 pImportByName->Name 의 크기는 1022 바이트가 됩니다.
      strcpy(pImportByName->Name, "ReverseCore");


      BYTE 배열을 쓰는 이유는 위 코드에서 보시는 것처럼 직접 그 위치에 문자열을 배치시키기 위함이지요.


      C 언어 컴파일러는 자료형의 타입을 체크하기 때문에 프로그래밍이 까다로울 수 있는데요. 개념을 이해하시면 타입 변환이 훨씬 쉬워집니다.

      감사합니다.

  9. TeamKhan 2011.04.08 20:00 신고 댓글주소 | 수정 | 삭제 | 댓글

    댓글에 달린 질문들을 읽어보다가 문득 궁금증이 생겨서 여쭤보게되는데요
    e_lfanew 에 값에따라서 IMAGE_OPTIONAL_HEADER 값이 바뀐다고 하셧는데
    e_lfanew 에는 NT_HEADER의 주소값이 명시되어잇는데
    NT_HEADER주소값에 따라서 당연히 INAGE_OPTION_HEADER의 주소 값이 바뀌는건 알겟는데요
    어떻게 NT_HEADER시작지점에서부터 여기까지가 FILE_HEADER이다~
    여기서부터 여기까지는 OPTIONAL_HEADER 이내~인걸 파악할수 잇으신건가요 ?
    FILE_HEADER 와 OPTIONAL_HEADER 들의
    크기를 알고계셔서 그런걸 파악할수 잇으셧던건가여?
    그리고 OPTIONAL_HEADER의 시작위치를
    파악하엿을떄 virtual[1].address의 시작지점을
    어떻게 파악하실수 잇으셧던건가요 ?
    상세하게 설명좀 부탁드려요ㅠ
    그리고 PEview 로 분석하는 습관을 들이는게좋나요
    아니면 헥스에디터가 좋은가요 ?
    저는 개인적으로 핵스에디터로 설명을 해주셔서
    그런지 핵스에디터가 더 편한거같기도한데..

    • reversecore 2011.04.12 21:48 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      IMAGE_FILE_HEADER, IMAGE_OPTIONAL_HEADER 위치를 어떻게 그리 쉽게 알 수 있었냐는 질문이신거죠?

      그건 IMAGE_NT_HEADERS 구조체의 정의를 살펴보시면 쉽게 이해하실 수 있습니다.

      typedef struct _IMAGE_NT_HEADERS {
      DWORD Signature;
      IMAGE_FILE_HEADER FileHeader;
      IMAGE_OPTIONAL_HEADER32 OptionalHeader;
      } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

      바로 IMAGE_FILE_HEADER 와 IMAGE_OPTIONAL_HEADER32 는 IMAGE_NT_HEADERS 의 멤버이기 때문이지요. C 언어의 구조체는 크기가 결정되어 있기 때문에 IMAGE_NT_HEADERS 구조체의 시작 주소를 알면 내부 멤버는 쉽게 알아낼 수 있습니다. ^^

      참고로 IMAGE_NT_HEADERS 위치는 e_lfanew 값으로 결정되구요.

      그리고 처음 공부하시는 입장에서는 PEView 와 Hex Editor 를 같이 놓고 보시는 것이 좋습니다.

      감사합니다.

  10. reversingk 2011.05.18 13:42 신고 댓글주소 | 수정 | 삭제 | 댓글

    좋은 강의 감사 드립니다.^^ 강의를 보다 궁굼한점이 생겨 이렇게 질문을 드립니다.

    IAT하는 과정에서 IMAGE_IMPORT_DESCRIPTOR 구조체 값중 제일 먼저
    NAME 멤버 값을 가져와 LOADLIBRARY 함수를 이용하여 DLL을 인젝션 시킨다고 되어있는데여
    KELNER32.DLL이 매핑 되어 있지않다면 LOADLIBRARY 함수도 사용 못하지 안나여?? 그럼
    LOADLIBRARY 함수를 사용하기 위해서 KELNER32.dll은 따로 매핑이 되는지 된다면 어떻게 매핑이 이루어 지는지 궁굼해서 질문을 드리게되었습니다^^

    • reversecore 2011.05.18 19:56 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      좋은 질문입니다~ 개념을 제대로 이해하신것 같습니다.

      지금 다시 본문을 읽어보니 <Fig. IAT 구조> 그림 밑의 박스 설명에 제가 분명히 "LoadLibrary("kernel32.dll") 과 GetProcAddress("GetCurrentThreadId") 라고 명시를 하였군요.

      개념적으로 저 API 의 의미와 같다는 뜻이었는데요. 확실히 오해의 소지가 있습니다. 수정 하도록 하겠습니다. ^^

      reversingk 님께서도 개념적으로 같다고 이해해 주시면 되겠습니다. 실제 프로세스를 생성하고 ntdll.dll, kernel32.dll 라이브러리를 로딩하는 것은 커널의 역할 입니다.

      감사합니다.

  11. reversingk 2011.05.18 16:47 신고 댓글주소 | 수정 | 삭제 | 댓글

    하나더 궁굼한것이 생겼습니다.^^;;
    귀찬게 하는거 아닌지 모르겠네여 ㅠㅠ

    windows7에서 실습을 하였습니다. 대상 notepad.exe

    강의를 보고 하나씩 따라가다보니 dll파일은 ADVAPI32.DLL파일이면
    첫 임포트 시키는 함수는 RegSetValuExW 함수입니다.
    peview 에서 RegSetValouExW 함수의 주소가 77 C7 1C 82로 나왔습니다.

    ImageBase(01000000) + FirstTrunk(1000) = 01001000
    상대주소를 구하고 ollydbg로 확인을 해보니 그런 주소가 없다고 나오기레

    ollydbg의 기능중 All Intermodule calls를 이용해서 RegSetValueExW함수를 찾아
    들어 가보니 상대주소 값이 B81000으로 나오고 RegSetValueExW 함수의 주소는
    76 7f 1c 82로 나왔습니다.

    이상해서 peviwe 로 구한 regSetValueExW (77 c7 1c 82)값이 였는데 왜 이런 값이 나올까
    생각 하다. 이 peviwe 의 RegSetValuExW값과 ollydbg RegSetValueExW 값을 빼보니00B80000 으로 나왔습니다. 여기서 00B80000 + FirstTrunk(1000)값을 더하니 메모리에 매핑 되었을때의
    IAT 주소가 구해졌습니다.

    그리고 ollydbg로 notepad.exe를 디버깅 할때 마다 그 값이 틀려 지던군요
    ImageBase 값은 010000000 으로 나왔는데 왜 이렇게 값이 나오는지 궁굼 합니다.

    pe 파일 포멧 (4) 에서도 제가 질문한 것이 비슷 한거 같은데 그 이유가 같다면
    여기 하나에만 답해주셔도 감사 하겠습니다 ^^

    매번 좋은 강의 보고 질문만 드려 귀찬게 하는것 같다 마음이 아픈데 어떻게 보담을
    드려야 할지 모르겠습니다(__) 너무 감사합니다. !!!

    • reversecore 2011.05.18 19:57 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      Vista, 7 에서 추가된 ASLR(Address Space Layout Randomization) 기법 때문입니다.

      아래 포스트를 참고하시기 바랍니다.

      http://www.reversecore.com/69

      감사합니다.

  12. 정민 2011.05.30 16:20 신고 댓글주소 | 수정 | 삭제 | 댓글

    설명이 너무 쉽고 깔끔하게 잘 되있네요~ 정말 감사합니다! ^^

  13. 말라뮤트 2011.06.08 14:59 신고 댓글주소 | 수정 | 삭제 | 댓글

    첫번째 값이 INT라고 하셨는데. .

    그럼 Characteristics 값은 어디에 저장이 된것인가요 ?

    C언어를 너무 오래전에 배워서 잘 기억이 안나는데..

    공용체는 맴버들끼리 메모리를 공유하지 않던가요.. ?

    그 메모리에 INT 주소값이 써진다면 Characteristics 멤버는 왜 있는 것인지.. 그냥 궁금증이 생기네요..

  14. tintin 2011.06.23 02:10 신고 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요?
    강의를 보다가 이상한게 나와서 질문드립니다.ㅠㅠ
    제 컴은 xp구요

    강의쭉 따라가다가

    OriginalFirstThunk (INT) = 7840h (file offset : 6C40h)
    TimeDateStamp = FFFFFFFFh
    ForwarderChain = FFFFFFFFh
    Name = 7AFAh (file offset : 6EFAh)
    FirstThunk (IAT) = 1174h (file offset : 574h)

    이렇게 구해서 접근해봤는데

    함수이름은 은 shell32.dragfinish 로 잘 나오거든요

    사진참고 : http://blog.naver.com/0orjsgmlo0/90116377273

    이와같이 틀린주소값이 나오네요..

    주소끝이 9d / 18 로 틀린데 왜그런걸까요..?

    실수했나해서 여러번해봣는데도 결과는똑같네요

  15. 질문 2011.09.23 16:18 신고 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요. PE format 관련 질문이 있습니다.
    언패커를 공부하다가 INT가 비어있는 경우가 종종 보이는데요, 이럴때는 IAT에 Hint와 Name의 주소가 들어있었습니다. 이럴때에는 PELoader가 IAT를 따라가서 Hint(ordinal)과 Name을 읽어서 그 IAT 위치에 바로 실제함수주소를 적어주나요?
    그리고 한번 PEloader에 의해 IAT가 채워진 다음에는 INT는 더이상 참조되지 않나요?

    • reversecore 2011.09.28 20:47 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      네, 말씀하신 내용 대로입니다.

      PE Loader 는 INT 에서 정보를 얻을 수 없다면 IAT 에서 시도해 봅니다.

      감사합니다.

  16. 2012.02.03 15:31 신고 댓글주소 | 수정 | 삭제 | 댓글

    INT에 있는 함수의 시작주소(HINT)가 해당 dll를 dependency walker로 열어봤을때 의 HINT값인건가요? 이 HINT값을 보고 entry point의 값과 dll의 preferred base값을 이용해서 구한 값이 함수의 주소 값이되는건가요? 그리고 HINT값 뒤의 스트링은 DLL name 처럼 함수의 이름으로 사용되는게 맞나요? ㅎ

  17. 리버서 API 2012.04.11 01:41 신고 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요. 요즘 PE 를 계속 실습 중 인데요.
    OS 는 winXP 서비스팩3 입니다.

    계산기, 지뢰찾기 kernel32.dll 의 IAT 안에 있는 값과 디버깅 값이 다릅니다. PEview 로 체크해봐도 77--- 로 되어있더라구요. 디버깅해보면 7C--- 로 되어있구요.

    이유가 뭔가요?

  18. 초급 2012.07.28 19:49 신고 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요.
    공부하다 설명에서 궁금한점이 있어 글남깁니다.

    "그림에서 보듯이 RVA 가 7604h 이니까 File Offset 은 6A04h" 라고 하셨는데요
    7604h는 값에서 알수 있지만 6A04h가 도출하기까지의 식을 알 수 있을까요?
    RVA, RAW등 설명을 찬찬히 보면서 하룻동안 꼬박 생각했는데도 알 수 없어 글남깁니다.
    답변 기다리겠습니다.

    • 떼르미 2012.07.31 13:42 신고 댓글주소 | 수정 | 삭제

      바로 앞 강좌를 보시면 image section header 구조체 설명하는 부분 아래쪽에 notepad.exe 헤더를 덤프뜬 내용이 있습니다.

      거기서 7604h 주소는 .text image section header 영역에 포함되어 있으니까

      7604h - 1000h(RVA of .text image section header) + 400h(pointer to raw data of .text image section header) = 6A04h

      이렇게 되네요.

  19. Ange 2012.12.11 18:17 신고 댓글주소 | 수정 | 삭제 | 댓글

    윈도우 실행 정보 @ http://pe101.corkami.com

  20. 조영용 2013.01.23 09:32 신고 댓글주소 | 수정 | 삭제 | 댓글

    win7 32bit 환경에서 HxD를 사용하여 notepad를 열었는데
    offset 160에서 04 76 00 00 값이 아니라 48 A0 00 00이라는 다른 값이 나옵니다.

    다른 분들의 댓글을 살펴보니 win7 32bit 환경에서 A048 번지는 .data영역이 아니라 .text영역이라고 하셨는데 어째서 인가요?

    .data 영역의 범위는 01009000 에서 0100B000 인데 A048 은 당연히 .data 영역 아닌가요?





티스토리 툴바