10.15(월) 실습-1( 가상메모리, MMF, DLL Inject )

2007/10/15 10:00

1. 가상메모리 기본( VirtualAlloc )
[ more.. | less.. ]
int main()
{
    const int size = 1024 * 1024 * 100;    // 100M

    char* p1 = (char*)VirtualAlloc( (void*)0, // 원하는 주소(64k배수), 자동으로 할당 0
                             size*15,            // 원하는 크기(4k 단위)
                             MEM_RESERVE,        // 예약만
                             PAGE_NOACCESS );    // 보호 속성(어짜피 접근못하므로 NOACCESS)

    printf( "예약된 주소 : %p\n", p1 );
    //*p1 = 'a';    // 예약만 된 주소를 사용하는 경우. - RunTime Error 발생..

    char* p2 = p1;
    for( int i = 0; i < 15; ++i )
    {
        getch();

        void* p3 = VirtualAlloc( p2, size, MEM_COMMIT, PAGE_READWRITE );

        printf( "확정된 메모리 : %d\n", p3 );
        p2 = p2 + size;    // 다음 확정할 주소
    }

    VirtualFree( p1, size*15, MEM_DECOMMIT );    // 다시 예약 상태로
    VirtualFree( p1, 0, MEM_RELEASE );        // 다시 Free상태로
                                                                // MEM_RELEASE 지정시 반드시 두번째인자는 0!!
}

2. MMF 기본
[ more.. | less.. ]
/////////////////////////////////////////////////////////////////////////////////////
// exe 로드해서 IMAGE_DOS_HEADER 읽기

int
main()
{
    // MMF 1단계 - 화일 Create 또는 Open
    HANDLE hFile = CreateFile(
                               "c:\\windows\\system32\\calc.exe",
                               GENERIC_READ | GENERIC_WRITE,
                               FILE_SHARE_READ,
                               0,
                               OPEN_EXISTING,
                               FILE_ATTRIBUTE_NORMAL, 0 );
    // MMF 2단계 - 화일 매핑 KO 생성
    HANDLE hMap = CreateFileMapping( hFile,        // 연결할 파일
                                     0,                                 // 매핑KO( 섹션 Object ) 보안 속성
                                     PAGE_READONLY,         //
                                     0, sizeof(IMAGE_DOS_HEADER),    // 매핑할 크기
                                     "map" );                        // 매핑 객체 이름

    // MMF 3단계 - 매핑 객체를 사용해서 화일을 가상주소와 연결한다.
    IMAGE_DOS_HEADER* p = (IMAGE_DOS_HEADER*) MapViewOfFileEx( hMap, FILE_MAP_READ,
           0, 0,        // Offset( 파일 생성시는 무조건 0 기존파일 Open시에만 적용 )
           0,           // 크기( 0 매핑 객체에 지정된 크기를 사용 )
           (void*)0x00600000 ); // 원하는 주소

    if ( p == 0 ) printf( "error\n" );
    else
    {
        printf( "매핑된 주소 : %p\n", p );
        printf( "%x\n", p->e_magic );    // 모든 실행 파일은 "MZ" 로 시작한다.
    }
    UnmapViewOfFile( p );
    CloseHandle( hMap );
    CloseHandle( hFile );
}

/////////////////////////////////////////////////////////////////////////////////////
// FILE 로드해서 버퍼처럼 쓰기~
int main()
{
    // MMF 1단계 - 화일 Create 또는 Open
    HANDLE hFile = CreateFile( "a.txt", GENERIC_READ | GENERIC_WRITE,
                               FILE_SHARE_READ,
                               0,
                               CREATE_ALWAYS,
                               FILE_ATTRIBUTE_NORMAL, 0 );
    // MMF 2단계 - 화일 매핑 KO 생성
    HANDLE hMap = CreateFileMapping( hFile,        // 연결할 파일
                                     0,            // 매핑KO( 섹션 Object ) 보안 속성
                                     PAGE_READWRITE,    //
                                     0, sizeof(IMAGE_DOS_HEADER),    // 매핑할 크기
                                     "map" );    // 매핑 객체 이름

    // MMF 3단계 - 매핑 객체를 사용해서 화일을 가상주소와 연결한다.
    char* p = (char*) MapViewOfFileEx( hMap, FILE_MAP_WRITE,
           0, 0,        // Offset( 파일 생성시는 무조건 0 기존파일 Open시에만 적용 )
           0,           // 크기( 0 매핑 객체에 지정된 크기를 사용 )
           (void*)0x00600000 ); // 원하는 주소

    if ( p == 0 ) printf( "error\n" );
    else
    {
        printf( "매핑된 주소 : %p\n", p );

        strcpy( p, "Hello" );    // 파일에 출력
        p[40] = 'C';
    }
    UnmapViewOfFile( p );
    CloseHandle( hMap );
    CloseHandle( hFile );
}

3. MMF 를 이용한 그리기 통신( B가 A에게 오버헤드를 발생시킨다.ㅜ_ㅜ )
[ more.. | less.. ]
//-------------------------------------------------------------------------------
// A.cpp - 좌표를 pagefile에 써준다.

