'Study/API'에 해당되는 글 37건

  1. 2007/10/10 9.13(목) 실습-1
  2. 2007/10/10 9.12(수) 이론
  3. 2007/10/09 System - Object Table
  4. 2007/10/05 GetPrivateProfileString Function (ini 읽기) (3)
  5. 2007/09/15 API 디자인 발표영상
  6. 2007/09/12 예제 모음
  7. 2007/09/11 sprintf 와 wsprintf 이야기 (2)

9.13(목) 실습-1

from Study/API 2007/10/10 11:18 view 17251
// 1. 가상주소값 알아보기
int
x = 0;

int main()
{
    cout << main << endl;
    cout << &x << endl;

    // Exe의 가상주소를 구한다.( 이값이 instance )
    void* p1 = GetModuleHandle(0);
    void* p2 = GetModuleHandle("ntdll.dll");
    void* p3 = GetModuleHandle("kernel32.dll");
    void* p4 = GetModuleHandle("user32.dll");
    void* p5 = GetModuleHandle("gdi32.dll");

    cout << "exe 주소        : " << p1 << endl;
    cout << "ntdll.dll 주소        : " << p2 << endl;
    cout << "kernel32.dll 주소    : " << p3 << endl;
    cout << "user32 주소        : " << p4 << endl;
    cout << "gdi32 주소        : " << p5 << endl;

    // 가상주소를 알때 모듈이름을 구하는 함수
    char name[256];
    GetModuleFileName((HMODULE)0x00400000, name, 256);

    cout << name << endl;
   
    // 이 함수는 User32.dll 에 있다.
    // 또한 내부적으로 Gdi32.dll의 함수를 사용한다.
    MessageBox( 0, "","",MB_OK);
   
    return 0;
}

// 2. DLL의 명시적 연결 : 헤더파일과 .lib 파일이 필요 없다. 단지 DLL만 있으면 된다.
//                        단, DLL이 가진 함수의 모양(signaure)는 알아야 한다.
typedef void (*F)(const char* s);

int main()
{
    getch();

    HMODULE hDll = LoadLibrary( "MyDll.dll" );

    if( hDll == 0 )
    {
        printf("DLL을 찾을 수 없습니다.\n");
        return -1;
    }
   
    printf("DLL이 Load된 주소 : %p\n", hDll );
    //------------------------------------------
    // DLL 에서 함수 찾기
    F f = (F)GetProcAddress( hDll, "PutStringA" );

    if( f == 0 )
        printf("PutString 함수가 없습니다.\n");
    else
        f("AAA");

    getch();
    FreeLibrary( hDll );

    return 0;
}

// 3. 여러가지 이야기
// 링커에게 exe의 종류를 알려준다.
#pragma comment( linker, "/subststem:console" )

// 함수 호출 규약
// __cdecl : 모든 CPU가 지원하는 기계어 코드 생성
// __stdcall : 메모리 사용량이 __cdecl 보다 적다
// __fastcall : 가장 빠르게 동작하는 기계어 생성

// this : C++ 멤버 함수가 호출되는 원리 - this가 ecx로 전달
// naked : 디바이스 드라이버 만들 때 주로 사용 - stack Frame이 생략된다.

// 4. 윈도우 열거(EnumWindows)
BOOL
CALLBACK foo( HWND hwnd,  LPARAM lParam );
int main()
{
   
// 모든 Top-Level 윈도우를 열거한다.
    EnumWindows(
        foo,   
// Callback 함수
        0 );    // 함수의 2번째 인자로 보낼 Data
}

