10.16(화) 실습 ( 스택,힙, 훅, DLL )

2007/10/16 15:12

1. 스택의 원리
[ more.. | less.. ]
void print( MEMORY_BASIC_INFORMATION* p );

int main()
{
    char* addr = (char*)0x0;

    MEMORY_BASIC_INFORMATION mbi;

    while( addr < (char*)0x80000000 )
    {
        // 가상 주소 공간의 상태를 조사한다.
        VirtualQuery( addr, &mbi, sizeof(mbi) );

        print( &mbi );

        addr += mbi.RegionSize;
    }
}

void print( MEMORY_BASIC_INFORMATION* p )
{
    char* s = (char*)p->BaseAddress;
    char* e = s + p->RegionSize - 1;

    printf( "%08x ~ %08x : ", s, e );

    char* state = "unknown";
    switch ( p->State )
    {
    case MEM_FREE:        state = "Free    "; break;
    case MEM_RESERVE:    state = "Reserve";    break;
    case MEM_COMMIT:    state = "Commit";    break;
    }
    printf("%s\n", state );
}

2. 기본힙과 새로운 힙 만들기
[ more.. | less.. ]
int main()
{
    // 프로세스의 기본힙을 구한다.
    HANDLE h1 = GetProcessHeap();
    // 새로운 힙을 만든다.. 동기화 되지 않게 하고..
    // 메모리 할당 실패시 예외가 나오게 한다.
    HANDLE h2 = HeapCreate( HEAP_NO_SERIALIZE | HEAP_GENERATE_EXCEPTIONS,
                            1024*1024, // 초기크기
                            0 );       // 최대 크기( 0은 자동증가(Commit) )
    // 이제 힙에서 메모리를 할당한다.
    void* p1 = HeapAlloc( h1, HEAP_ZERO_MEMORY, 10 );
    void* p2 = HeapAlloc( h2, HEAP_ZERO_MEMORY, 10 );

    printf( "%p\n", p1 );
    printf( "%p\n", p2 );

    HeapFree( h1, 0, p1 );
    HeapFree( h2, 0, p2 );

    // 힙 파괴
    HeapDestroy( h2 );
}

[ more.. | less.. ]
// 자신 전용의 힙을 사용하는 Node
class Node
{
    static HANDLE my_heap;
    static int count;
public:
    void* operator new( size_t sz )
    {
        if( my_heap == 0 )
        {
            my_heap = HeapCreate( 0, 1024*1024, 0 );
        }
        void* p = HeapAlloc( my_heap, HEAP_ZERO_MEMORY, sz );
        ++count;
        return p;
    }
    void operator delete( void* p )
    {
        HeapFree( my_heap, 0, p );
        --count;
        if( count == 0 )    HeapDestroy( my_heap );
    }
};

HANDLE Node::my_heap = 0;
int Node::count = 0;

int main()
{
    Node* p1 = new Node;
    int*  p2 = new int;
}

3. 동적 TLS를 사용한 DLL ( strtok 문제 해결 )
[ more.. | less.. ]
// 정적 TLS : LoadLibrary를 사용하게 되면 문제가 된다.
// __declspec(thread) char* buf = 0;

DWORD index = 0; // 동적 TLS index로 사용.

extern "C" _declspec(dllexport) void SetData( char* s )
{
    char* buf = (char*) TlsGetValue( index );
    strcpy( buf, s );
}
extern "C" _declspec(dllexport) void GetData( char* s )
{
    char* buf = (char*) TlsGetValue( index );
    strcpy( s, buf );
}