struct LINE
{
    POINTS ptFrom;
    POINTS ptTo;
};

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static HANDLE hEvent, hMap, hEvent2;
    static POINTS ptFrom, ptTo;
    static LINE* pLine;

    switch( msg )
    {   
    case WM_CREATE:
        {
            hEvent = CreateEvent( 0, 0, 0, "e1" );
            hEvent2 = CreateEvent( 0, 0, TRUE, "e2" );

            // 매핑 객체 생성
            hMap = CreateFileMapping( (HANDLE)-1, // Page 화일을 사용하라.
                                      0, PAGE_READWRITE,
                                      0, sizeof(LINE), "map" );
            pLine = (LINE*) MapViewOfFile( hMap, FILE_MAP_WRITE, 0, 0, 0 );
        }
        return 0;

    case WM_LBUTTONDOWN:
        ptFrom = ptTo = MAKEPOINTS( lParam );
        return 0;
    case WM_MOUSEMOVE:
        if( wParam & MK_LBUTTON )
        {
            POINTS pt = MAKEPOINTS( lParam );
            HDC hdc = GetDC( hwnd );

            MoveToEx( hdc, ptFrom.x, ptFrom.y, 0 );
            LineTo  ( hdc, pt.x, pt.y );

            ReleaseDC( hwnd, hdc );

            // MMF에 넣는다.
            WaitForSingleObject( hEvent2, INFINITE ); // 꺼내 갔는가를 확인..

            pLine->ptFrom = ptFrom;
            pLine->ptTo = pt;

            // Event Signal
            SetEvent( hEvent );

            ptFrom = pt;
        }
        return 0;

    case WM_DESTROY:
        UnmapViewOfFile( pLine );
        CloseHandle( hEvent );
        CloseHandle( hMap );

        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc( hwnd, msg, wParam, lParam);
}
//-------------------------------------------------------------------------------
// B.cpp - A.cpp 에서 입력한 pagefile을 읽는다.

struct LINE
{
    POINTS ptFrom;
    POINTS ptTo;
};

DWORD WINAPI Work( void* p )
{
    HWND hwnd = (HWND)p;
    HANDLE hEvent = OpenEvent( EVENT_ALL_ACCESS, 0, "e1" );
    HANDLE hEvent2 = OpenEvent( EVENT_ALL_ACCESS, 0, "e2" );

    HANDLE hMap   = OpenFileMapping( FILE_MAP_ALL_ACCESS, 0, "map" );

    LINE* pLine = (LINE*) MapViewOfFile( hMap, FILE_MAP_READ, 0, 0, 0 );

    while( 1 )
    {
        WaitForSingleObject( hEvent, INFINITE );
       
        // 약간의 지연이 발생..
        Sleep(10);

        // 이제 MMF에서 꺼내서 그린다.
        HDC hdc = GetDC( hwnd );

        MoveToEx( hdc, pLine->ptFrom.x, pLine->ptFrom.y, 0 );
        LineTo( hdc,   pLine->ptTo.x,   pLine->ptTo.y );

        // 꺼내 갔음을 알려준다.
        SetEvent( hEvent2 );

        ReleaseDC( hwnd, hdc );
    }
    return 0;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch( msg )
    {   
    case WM_CREATE:
        CloseHandle( CreateThread( 0, 0, Work, (void*)hwnd, 0, 0 ) );
        return 0;

    case WM_LBUTTONDOWN:
        return 0;

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



4. DLL Inject

[ more.. | less.. ]
// spy.dll 만들기~
WNDPROC old;
// 계산기의 메세지 처리함수를 가로챌 함수

LRESULT CALLBACK foo( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
    case WM_COMMAND:    // 메뉴 선택
        switch( LOWORD(wParam) )
        {
        case 304:    //    "공학용" 메뉴
            MessageBox( 0, "^^", "", MB_OK );
            return 0;
        }
        break;
    }
    // 나머지 메세지는 모두 원래의 함수로 보낸다.
    return CallWindowProc( old, hwnd, msg, wParam, lParam );
}

// DLL이 프로세스에 매핑될때 자동으로 호출되는 함수
BOOL WINAPI DllMain( HANDLE h, DWORD r, LPVOID how )
{
    if ( r == DLL_PROCESS_ATTACH )
    {
        HWND hwnd = FindWindow( 0, "계산기" );

        old = (WNDPROC)SetWindowLong( hwnd, GWL_WNDPROC, (LONG)foo );
    }
    return TRUE;    // 반드시 TRUE를 리턴.
}

////////////////////////////////////////////////////////////////////////////////
// DLLInject.cpp

void DLLInject( DWORD pid, const char* path )
{
    HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, 0, pid );

    ///////////////////////////////////////////////////////////
    // LoadLibrary 의 주소를 찾는다.
    HMODULE hDll = GetModuleHandle( "Kernel32.dll" );

    PTHREAD_START_ROUTINE f = (PTHREAD_START_ROUTINE)
                                GetProcAddress( hDll, "LoadLibraryA" );

    // PTHREAD_START_ROUTINE 은 미리 정의된 스레드 함수 포인터 type이다.
    void* p = VirtualAllocEx( hProcess, 0, strlen(path)+1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE );

    // Inject 할 DLL의 경로를 계산기 메모리에 복사해 놓는다.
    DWORD len;
    WriteProcessMemory( hProcess, p, path, strlen(path)+1, &len );

    ///////////////////////////////////////////////////////////
    // 다른 프로세스에 스레드를 생성한다.
    HANDLE hThread = CreateRemoteThread( hProcess, 0, 0,
                                         f, (void*)p,        // 함수, 파라미터
                                         0, 0 );
}

int main()
{
    HWND hwnd = FindWindow( 0, "계산기" );

    if ( hwnd == 0 )
    {
        printf( "계산기 부터 실행하세요..\n" );
        return 0;
    }
    // 계산기 프로세스에 DLL을 강제로 넣는다.
    DWORD pid;
    DWORD tid = GetWindowThreadProcessId( hwnd, &pid );

    DLLInject( pid, "C:\\spy.dll" );
}

Tags

System, 실습