BOOL CALLBACK foo( HWND hwnd,  LPARAM lParam )
{
   
char cname[256];
   
char title[256];

   
if ( IsWindowVisible( hwnd ) == FALSE ) return TRUE; // 안보이는 윈도우.
    if ( GetWindowTextLength( hwnd ) == FALSE ) return TRUE; // 캡션 글자가 없는경우

    // 작업관리자는 바탕화면을 열거 하지 않는다.
    // 작업관리자 자신도 열거 하지 않는다.

    // 소유된 윈도우도 열거 하지 않는다.(OWNER)
    // Getparent() : 부모 또는 소유 윈도우의 핸들을 구한다.
    if ( GetParent(hwnd) != 0 ) return TRUE;    // 소유된 윈도우 제거

    if ( IsWindowVisible( hwnd ) == FALSE ) return TRUE; // 안보이는 윈도우.

    GetClassName( hwnd, cname, 256 );
    GetWindowText( hwnd, title, 256 );

    cout << hex << hwnd <<
"-" << cname << "-" << title << endl;

   
return TRUE;    // 계속 찾으라는 의미
                    // 여기서 FALSE를 리턴하면 더이상 열거하지 않는다.
}

// 5. lib를 만들어 보기.
// a.h
// 라이브러리 파일에 있는 모든 함수의 선언을 제공해 준다.
// C++ 에서 사용할 경우를 대비 해야 한다.
#ifdef __cplusplus
extern "C" {
#endif

int Add(int , int);

#ifdef __cplusplus
}
#endif
// a.c
int Add( int a, int b )
{
   
return a + b;
}
// 빌드하면 DLib.lib 가 만들어진다.

// 6. 정적 라이브러라 사용하기.
// 정적 라이브러리를 사용하는 방법
// 1. 헤더 include
#include "aa.h"   
// 2. lib를 링커에게 알려준다.
#pragma comment(lib, "DLib.lib")

int CppAdd( int a, int b )
{
   
return a + b;
}
int CppAdd( int a, int b, int c )
{
   
return a + b + c;
}

int main()
{
   
int n = Add(1, 2);
    printf(
"%d\n", n);
    n = CppAdd(1, 2);
    printf(
"%d\n", n);
    n = CppAdd(1, 2, 3);
    printf(
"%d\n", n);
}



Tag | ,

9.12(수) 이론

from Study/API 2007/10/10 11:00 view 14001
  1. PE 구조 - -_-....아.
    1. An In-Depth Look into the Win32 Portable Executable File Format - Matt Pietrek

  2. 프로그램, 프로세스, 스레드
    1. 메시지 Q는 스레드당 하나이다. 스레드가 생성한 윈도우 창은 스레드와 운명을 같이 한다.
    2. I/O 는 제외.. 프로세스가 관리한다.

  3. 가상메모리
    1. RAM의 물리메모리가 512MB라면 HDD에 가상메모리(pagefile.sys)를 1~2배정도 설정한다.

  4. 잘못된 메모리 참조
    1. 3.1(OS) 시절 일때는 메모리를 잘못 참조 하면 OS가 죽었다. Ex)*0x2100 = 20; 사망!!
    2. 하지만 페이지 Table < 실제주소:1000 와 가상주소:5000>을 갖게 되면서 안전해졌다.(?)
    3. 페이지 Table은 프로세스마다 하나씩 있으며 가상주소내에서 프로세스가 실행된다.
    4. 다른 프로그램의 주소를 침법하지 않는다는 보장이 생겼다.

  5. 페이지 Table







    1. A.exe 내의 improt(dll)을 메모리로 올린다. 이 dll은 자기를 불러낸 PageTable에서 관리하는데 이를 모듈이라 하며 가상 주소를 갖게 된다. 이 가상주소를 '모듈의 번호' Handle로 사용할 수 있게 된다.
    2. 여기서 알수 있는 사실은 HINSTANCE는 실행 파일의 주소 0x0040,000 를 갖게 된다는 것이다.
    3. LoadIcon( hwnd, ID ); 여기서 핸들은 가상메모리에 로드된 핸들의 가상주소를 의미하게 된다.
Tag |

System - Object Table

from Study/API 2007/10/09 17:09 view 14800
출처 : -_-..홈페이지가 폐쇠됨..
// 테이블 3개가 존재한다는 말은 2000 구조같은데... xp는 어떨지...