BOOL
WINAPI DllMain( HANDLE hDll,    // DLL 핸들, 결국 주소
                     DWORD  r,                    // DllMain 이 호출된 이유
                     LPVOID how                 // DLL이 Load된 방식( 0이면 LoadLibrary 사용 )
                    )
{
    char* buf = 0;

    switch( r )
    {
    case DLL_PROCESS_ATTACH:
///////////////////////////////////////////////////////////////////////////////////
        {
            // 특정기능을 수행
            foo();

            // DLL을 자동으로 내린다.
            // FreeLibrary( hDll ); // Dll이 내려가면 메모리에서 사라지므로 안됨.

            // 강제로 dll를 내리고 스레드를 종료한다.
            // 아래 함수를 만든이가 DLL안에서 스스로를 Free하는 기회를 주기 위해서.
            FreeLibraryAndExitThread( hDll, 0 );

            // DLL이 Load되면 동시에 스레드를 만든다.
            // HANDLE h = CreateThread( 0, 0, foo, 0, 0, 0 );    // 바이러스 검사
            // 새로운 스레드가 Wait에 막혀서 절대 실행될 수 없다.
            // 주스레드가 아직 DllMain을 나가지 못했으므로 foo를 실행하는
            // 새로운 스레드는 DllMain을 실행하기 위해 Block된 상태..
            // WaitForSingleObject( h, INFINITE );
        }
///////////////////////////////////////////////////////////////////////////////////
        //buf = (char*)HeapAlloc( GetProcessHeap(), 0, 1000 );    // 기본힙으로부터 1000byte를 할당..

        index = TlsAlloc();    // 동적 TLS에 빈슬롯 할당
        // 주스레드를 위해서 메모리 할당.
        buf = (char*)HeapAlloc( GetProcessHeap(), 0, 1000 );
        // 주소를 TLS에 보관
        TlsSetValue( index, (void*)buf );

        printf( "DLL이 Load되었습니다. 주소 : %p\n", hDll );
        printf( "%s\n", how ? "implicit linking" : "explicit linking");    // 명시적, 암시적 로드를 판단.!!
        break;
       
    case DLL_PROCESS_DETACH:
        //HeapFree( GetProcessHeap(), 0, buf );

        TlsFree( index );
        printf( "DLL이 프로세스에서 해지 됩니다.\n" );
        break;

    case DLL_THREAD_ATTACH:
        // 새로운 스레드가 생성 될 때마다 메모리를 할당해서 Thread-Safe를 보장!!
        buf = (char*)HeapAlloc( GetProcessHeap(), 0, 1000 );
        TlsSetValue( index, (void*)buf );

        printf( "스레드가 생성됩니다.\n" );
        break;

    case DLL_THREAD_DETACH:
        buf = (char*) TlsGetValue( index );
        HeapFree( GetProcessHeap(), 0, buf );

        printf( "스레드가 파괴됩니다.\n" );
        break;
    }
    return TRUE;
}

4. DLL이 로드되는 과정 보기(위예제랑 연동)
[ more.. | less.. ]
DWORD WINAPI foo( void* p )
{
    Sleep(5000);
    return 0;
}

int main()
{
    getch();
    HMODULE hDll = LoadLibrary( "DllMain.dll" );

    CloseHandle( CreateThread( 0, 0, foo, 0, 0, 0 ) );    // 스레드 생성

    getch();
    FreeLibrary( hDll );

    getch();
}

5. Hook Dll 만들기
[ more.. | less.. ]
HMODULE g_hDll;        // 훅 함수를 가진 DLL의 핸들

#pragma data_seg( "AAA" )
HHOOK    g_hHook = 0;    // 훅 핸들
#pragma data_seg()

#pragma comment(linker, "/section:AAA,RWS")


BOOL WINAPI DllMain( HANDLE h, DWORD r, LPVOID how )
{
    if ( r == DLL_PROCESS_ATTACH )
    {
        g_hDll = (HMODULE)h;
    }
    return TRUE;
}
// 훅필터 함수
LRESULT CALLBACK GetMsgProc( int nCode, WPARAM wParam, LPARAM lParam )
{
    if ( nCode == HC_ACTION )        // 메시지를 꺼내 가는중..
    {
        MSG* pMsg = (MSG*)lParam;    // 꺼내가고 있는 메시지.

        if ( pMsg->message == WM_CHAR )
        {
            // 다른 곳으로 보낸다.( N/W또는 IPC사용, 또는 파일로 저장 )
            HWND hwnd = FindWindow( 0, "Friend" );
            PostMessage( hwnd, WM_USER+100, pMsg->wParam, pMsg->lParam );
        }
    }
    // 다음 번 훅필터 함수에게 전달한다.( Hook Chain )
    return CallNextHookEx( g_hHook, nCode, wParam, lParam );
}

// 훅을 설치하는 함수
extern "C" __declspec(dllexport) void Install( DWORD tid )
{
    g_hHook = SetWindowsHookEx( WH_GETMESSAGE,    // 훅의 종료(15가지)
                                GetMsgProc,        // 훅함수
                                g_hDll,            // 훅함수를 가진 DLL핸들(주소)
                                tid );            // 훅을 설치할 스레드( 0:Global hook )
}
extern "C" __declspec(dllexport) void Uninstall()
{
    UnhookWindowsHookEx( g_hHook );
}

6. Hook 한 메시지 읽어오기( Friend)
[ more.. | less.. ]
typedef void(*INSTALL)(DWORD);
typedef void(*UNINSTALL)();

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static HMODULE hDll;
    static INSTALL Install;
    static UNINSTALL Uninstall;
    static HWND hEdit;

    switch( msg )
    {   
    case WM_CREATE:

        hEdit = CreateWindow( "edit", "", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE,
                              10, 10, 500, 500, hwnd, (HMENU)1, 0, 0 );
        hDll = LoadLibrary( "HookDll.dll" );
        Install   = (INSTALL)GetProcAddress( hDll, "Install" );
        Uninstall = (UNINSTALL)GetProcAddress( hDll, "Uninstall" );

        Install(0); // global hook 설치
        return 0;

    case WM_USER+100:
        // Edit에 WM_CHAR를 보내면 알아서 출력해 줄것이다.
        SendMessage( hEdit, WM_CHAR, wParam, lParam );
        return 0;

    case WM_DESTROY:
        Uninstall();
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc( hwnd, msg, wParam, lParam);
}

Tags

System, 실습