본문 바로가기

My Study/Programming&Theory

Windows Clipboard : GetClipboardData

http://ezbeat.tistory.com/444  (Windows Clipboard : OpenClipboard)


저번글에 이어 이번엔 GetClipboardData 함수가 어떻게 동작하는지 알아보겠습니다.


2. GetClipboardData(CF_TEXT)

역시나 MSDN 설명

클립보드의 지정된 포맷으로부터 데이터를 검색한다고 되어 있습니다.

인자로 들어간 값이 클립보드 포맷입니다. 수십가지의 표준 포맷이 있지만 여기선 텍스트 포맷을 사용하였습니다.

텍스트 포맷 : 라인의 끝은 (CR-LF) 결합으로 되어 있고, 데이터 끝은 NULL로 되어 있습니다. ANSI 만 가능!

유니코드로 사용하려면 CF_UNICODETEXT 사용!


아무튼 함수 설명은 이러합니다. 코드를 보면 알 수 있듯이 리턴 값은 포인터인데 해당 포인터엔 클립보드에서 가져온 데이터가 있습니다. 출력출력!


이제 내부적으로 어떻게 동작하는지 알아봐야겠네요.


먼저 GetClipboardData 가 User영역에서 어떻게 동작하는지를 알아보겠습니다.


어셈코드를 쭉 봐봤는데 대충 이렇게 작동합니다.

1. hData = NtUserGetClipboardData( CF_TEXT, pClipData ) 호출, 호출 후 pClipData에는 현재 클립보드 상태가 담겨짐

2. pClipData.uFmtRet 값이 CF_TEXT 와 비교

3-1. CF_TEXT 와 같은 경우 user32의 전역변수인 RTL_CRITICAL_SECTION 구조체 변수 gcsClipboard 를 인자로 크리티컬 섹션 진입

3-2. user32의 전역변수인 클립보드 구조체 변수 gphn 체이닝 확인

3-3-1. gphn 체이닝이 있는 경우 해당 체이닝을 뒤져서 해당 포맷에 맞는 데이터를 리턴

3-3-2. gphn 체이닝이 없는 경우 힙 할당하고 힙에 포맷, hData, 메모리 핸들 등을 초기화해서 체이닝에 등록

         이 때, hData를 인자로 CreateLocalMemHandle 함수를 쓰면 포맷(CF_TEXT)에 맞는 클립보드 내용을 담은 메모리 값을

         리턴 = hMem

4-1. CF_TEXT와 비교결과 다른고 CF_UNICODETEXT인 경우

4-2. CreateLocalMemHandle 함수로 해당 CF_UNICODETEXT에 맞는 메모리 내용을 가져오고 WideCharToMultiByte를 사용해 유니코드를 ANSI로 변경 후 CF_TEXT 포맷 데이터 위치에 값 쓰고 다시 얻어와서 리턴!


GetClipboardData 함수 분석.txt


[먼말인지 잘 이해가 안가실탠데 간단하게 요약하겠습니다.]

클립보드 관리는 커널에 있는 스테이션이라는 커널오브젝트에서 관리를 하게 됩니다.

이 때 클립보드는 여러가지 기본 포맷을 가지는데요. CF_TEXT, CF_UNICODETEXT, CF_BITMAP 등등.. 이런 포맷에 맞게 각각의 데이터 공간을 가지고 있습니다. 


reactOS 코드 일부입니다.

PCLIP static FASTCALL

IntIsFormatAvailable(PWINSTATION_OBJECT pWinStaObj, UINT fmt)

{

    unsigned i = 0;


    for (i = 0; i < pWinStaObj->cNumClipFormats; ++i)

    {

    if (pWinStaObj->pClipBase[i].fmt == fmt)

            return &pWinStaObj->pClipBase[i];

    }


    return NULL;

}


이 함수는 윈도우 스테이션 오브젝트에서 해당 클립보드 포맷에 맞는 클립보드 베이스 주소를 가져오는 함수입니다.

이처럼 윈도우 스테이션은 클립보드 포맷 개수와 각 포맷에 있는 베이스 주소를 가지고 있습니다. 하나의 클립보드 공간을 전부 공유하는 형태가 아니라는 것입니다.


저희가 notepad.exe를 켜고 "test" 라는 단어를 쓴 다음 Ctrl+C로 복사를 하게되면 이는 CF_UNICODETEXT 포맷의 클립보드 영역에 저장이됩니다. 하지만 프로그램에서 CF_TEXT로 클립보드 내용을 요청하면 CF_UNICODETEXT 포맷의 클립보드 영역에 있는 값을 가져와 ANSI로 변환 후 CF_TEXT 클립보드 영역으로 복사하고 그 내용을 보여주는 것입니다.

그리고 CF_TEXT를 인자로주어 클립보드 내용을 변경시켜도 당연히 CF_UNICODETEXT 포맷의 클립보드 영역은 그대로 입니다. 실제로 CF_TEXT 를 인자로주어 클립보드 내용을 "Ezbeat" 으로 변경시켜고 Ctrl+V를 눌러보시면 "test" 가 그대로 나오는 것을 보실 수 있을 것입니다.


아래는 GetClipboardData 함수 작동 메커니즘을 순서도로 나타낸 그림입니다.

( 출처 : Extracting the Windows Clipboard from Physical Memory )


원본에는 없지만 색은 제가 넣어봤습니다. 연두색으로 표시된 부분은 user 영역입니다.



커널까지 더욱 깊게 분석을 해봐야하지만 시간이 없어 여기까지만 하겠습니다.