Win32 개발자들은 핸들이 뭔지는 알고 있을 것이다.

 그리고 이 핸들이 윈도우핸들(HWND)이 아님을 알고 있을것이다. 이 핸들이란건 특정 동작을 하기 위한 핸들링 용도로 쓰이고 있으며 대표적으로 파일을 다루는 WINAPI인 CreateFile(), ReadFile, WriteFile()함수들을 예로 들어 볼 수 있다.
 
 CreateFile()함수 성공시에 리턴값으로 핸들을 받게 되는데 이 핸들은 나중에 특정 파일을 핸들링 하기 위해 그 정보를 유지 해야 하는데 그 정보는 커널영역에 오브젝트 형태로 존재하게 되며 보통은 커널 오브젝트라고 부른다.

그럼 핸들값이 의미하는 숫자는 무엇일까?
이 핸들값은 커널오브젝트를 찾기 위한 일종의 인덱스 역활을 하는 값이다.
밑에 그림을 보도록 하자.



 위 그림은 유저영역에 존재하는 HANDLE을 통해 실제 객체를 나타내는 커널 오브젝트를 찾아가는 그림이다. 그림에선 중간에 HANDLE TALBLE이란게 존재하여 핸들값을 핸들테이블에서 인덱스로 사용하여 커널 오브젝트의 포인터를 찾는다.

자 이제 핸들테이블에 대해 알아보자.

핸들테이블이란?
 현 프로세스에서 생성된 핸들들에 대해서 커널 객체를 찾아가기 위한 인터페이스 역할을 하는 배열 형태로 된 테이블 영역이다. 이 핸들 테이블은 프로세스당 하나가 할당되며 프로세스간 독립적으로 핸들 테이블을 관리하게 되는데 우리가 CreateFile()로 생성된 핸들은 CloseHandle()를 사용하지 않고 프로세스를 죽였을때 운영체제는 이 핸들테이블에서 할당된 정보를 보고 모든 핸들을 반환하게 된다.

밑에 그림은 객체의 포인터를 찾기 위한 핸들테이블 구조를 보여준다.




 핸들테이블은 세개의 영역이 존재하는데 Top, Middle, entry가 존재함을 그림을 통해
알수가있다.
 그럼 위 테이블 구조를 어떤식으로 접근하여 실제 커널 오브젝트를 찾을 것인가? 아까 핸들값이 인덱스 역할을 하는 값이라고 설명하였다. 이 핸들값을 바이트 단위로 3등분 한후 핸들 테이블의 각 레벨의 배열을 인덱스로 사용하고 찾아간다.

밑에 그림은 핸들값이 0x4일때 이걸 세등분 한 후 핸들 테이블을 인덱스 삼아
실제 오브젝트를 찾아가는 그림이다.




 핸들값은 상위 6비트와 하위 2비트는 핸들테이블을 인덱싱하는 부분에선 해당되지 않는 비트이며 중간에 있는 총 3개의 8비트가 우리가 커널 오브젝트를 찾기 위한 핸들테이블을 접근하는 인덱스 번호가 되겠다.

 Table ptr은 Top레벨의 주소를 가리킨다.여기서부터 핸들값의 인덱스가 적용된다. 우리가 보고자 하는 0x4값은 각각 Top:0, Mid:0, Entry:1값을 가진다. 핸들 테이블에 이 값들을 적용하면 Top[0], Mid[0], Entry[1]이 되며. 처음 Top[0]에는 Mid의 첫번째 배열 주소를 가지키며 Mid[0]은 Entry의 주소를 가리키고 있다. 마지막 Entry는 실제 커널 오브젝트를 가리키는 주소를 가지고 있게된다.

코드로 함 살펴보기로 하자.

lkd> dt nt!_EPROCESS
   +0x000 Pcb              : _KPROCESS

   +0x0c4 ObjectTable      : Ptr32 _HANDLE_TABLE

 커널에서 프로세스를 표현하는 EPROCESS에 보면 _HANDLE_TABLE ObjectTable가 존재한다.이 필드는 HANDLE_TABLE이란 구조체의 포인터를 가리키며 이 필드가 실제 핸들 테이블의 정보를 가지고 있는 셈이다. 윈도우 XP에서 ObjectTable의 위치는 0x0c4로 되어있다.

자 이제 HANDLE_TABLE의 구조를 살펴 봐야 한다.

lkd> dt nt!_HANDLE_TABLE
   +0x000 TableCode        : Uint4B
   +0x004 QuotaProcess     : Ptr32 _EPROCESS
   +0x008 UniqueProcessId  : Ptr32 Void
   +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
   +0x01c HandleTableList  : _LIST_ENTRY
   +0x024 HandleContentionEvent : _EX_PUSH_LOCK
   +0x028 DebugInfo        : Ptr32 _HANDLE_TRACE_DEBUG_INFO
   +0x02c ExtraInfoPages   : Int4B
   +0x030 FirstFree        : Uint4B
   +0x034 LastFree         : Uint4B
   +0x038 NextHandleNeedingPool : Uint4B
   +0x03c HandleCount      : Int4B
   +0x040 Flags            : Uint4B
   +0x040 StrictFIFO       : Pos 0, 1 Bit

 대충 보면 몇몇 필드는 변수명으로 그 의미를 파악할 수 있다. 여기서 HandleCount는 현재 프로세스에 할당된 핸들 총갯수 즉 핸들 테이블에 기록된 핸들의 정보수를 나타낸다.

UniqueProcessId는 이 핸들 테이블을 소유한 프로세스의 ID를 뜻하며 실제적인 핸들 테이블을 나타내는 Table 필드가 우리가 알고자 하는 핸들 테이블을 가리키는 필드이다.

lkd> dt nt!_HANDLE_TABLE_ENTRY
   +0x000 Object           : Ptr32 Void
   +0x000 ObAttributes     : Uint4B
   +0x000 InfoTable        : Ptr32 _HANDLE_TABLE_ENTRY_INFO
   +0x000 Value            : Uint4B
   +0x004 GrantedAccess    : Uint4B
   +0x004 GrantedAccessIndex : Uint2B
   +0x006 CreatorBackTraceIndex : Uint2B
   +0x004 NextFreeTableEntry : Int4B

HANDLE_TABLE_ENTRY 구조체는 커널 오브젝트를 가리키는 Object필드와 핸들테이블 액세스 관련 필드인 GrantedAccess로 구성되어 있으며 Object가 OBJECT_HEADER를 가리키며 OBJECT_HEADER + OBJECT BODY 형태로 되어있다. 구조는 밑에와 같이 생겼으며 Body로 된 부분은 커널 객체마다 다른 구조로 되어 있다.

lkd> dt nt!_OBJECT_HEADER
   +0x000 PointerCount     : Int4B
   +0x004 HandleCount      : Int4B
   +0x004 NextToFree       : Ptr32 Void
   +0x008 Type             : Ptr32 _OBJECT_TYPE
   +0x00c NameInfoOffset   : UChar
   +0x00d HandleInfoOffset : UChar
   +0x00e QuotaInfoOffset  : UChar
   +0x00f Flags            : UChar
   +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION
   +0x010 QuotaBlockCharged : Ptr32 Void
   +0x014 SecurityDescriptor : Ptr32 Void
   +0x018 Body             : _QUAD

 이 Body부분은 운영체제에 지시한 특정 명령에 동작을 나타내는 정보를 가지고 있는 실제 커널 오브젝트를 뜻한다.
 이 객체는 CreateFile()과 같은 커널에서 수행되어야 될 명령을 오브젝트 형태로 커널에서 관리하기 위해서 만들어진 구조체이며 유저영역에선 핸들을 사용하여 커널 오브젝트에 접근하게 된다.
 커널오브젝트는 스스로 제거될 수 있도록 참조 개수를 유지 하게 되는데. HandleCount, PointerCount란 필드를 통해서 관리된다. HandleCount는 우리가 CreateFile()로 생성되었을때 리턴된 핸들의 갯수를 뜻하며 PointerCount는 핸들이 아닌 직접 객체의 포인터를 얻어왔을때 증가하게된다. 커널에선 ReferenceObjectByPointer(), ObDereferenceObject() 사용시에 PointerCount가 증감된다.



Tag |

GetPrivateProfileString Function (ini 읽기)

from Study/API 2007/10/05 21:04 view 31408
아나 감동의 쓰나미.. .ini 에서 등록한 키워드 읽어오기 굳이 ini아니라도 되지만..-_-..
참고 : http://msdn2.microsoft.com/en-us/library/ms724353.aspx 

// ini 파일
[section]
key = string

ex)
[C/C++]
KEYWORD = const,for,if


1. MFC에서 CMapStringToString 클래스를 사용하여 읽어오기

more..



2. CIniFile 클래스를 사용해서 읽어오기 ( 래핑 클래스 )

more..

Tag | , ,

API 디자인 발표영상

from Study/API 2007/09/15 15:00 view 16573
http://www.infoq.com/presentations/effective-api-design

-_-...아 R/C
Tag |

예제 모음

from Study/API 2007/09/12 11:10 view 17082
http://www.winapi.co.kr/pds/exam/exam.htm 
Tag |

sprintf 와 wsprintf 이야기

from Study/API 2007/09/11 20:23 view 50599
귓동냥으로 wsprintf()의 w가 windows라는것을 알았기 때문에, 윈도우즈에서 프로그래밍할때에는 막연히 wsprintf()를 썼었는데, 오늘 관련 자료를 찾아보면서 보다 정확히 알게 되었다.

간략하게 정리하면
sprintf()는 CRT 라이브러리를 사용하는 함수
wsprintf()는 플랫폼 SDK에서 제공하는 함수, 단 실수(float)형의 사용은 안된다.
/////////////////////////////////////////////////////////////////////////////////////////////
sprintf 를 사용하려면  #include <stdio.h> 가 필요합니다.

이는 보통
#include <vcl.h>
#include <stdio.h>
#pragma hdrstop 식으로 쓰이게 되죠.

그런데 왜 vcl에서는 sprintf 를 포함하지 않았는지 궁금했었는데... wsprintf 가 있기 때문이었습니다.

wsprintf 는
#include <stdio.h>
없이 사용가능한 WIN32 API 로,  일반 ANSI코드과 유니코드 사용 프로그램에
공히 사용할수 있는 문자열 합성 함수입니다.

유니코드를 사용하지 않는 프로그램이면 그냥 wsprintf 를 sprintf 대신 사용하면 됩니다.
유니코드를 사용하는 프로그램이면 역시 wsprintf 를 그냥 사용하면 됩니다.

유니코드를 사용할 경우는 문자열은 L"문자열"; 식으로 유니코드로 표현되어야 하며
char * 대신 wchat_t *  형이 사용되어야 합니다.

그래서 표준적으로 유니코드 사용프로그램과 일반 프로그램에 공히 하나의 코드로 처리하기 위해
컴파일러는 TCHAR 형과 TEXT 매크로를 제공합니다.

가령 OnCreate 이벤트에 다음과 같은 코딩이 가능합니다.
{
TCHAR  buf[100];
wsprintf(buf, TEXT("캡션 메시지: %s"), TEXT("안녕하세요. C++빌더입니다."));
Caption = buf;
}
이 코드는 ANSI형이나 유니코드형 프로그램 양쪽에서 안전하게 동작합니다.
TEXT 매크로 와 __TEXT 매크로는 동일합니다.

Project Option->Directorys...->Conditional defines ; 에 UNICODE 또는 _UNICODE 를 넣어주면
유니코드모드로 컴파일하게 되는데, 이 경우 안전하게 유니코드모드로 컴파일되게 합니다.

프로그래밍할때 아예 습관을 char 대신 TCHAR 으로 하고
"문자열" 대신 TEXT("문자열") 을 쓰는 것도 좋을 것입니다.
Tag | ,