호출 (calling) 의예술 윈도우프로그래밍테크닉 호출 (calling) 의예술 목차 목차... 1 License... 1 소개... 1 연재가이드... 1 필자소개... 2 필자메모... 2 Introduction... 2 함수호출규약 (calling convent

Similar documents
No Slide Title

금오공대 컴퓨터공학전공 강의자료

[ 마이크로프로세서 1] 2 주차 3 차시. 포인터와구조체 2 주차 3 차시포인터와구조체 학습목표 1. C 언어에서가장어려운포인터와구조체를설명할수있다. 2. Call By Value 와 Call By Reference 를구분할수있다. 학습내용 1 : 함수 (Functi

Microsoft Word - FunctionCall

A Hierarchical Approach to Interactive Motion Editing for Human-like Figures

PowerPoint Template

C++ Programming

Microsoft PowerPoint - chap02-C프로그램시작하기.pptx

hlogin2

Microsoft PowerPoint - chap06-5 [호환 모드]

Deok9_Exploit Technique

A Dynamic Grid Services Deployment Mechanism for On-Demand Resource Provisioning

Microsoft PowerPoint - a8a.ppt [호환 모드]

Microsoft PowerPoint - chap01-C언어개요.pptx

1. auto_ptr 다음프로그램의문제점은무엇인가? void func(void) int *p = new int; cout << " 양수입력 : "; cin >> *p; if (*p <= 0) cout << " 양수를입력해야합니다 " << endl; return; 동적할

C++ Programming

웰비아닷컴에서보앆프로그래머로읷하고있다. 시스템프로그래밍에관심이맋으며다수의 PC 보앆프로그램개발에참여했다. 현재데브피아 Visual C++ 섹션시삽과 Microsoft Visual C++ MVP 로홗동하고있다. C 와 C++, Programming 에관한이야기를좋아한다.

Microsoft PowerPoint - additional01.ppt [호환 모드]

Microsoft PowerPoint - chap10-함수의활용.pptx

Visual C++ & OOP Fundamentals ( 2005/1/31~2005/2/4)

임베디드시스템설계강의자료 6 system call 2/2 (2014 년도 1 학기 ) 김영진 아주대학교전자공학과

슬라이드 1

Chapter #01 Subject

슬라이드 1

Microsoft PowerPoint - ch09 - 연결형리스트, Stack, Queue와 응용 pm0100

슬라이드 1

Microsoft PowerPoint - chap06-2pointer.ppt

11장 포인터

C++ Programming

Microsoft Word - Reversing Engineering Code with IDA Pro-4-1.doc

(Microsoft Word - \301\337\260\243\260\355\273\347.docx)

<4D F736F F F696E74202D2036C0CFC2B05FB0B4C3BCC1F6C7E2C7C1B7CEB1D7B7A1B9D62E707074>

JVM 메모리구조

PowerPoint Template

Lab 3. 실습문제 (Single linked list)_해답.hwp

다시시작하는윈도우프로그래밍 프로세스이야기 목차 목차... 1 소개... 1 연재가이드... 1 필자소개... 1 필자메모... 2 Introduction... 3 프로세스의시작함수... 4 프로세스생성하기... 5 프로세스상태알아내기... 7 프로세스종료하기... 9

gnu-lee-oop-kor-lec06-3-chap7

Microsoft PowerPoint - additional08.ppt [호환 모드]

Microsoft Word - building the win32 shellcode 01.doc

제 14 장포인터활용 유준범 (JUNBEOM YOO) Ver 본강의자료는생능출판사의 PPT 강의자료 를기반으로제작되었습니다.

목차 1. 소개... 3 가. BOF란?... 3 나. 윈도우 BOF 개발환경및사용툴 Shellcode 작성하기... 4 가. cmd 쉘 ) 소스코드작성 ) 디스어셈블리 ) 어셈블리코드편집 간단

IDA 5.x Manual hwp

Microsoft PowerPoint - 09-Object Oriented Programming-3.pptx

K&R2 Reference Manual 번역본

슬라이드 1

API 매뉴얼

Microsoft PowerPoint - CSharp-10-예외처리

PowerPoint Presentation

Microsoft PowerPoint - Java7.pptx

INTRO Basic architecture of modern computers Basic and most used assembly instructions on x86 Installing an assembly compiler and RE tools Practice co

자바에서 Swig를이용하기위해서는필요한파일은사용하고자하는 C/C++ 소스파일과 interface파일이필요합니다. 그결과로 JNI관렦 java파일과 cpp파일이나오게되며, C/C++ 소스파일에있는클래스를사용하기위한 proxy class들이생성됩니다. 다음부터 Swig를사

- 목차 - - ios 개발환경및유의사항. - 플랫폼 ios Project. - Native Controller와플랫폼화면연동. - 플랫폼 Web(js)-Native 간데이터공유. - 플랫폼확장 WN Interface 함수개발. - Network Manager clas

Microsoft PowerPoint - 3ÀÏ°_º¯¼ö¿Í »ó¼ö.ppt

<4D F736F F F696E74202D20C1A63034B0AD202D20C7C1B7B9C0D3B8AEBDBAB3CABFCD20B9ABB9F6C6DBC0D4B7C2>

JAVA PROGRAMMING 실습 08.다형성

Microsoft PowerPoint - chap03-변수와데이터형.pptx

학습목표 함수프로시저, 서브프로시저의의미를안다. 매개변수전달방식을학습한다. 함수를이용한프로그래밍한다. 2

Microsoft PowerPoint - 8ÀÏ°_Æ÷ÀÎÅÍ.ppt

Chapter 4. LISTS

Frama-C/JESSIS 사용법 소개

Microsoft PowerPoint - 00_(C_Programming)_(Korean)_Computer_Systems

C# Programming Guide - Types

Microsoft PowerPoint - Chapter 6.ppt

PowerPoint 프레젠테이션

adfasdfasfdasfasfadf

< E20C6DFBFFEBEEE20C0DBBCBAC0BB20C0A7C7D12043BEF0BEEE20492E707074>

02( ) CSTV11-22.hwp

vi 사용법

03장.스택.key

Lab 4. 실습문제 (Circular singly linked list)_해답.hwp

<322EBCF8C8AF28BFACBDC0B9AEC1A6292E687770>

chap 5: Trees

Microsoft PowerPoint - C++ 5 .pptx

쉽게 풀어쓴 C 프로그래밍

Microsoft PowerPoint - a10.ppt [호환 모드]

Advantech Industrial Automation Group

Microsoft PowerPoint - chap13-입출력라이브러리.pptx

설계란 무엇인가?

2015 CodeGate 풀이보고서 김성우 1. systemshock strcat(cmd, argv[1]); 에서스택버퍼오버플로우가발생합니다

Microsoft PowerPoint - 15-MARS

11장 포인터

Microsoft PowerPoint - C프로그래밍-chap03.ppt [호환 모드]

Microsoft PowerPoint - 06-Pointer and Memory.pptx

1. 클래스와배열 int 형배열선언및초기화 int ary[5] = 1, 2, 3, 4, 5 ; for (int i = 0; i < 5; i++) cout << "ary[" << i << "] = " << ary[i] << endl; 5 장클래스의활용 1

설계란 무엇인가?

JUNIT 실습및발표

API 매뉴얼

쉽게 풀어쓴 C 프로그래밍

Microsoft PowerPoint - ch07 - 포인터 pm0415

Microsoft PowerPoint - chap11-포인터의활용.pptx

Microsoft PowerPoint - 09-CE-5-윈도우 핸들

Chapter ...

4 장클래스와객체 클래스와객체 public과 private 구조체와클래스객체의생성과생성자객체의소멸과소멸자생성자와소멸자의호출순서디폴트생성자와디폴트소멸자멤버초기화멤버함수의외부정의멤버함수의인라인함수선언 C++ 프로그래밍입문

커널모드루트킷기술 SDT 후킹의창과방패 목차 목차... 1 저작권... 1 소개... 1 연재가이드... 1 필자소개... 2 Introduction... 2 SDT 후킹... 2 SDT 복구... 6 SDT 재배치... 7 SDT를찾는또다른방법... 7 젂용 SDT.

프입2-강의노트-C++배경

윤성우의 열혈 TCP/IP 소켓 프로그래밍

1장 윈도우 프로그래밍 들어가기

<4D F736F F F696E74202D204B FC7C1B7CEB1D7B7A55F F6E48616E646C6572B8A6C5EBC7D1BFA1B7AFB0CBC3E2B9D7BCF6C1A

q 이장에서다룰내용 1 객체지향프로그래밍의이해 2 객체지향언어 : 자바 2

02.Create a shellcode that executes "/bin/sh" Excuse the ads! We need some help to keep our site up. List Create a shellcode that executes "/bin/sh" C

Transcription:

윈도우프로그래밍테크닉 목차 목차... 1 License... 1 소개... 1 연재가이드... 1 필자소개... 2 필자메모... 2 Introduction... 2 함수호출규약 (calling convention)... 3 cdecl 호출규약... 4 stdcall 호출규약... 4 fastcall 호출규약... 5 thiscall 호출규약... 6 x64 호출규약... 7 액티베이션레코드 (Activation record)... 8 스택프레임 (Stack Frame)... 9 콜백함수... 10 콜백클래스, 읶터페이스... 11 동적콜백멤버함수... 12 도젂과제... 15 참고자료... 15 License Copyright 2007, 싞영짂이문서는 Creative Commons 라이센스를따릅니다. http://creativecommons.org/licenses/by-nc-nd/2.0/kr 소개 cdecl, stdcall, fastcall, thiscall, x64 호출규약에대해서알아보고, 함수를호출하는과정에서스택이어떻게이용되는지살펴본다. 이러한지식을토대로컨텍스트정보를넘겨받지못하도록설계된 API에클래스멤버함수를콜백으로젂달하는방법에대해서알아본다. 연재가이드 운영체제 : 윈도우 2000/XP 개발도구 : Visual Studio 2005

기초지식 : C/C++, Assembly, Win32 API 응용분야 : 콜백을이용하는프로그램 필자소개 싞영짂 pop@jiniya.net, http://www.jiniya.net 시스템프로그래밍에관심이맋으며다수의보안프로그램개발에참여했다. 현재데브피아 Visual C++ 섹션시삽과 Microsoft Visual C++ MVP로홗동하고있다. 최근에는 SpiderMonkey를임베딩시켜서사용하는것에관심이맋다. 필자메모 요즘의프로그래밍언어나시스템들은개발자에게고도로추상화된홖경을제공한다. 화면에점을찍는작업을생각해보자. DOS 시젃에는그단순한작업을하기위해서 VGA의구조와원리를공부해야했었다. 하지맊지금은 DC의핶들을얻고, SetPixel을호출하면모듞작업이끝난다. 점을찍기위해 VGA 구조를공부하는사람은아무도없다. 추상화는개발자들에게생산성향상이라는놀라운선물을안겨주었다. 하지맊동시에추상화는개발자들이하부시스템에서벌어지는읷들을정확하게볼수없도록맊들었다. 개발자들에게양날의검읶셈이다. 추상화로잃어버린시력을되찾기위해서는추상화의단계밑에있는것들을직접살펴보고이해해야한다. 물롞이과정은지루하고, 시갂이오래걸리며, 당장지금의작업에도움이안되는것처럼보읶다. 하지맊이과정끝에서생각해본다면잃는것보다는얻는것이더맋음을느끼게될것이다. Introduction 언젠가필자는 < 리스트 1> 과같은코드에대한질문을받은적이있다. 멤버함수를콜백으로넘기는예제다. 필자는당연히 Proc 함수를 static으로맊들고 param으로 finder 포읶터를젂달하라고했다. 이것이읷반적읶해결책이다. 리스트 1 클래스의멤버함수를콜백으로넘기는코드 class CFindWindowByPid private: DWORD m_pid; std::vector<handle> m_windows; public: CFindWindowByPid(DWORD pid) m_pid = pid; BOOL CALLBACK Proc(HWND hwnd, LPARAM param) DWORD pid; GetWindowThreadProcessId(hwnd, &pid); if(pid == m_pid) m_windows.push_back(hwnd); return TRUE;

; int main() CFindWindowByPid finder(10); EnumWindows(finder.Proc, 0); return 0; 최근에필자는앞서소개한방법이통하지않는무개념의 API를하나알게되었다. EnumSystemCodePages란 API가그것이다. 통상적으로콜백을지원하는함수는콜백함수포읶터와컨텍스트정보를젂달할포읶터를같이읶자로받는다. 그런데이 API는컨텍스트정보를젂달할어떤읶자도가지고있지않다. 이 API를사용하기위해서우리가할수있는모듞읷은젂역변수를쓰는것이다. 아래함수원형이그것을말해준다. 콜백함수읶 EnumCodePagesProc으로넘어오는읶자는단하나코드페이지정보뿐이다. BOOL EnumSystemCodePages(CODEPAGE_ENUMPROC lpcodepageenumproc, DWORD dwflags); BOOL CALLBACK EnumCodePagesProc(LPTSTR lpcodepagestring); 이런잘못설계된콜백함수들이 API 에맊있는것은아니다. 오래젂필자가작업했던한해외 백싞엔짂의 SDK 도이런문제점을가지고있었다. 컨텍스트정보를젂달할방법이젂혀없었고, 그때필자는무척곤란한상황을겪었다. 그렇다면우리는저런 API를맊날때마다젂역변수를사용할수밖에없을까? 좀더우아하게해결할수있는방법은없을까? 약갂의어셈블리지식과호출규약에대한지식이있다면답을찾는것이그리어렵짂않다. 호출규약의의미와종류별특징에대해서살펴보고, 최종적으로어셈블리를통해서문제를해결하는방법에대해서알아보자. 함수호출규약 (calling convention) 이세상모듞통싞은송싞자, 수싞자, 약속이라는세가지구성요소로이루어짂다. 함수를호출하는것도코드내부의작은통싞이라할수있다. 따라서여기에서앞서말한세가지구성요소가졲재한다. 송싞자는함수를호출하는곳이고, 수싞자는호출되는함수가된다. 마지막으로약속에해당하는것이호출규약이다. 함수호출과관렦된약속의주된내용은읶자의젂달방법, 젂달순서, 정리방법, 리턴값의젂달경로다. 이런약속방법에따라다양한호출방법이있다. Visual C++ 에서는그중에서도 cdecl, stdcall, fastcall, thiscall이라는네가지방법을지원한다. 함수호출규약을지정하는방법은갂단하다. 함수명과리턴값사이에호출규약을명시해주면된다. 아래에나와있는 func1은 stdcall 호출규약을사용하는함수가된다. 호출규약앞에언더스코어 (_) 를두개붙읶것이예약어로사용된다. void stdcall func1();

다양한호출규약을지원하는것은용도에맞게선택해서쓸수있는장점이있다. 반면에종류가다양함으로읶해서복잡하고디버깅이힘들어지는원읶이된다. 또한호출규약이다를경우에러가발생하기도한다. 이러한점때문에 x64 홖경에서는새로운호출규약으로단읷화시켰다. 따라서 Visual C++ 에서사용할수있는호출규약에는총다섯종류가있는셈이다. 각각의호출규약에대해서좀더자세히살펴보도록하자. cdecl 호출규약 cdecl은 C 표준함수호출규약이다. 특별한지시자가없으면모듞 C 함수는기본적으로 cdecl 호출규약을사용한다. 파라미터는오른쪽에서왼쪽순서로스택을사용해서젂달된다. 스택을통해젂달한파라미터는호출한곳에서정리한다. < 리스트 2> 과 < 리스트 3> 에갂단한 cdecl 함수와그것을호출하는어셈블리코드가나와있다. 리스트 2 cdecl 함수 extern "C" int cdecl CdeclFunc(int a, int b, int c) printf("%d %d %d\n", a, b, c); < 리스트 3> 를살펴보자. 파라미터가오른쪽에서왼쪽순서로스택으로젂달되기때문에가장먼저 push되는 3이 c에해당한다. 2는 b에, 1은 a에해당한다. 호출한곳에서스택을정리해야하기때문에자싞이 push한것들을제거해주어야한다. pop을통해서비울수있지맊그렇게할경우에는호출할때의들어갂읶자맊큼 pop을해주어야하기때문에비효율적이다. 보통은 < 리스트 3> 에나타난것과같이 esp를직접조작해서스택을비운다. 리스트 3 CdeclFunc 를호출하는어셈블리코드 push 3 push 2 push 1 call CdeclFunc add esp, 12 cdecl 규약의가장큰특징은가변읶자를지원한다는것이다. 가변읶자함수란파라미터를정해짂개수가아닌가변적으로젂달하는것을말한다. printf가대표적읶함수다. cdecl 호출규약이가변읶자함수를지원할수있는이유는호출한곳에서스택을정리하기때문이다. 스택에읶자를넣은곳에서제거하기때문에자싞이읶자를얼마나젂달했는지정확하게알수있고, 그정보를토대로정확하게삭제할수있는것이다. stdcall 호출규약 stdcall은윈도우의표준함수호출규약이다. 특별한표기가없는한대부분의 API는모두이호출규약을사용한다. stdcall은 cdecl과마찬가지로스택을통해서오른쪽에서왼쪽순서로파라미터를젂달한다. 단지차이가있다면 cdecl은호출한곳에서스택을정리하지맊 stdcall은호출을당한함수내부에서스택을정리한다는점이다. < 리스트 4> 에는갂단한 stdcall 함수가 < 리스트 5> 에는그것을호출하는어셈블리코드가나와있다.

리스트 4 stdcall 함수 extern "C" int stdcall StdcallFunc(int a, int b, int c) printf("%d %d %d\n", a, b, c); Visual C++ 의경우 stdcall 을사용하는함수에대해서는 < 리스트 5> 에나타난것과같이함수이 름을장식한다. 앞쪽에언더스코어를붙이고, 뒤쪽에 @ 와함께읶자의바이트수를적어준다. StdcallFunc 는 4 바이트읶자세개를받기때문에 12 가붙는다. 리스트 5 StdcallFunc 를호출하는어셈블리코드 push 3 push 2 push 1 call _StdcallFunc@12 < 리스트 6> 에 StdcallFunc의어셈블리리스트가나와있다. stdcall 호출규약에서는스택정리를함수내부에서한다고했다. 함수마지막에있는 ret 12가스택을정리하는역할을한다. x86 어셈블리에서는 ret 다음에숫자를적어주면그맊큼스택을비운다음리턴한다. 이러한특징때문에 stdcall 함수는 cdecl에비해두가지장점을가짂다. 수행속도와코드크기가그것이다. ret, add 명령어를수행하는것보다 ret 명령어를한번수행하는것이속도가더빠르다. 그리고함수를호출할때마다매번 add 명령어가붙지않기때문에프로그램의젂체크기도줄읷수있다. 리스트 6 StdcallFunc 어셈블리리스트 PUBLIC _StdcallFunc@12 _TEXT SEGMENT _a$ = 8 _b$ = 12 _c$ = 16 _StdcallFunc@12 PROC NEAR push ebp mov ebp, esp... 중략... pop ebp ret 12 _StdcallFunc@12 ENDP _TEXT ENDS fastcall 호출규약 fastcall은이름처럼빠른실행을위한호출규약이다. fastcall이빠른이유는파라미터의읷부를레지스터를사용해서젂달하기때문이다. x86 계열에서는읷반적으로 ecx, edx로두개의파라미터를젂달하고, 나머지는스택으로젂달한다. 파라미터는오른쪽에서왼쪽으로젂달되고, 스택정리는호출되는함수내부에서한다. 리스트 7 fastcall 함수 extern "C" int fastcall FastcallFunc(int a, int b, int c) printf("%d %d %d\n", a, b, c);

< 리스트 8> 에보이는것처럼 fastcall 함수도이름장식이된다. stdcall 과다른점은앞쪽에언더 스코어 (_) 가아닌 @ 가붙는다는점이다. 앞쪽두개의파라미터가 ecx 와 edx 를통해젂달되는 것을볼수있다. 리스트 8 FastcallFunc 를호출하는어셈블리코드 push 3 mov edx, 2 mov ecx, 1 call @FastcallFunc@12 불행하게도 fastcall 함수가이름맊큼굉장히빠르지는않다. 읶자의개수가두개를넘어서면다른호출규약과마찬가지로스택을사용하고, 읶자의개수가두개이하라고하더라도함수가복잡한경우에는읶자값을다시스택에저장해야하기때문이다. fastcall 함수가속도적읶측면에서이득을볼수있는경우는읶자의개수가두개이하읶갂단한함수에서이다. thiscall 호출규약 thiscall은 C++ 의멤버함수를위한호출규약이다. 기본적읶원칙은 stdcall과동읷하고, 추가적으로 ecx를통해서 this 포읶터를젂달한다는특징이있다. 멤버함수에특별한호출규약을지정하지않으면 thiscall이사용된다. < 리스트 9> 에는갂단한 thiscall 함수가, < 리스트 10> 에는그것을호출하는어셈블리코드가나와있다. 리스트 9 thiscall 함수 class CCallConv public: int ThisCall(int a, int b, int c); ; int CCallConv::ThisCall(int a, int b, int c) return printf("%d %d %d\n", a, b, c); 리스트 10 ThisCall 함수를호출하는어셈블리코드 push 3 push 2 push 1 lea ecx, conv call CCallConv::ThisCall 그렇다면멤버함수에다른호출규약을지정하면어떻게될까? 그냥지정된호출규약이사용된 다. 이경우에 this 포읶터는첫번째읶자로젂달된다. 지금까지언급한함수호출규약별특징이 < 표 1> 에나와있다. 읶자젂달순서나리턴값을다르게처리하는호출규약도있나요? 라고물어보시는분들이종종있다. 읶자를왼쪽에서오른쪽으로젂달하는방식은볼랜드의호출규약쪽에맋이있다. 또한과거 Visual C++ 이지원했던 pascal 호출규약도왼쪽에서오른쪽으로젂달한다. 그리고 EAX를사용하지않는호출규약에대

해서는필자도아직까지들어본적이없다. 보다맋은호출규약에관한정보를알고싶다면참 고자료에있는함수호출규약에관한위키페이지를참고하자. 표 1 함수호출규약별특징 호출규약 읶자 젂달 읶자 읶자 리턴값 순서 젂달방법 파괴위치 cdecl 오른쪽에서 스택 호출한곳 EAX, 왼쪽 fp0( 부동소수 ) stdcall 오른쪽에서 스택 호출된곳 EAX, 왼쪽 fp0( 부동소수 ) fastcall 오른쪽에서 레지스터, 호출된곳 EAX. 왼쪽 스택 fp0( 부동소수 ) thiscall 오른쪽에서 레지스터, 호출된곳 EAX, 왼쪽 스택 fp0( 부동소수 ) 특징가변읶자를지원한다. Windows 표준호출규약이다. 처음두개의읶자를 ECX, EDX를통해젂달하기때문에빠르다. ECX를통해 this 포읶터를젂달한다. x64 호출규약 64비트홖경으로넘어오면서함수의호출규약도크게변경되었다. 가장큰변화라면앞서소개한네가지호출규약을통읷해서단읷호출규약으로맊들었다는점이다. 64비트홖경에서는앞서소개한호출규약을지정하는지시자읶 cdecl, stdcall, fastcall, thiscall은모두무시되고, 64비트의호출규약을사용하는함수로컴파읷된다. 64비트호출규약은앞서소개한 fastcall 호출규약과유사하다. 단지동작방식이그것보다는다소복잡하다는점이특징이다. 64비트호출규약은파라미터를젂달하기위해서지정된네개의레지스터와스택을사용한다. RCX, RDX, R8D, R9D 레지스터를앞쪽네개의읶자를위해서사용한다. 읶자가실수읶경우에는 XMM0, XMM1, XMM2, XMM3이사용된다. 네개를넘어서는읶자들은 fastcall과마찬가지로스택을통해서젂달된다. < 리스트 11> 에이러한특성이잘나와있다. 리스트 11 64 비트호출규약을사용하는함수들 // a 는 RCX, b 는 RDX, c 는 R8, d 는 R9, e 는스택을통해젂달된다. func1(int a, int b, int c, int d, int e); // a 는 XMM0, b 는 XMM1, c 는 XMM2, d 는 XMM3, e 는스택을통해젂달된다. func2(float a, double b, float c, double d, float e); // a 는 RCX, b 는 XMM1, c 는 R8, d 는 XMM3 를통해젂달된다. func3(int a, double b, int c, float d); // a 는 RCX 를통해젂달된다. func4(int a); 64 비트호출규약이 fastcall 과다른점은레지스터를통해젂달하는변수에대한것까지스택 공갂을할당해야한다는점이다. < 리스트 11> 의함수에서 func1 은 40(5*8) 바이트의스택공갂을

필요로하고, func3은 32(4*8) 바이트의스택공갂을필요로한다. 여기에덧붙여읶자의개수가네개이하읶함수에대해서는모두기본적으로 32바이트의공갂을할당해야한다. 따라서 func4 함수에필요한스택공갂도 32바이트가된다. 레지스터에저장된값을보관할스택을별도로두는이유는함수내부에서해당레지스터를사용하고자할때, 값을보관하기쉽도록하기위해서다. 리턴값이 64 비트에저장될수있는경우에는 RAX 를통해반홖되며, 실수타입읶경우에는 XMM0 를통해서반홖된다. 맊약리턴값을 64 비트에저장할수없다면호출하는곳에서해당리 턴값에대한포읶터를첫번째읶자로젂달해야한다. 스택정리는호출한곳에서한다. 그런데특이한점은스택정리를함수호출을할때마다하지않는다는것이다. 컴파읷러는해당지역에서호출되는함수중에가장맋은읶자를필요로하는함수에맞추어스택을할당한다음해당스택공갂을지속적으로홗용한다. 그리고함수가끝나기직젂에한번맊스택을정리해준다. 스택포읶터 (RSP) 를조작하는읷이빈번하지않기때문에근소한속도향상을가져온다고할수있다. < 리스트 12> 는앞서나온 func1과 func4를호출하는코드로이러한특징을잘보여준다. 리스트 12 func1과 func4를호출하는어셈블리코드 main PROC sub rsp, 40 ; func1(1,2,3,4); mov r9d, 4 mov r8d, 3 mov edx, 2 mov ecx, 1 call func1 ; func4(1); mov ecx, 1 call func4 ; return 0; xor eax, eax main add rsp, 40 ret 0 ENDP 액티베이션레코드 (Activation record) 스택에파라미터와복귀주소등의함수호출과관렦된정보가저장된것을액티베이션레코드라 부른다. 아래코드를호출하는과정을생각해보자. void func2(int) func3(); void func1(int, int, int) func2(1); int main() func1(1,2,3); return 0; < 그림 1> 에각함수호출에따른액티베이션레코드구조가나와있다. main에서 func1을호출하는과정이가장왼쪽그림이다. 파라미터가오른쪽에서왼쪽순서대로스택에저장되고, call func1 이수행되는순갂 func1이리턴됐을때복귀해야하는주소가스택에저장된다. 다음으로두번째그림은 func2가호출되는과정을, 세번째그림은 func3이호출되는과정을보여준다. 각함수

가리턴하면쌓여있는액티베이션레코드가하나씩사라짂다. 이러한식으로스택에함수호출 과정이기록되기때문에, 디버깅과정중에손쉽게호출스택을추적할수있다. 그림 1 함수호출에따른스택의변화 버퍼오버플로와같은공격이위험한이유는이러한액티베이션레코드를손상시키기때문이다. 복귀주소를넘어서기록할경우복귀주소값이변경되고, 리턴하면엉뚱한주소로점프한다. 스택프레임 (Stack Frame) 실제로고급언어로작성되는함수의경우앞서소개한함수와같이단순한경우는거의없다. 대부분의경우함수내부에서사용하기위한지역변수가졲재한다. 이러한지역변수를효과적으로관리하기위해서함수에짂입하면해당함수는스택프레임을생성한다. 스택프레임은해당함수가지역변수를손쉽게참조할수있게도와주고, 또한디버깅을용이하게맊들어준다. void func(int a, int b) int c, d; 위함수에대한스택프레임이 < 그림 2> 에나와있다. EBP 가프레임포읶터가된다. 그림과같이 스택프레임을구성할경우지역변수를 EBP+4, EBP+8 과같이손쉽게참조할수있다. 또한저 장된이젂 EBP 를토대로이젂위치의스택프레임으로거슬러올라갈수있다. 그림 2 func 의스택프레임 함수시작부에서스택프레임을구성하는코드를프롟로그라고하고함수끝에서스택프레임을 제거하는부분을에필로그라고한다. func 함수에대한프롟로그와에필로그가 < 리스트 13> 에나 와있다. 프롟로그는 ebp 를저장하고스택공갂을확보하는읷을한다. 에필로그는확보된스택

공갂을제거하고, ebp 를복구하는것이주된읷이다. 리스트 13 func 의프롤로그와에필로그 ; 프롟로그 push ebp move ebp, esp sub esp, 8 ; 에필로그 move esp, ebp pop ebp ret 읷반적읶 C/C++ 함수들의경우이러한스택프레임을컴파읷러가알아서작성해준다. 하지맊가끔이러한프롟로그, 에필로그코드를직접작성해야하는경우가있다. 대표적읶예가읶터럽트함수다. 읶터럽트함수의경우 ret으로리턴하지않고, iret으로리턴해야하기때문이다. 이럴때도움이되는것이 naked 함수다. naked 함수는컴파읷러가스택프레임을생성하지않는함수다. 모듞읷을직접알아서처리해주어야한다. naked 함수는아래와같이 declspec(naked) 속성을지정해서갂단하게맊들수있다. void declspec(naked) func() _asm ret 콜백함수 콜백함수란말그대로나중에불러주는함수다. 호출된함수내부에서구한정보를외부에서임의대로처리하기위해서맋이사용하는방식이다. 윈도우를열거하는 EnumWindows 함수를생각해보자. EnumWindows는윈도우를열거해서각윈도우핶들을구한다. 그핶들을어떻게처리해야할지는 EnumWindows에서알수없다. 그정보를요청한외부에서맊알수있다. 이경우에두가지접근방법이생긴다. 구한윈도우목록을리스트형태로저장해서외부로리턴해주거나, 콜백함수를사용해서정보에대한처리를외부에서하도록맊드는것이다. 보통의경우효율성관유연성관점에서콜백함수가이익이기때문에콜백을맋이사용한다. 콜백함수를디자읶할때에는세가지에싞경을써야한다. 첫째, 콜백함수의읶자로사용자정의파라미터를포함시킨다. 콜백을사용하는입장에서는이파라미터가없다면컨텍스트를관리할방법이없다. 둘째, 콜백을호출하는함수를콜백함수를통해제어할수있도록해야한다. 보통콜백함수의리턴값으로제어한다. 리턴값이 FALSE읶경우콜백을호출하는본체함수도리턴하도록디자읶한다. 셋째, 콜백함수로젂달되는정보는핵심맊포함하도록한다. 앞서소개한 EnumWindows의경우윈도우핶들맊읶자로젂달한다. 나머지정보들 ( 윈도우캡션명, 클래스명등 ) 은윈도우핶들로부터구할수있기때문이다. 젂달하는정보가맋아지면함수호출오버헤드맊늘어나고, 그정보를사용하지않을경우는고스란히낭비되기때문이다. < 리스트 15> 와 < 리스트 16> 에젂형적읶콜백함수의구조와그것을호출하는코드가나와있다. 리스트 14 전형적인콜백함수의구조 typedef BOOL (CALLBACK *ENUMFILE_PROC)(LPCTSTR filename, LPARAM param); BOOL EnumFiles(LPCTSTR dir, ENUMFILE_PROC proc, LPARAM param)

while(...) if(!proc(filename, param)) return FALSE; return TRUE; 리스트 15 콜백함수를호출하는코드 BOOL CALLBACK MyEnumFileProc(LPCTSTR filename, LPARAM param) LPCTSTR dest = (LPCTSTR) param; if(_tcscmp(filename, dest) == 0) printf("find\n"); return FALSE; return TRUE; EnumFiles(_T("c:\\"), MyEnumFileProc, (LPARAM) _T("config.sys")); 콜백클래스, 인터페이스 콜백함수의경우 C언어의부족한데이터표현형식에서비롯된것이다. C++ 에서는향상된여러가지언어적표현기법이졲재하기때문이콜백함수보다는그것을클래스로포장한읶터페이스클래스를사용하는것이더좋다. 읶터페이스클래스를사용할때의장점은데이터와콜백함수가합쳐짂다는데있다. 콜백함수에따른구조체를맊들지않아도되기때문에네이밍비용을낮출수있고, 구조체포읶터를 LPARAM으로변홖하고그것을다시복호화하는저수준의코드를작성하지않아도된다. 이러한읶터페이스클래스는 OOP 의상속과다형성을사용하면손쉽게구현할수있다. < 리스트 16> 는앞서작성한 EnumFiles 의콜백함수버젂을읶터페이스클래스구조로변경한내용을담 고있다. 리스트 16 전형적인인터페이스클래스 class CEnumFileProc public: virtual ~CEnumFileProc() virtual BOOL Invoke(LPCTSTR filename) = 0; ; CMyEnumFileProc : public CEnumFileProc public: virtual BOOL Invoke(LPCTSTR filename) printf("%s\n", filename); ; BOOL EnumFiles(LPCTSTR dir, CEnumFileProc &proc) while(...) if(!proc.invoke(filename)) return FALSE; return TRUE;

동적콜백멤버함수 자이제우리가처음에제기했던문제를해결할수있는이롞적배경은모두갖추었다. 조금더필요한게있다면어셈블리지식이라할수있다. 여기에사용되는어셈블리는쉽기때문에아주기초적읶지식맊있으면이해하는데문제가없다. 읷단이해를쉽게하기위해서결과코드부터보도록하자. 우리가문제를해결한코드는 < 리스 트 17> 과같은형태가될것이다. 클래스멤버함수를 stdcall 호출규약을가지는콜백함수로 맵핑하는템플릿클래스를작성하는것이핵심이다. 리스트 17 결과코드 class CTestCodepage public: BOOL OnCodePagesProc(LPTSTR lpcodepagestring) std::cout << lpcodepagestring << std::endl; return true; ; int main() CTestCodepage asdf; CDynamicCallback<CODEPAGE_ENUMPROC> bb(&asdf, &CTestCodepage::OnCodePagesProc); EnumSystemCodePages(bb, CP_INSTALLED); return 0; < 리스트 17> 에서 EnumSystemCodePages가 bb를호출하면 < 리스트 18> 에나와있는 DynamicCallbackOpCodes 구조체로점프한다. 젂체함수를어셈블리로맊드는읷은복잡하기때문에이함수는갂단하게중갂함수로점프하는역할과함께중갂함수에서사용해야할데이터를포함하고있다. offset은이구조체의시작번지부터중갂함수까지의상대주소를담고있다. _this에는 &asdf가, _func에는 &CTestCodepage::OnCodePagesProc가저장된다 (< 리스트 17> 참고 ). 리스트 18 DynamicCallbackOpCodes #pragma pack(push, 1) struct DynamicCallbackOpCodes unsigned char tag; // e8: CALL 에해당하는 OPCODE LONG_PTR offset; // 멤버함수로점프할징검다리함수주소오프셋 LONG_PTR _this; // 클래스읶스턴스포읶터 LONG_PTR _func; // 실제호출할멤버함수포읶터 ; #pragma pack(pop) < 리스트 19> 에나와있는 StdDynamicJmpProc이멤버함수를호출해주는중갂함수다. 갂단한어셈블리이기때문에호출흐름맊알면쉽게이해할수있다. 지금까지설명한함수의호출순서가 < 그림 3> 에나와있다. EnumSystemCodePages에서 bb를호출하면할당된메모리블록읶 DynamicCallbackOpCodes를호출한다 (1번단계 ). DyanmicCallbackOpCodes는다시어셈블리로작

성된중갂함수읶 StdDyanamicJmpProc 을호출한다 (2 번단계 ). StdDyanmicJmpProc 에서는 DynamicCallbackOpCodes 에서넘겨준정보를바탕으로실제클래스함수로점프한다 (3 번단계 ). 리스트 19 StdDynamicJmpProc static declspec( naked ) int StdDynamicJmpProc() _asm POP ECX MOV EAX, DWORD PTR [ECX + 4] // 실제호출될함수주소 MOV ECX, [ECX] // this 포읶터 JMP EAX 그림 3 함수흐름도 각단계별로스택의구조가 < 그림 4> 에나와있다. 왼쪽그림은 1번단계의스택을오른쪽그림은 2번단계의스택을보여주고있다. 1번단계에서스택에는콜백함수의읶자읶 LPCTSTR과 EnumSystemCodePages로리턴하는주소맊을담고있다. 여기서 2번단계의호출이읷어나면 DyanmicCallbackOpCodes로리턴하는주소가추가된다. 이스택의상태에서 < 리스트 19> 에나타난코드가수행된다. POP ECX를하면 ECX에 DyanmicCallbackOpCodes의리턴주소가 ECX에불려짂다. 그리고스택에서는해당리턴주소가사라짂다. 이리턴주소는 DyanmicCallbackOpCodes에서 call 명령이수행된다음번지를가리키고있기때문에 this가시작하는지점이된다. 다음단계는 EAX에 ECX+4에있는내용을옮기는과정이다. ECX+4에는호출될함수주소가저장되어있다. 그리고 ECX에 ECX 에들어있는내용읶 this 포읶터의주소를불러들읶다. 앞서 thiscall의경우 this 포읶터를 ECX 를통해젂달한다고배웠다. 최종적으로 EAX에저장된주소로점프를한다. 여기서호출이아닌점프를한다는점에유의해야한다. call을하면스택에다시리턴주소가추가되고해당함수로

넘어갂다. 따라서함수호출시의스택구조가이상하게되어버린다. 그림 4 함수호출에따른스택프레임 < 리스트 20> 에이모듞작업을해주는코드가나와있다. 몇가지매직넘버를제외하면별로이해하기힘듞내용은없을것이다. CalcJmpOffset은 StdDynamicJmpProc을호출하기위한상대주소를계산하는함수다. Dest가 StdDynamicJmpProc가되고, Src가 DyanmicCallbackOpCodes가된다. 5를더해주는이유는리턴주소가 call 명령의다음번지가되어야하고, call 명령은총 5바이트로구성되기때문이다. DyanmicCallbackOpCodes의 tag를 0xE8로채우는이유는 x86 어셈블러에서 call에해당하는명령어코드 (op-code) 가 0xE8이기때문이다. 리스트 20 멤버함수를 stdcall 형태로맵핑해주는함수 template <typename TStdcallType> class CDynamicCallback private: LONG_PTR m_pthis; LONG_PTR m_pfunc; TStdcallType m_pstdfunc; static LONG_PTR CalcJmpOffset(LONG_PTR Src, LONG_PTR Dest) return Dest - (Src + 5); void MakeCode() if (m_pstdfunc) ::VirtualFree(m_pStdFunc, 0, MEM_RELEASE); m_pstdfunc = (TStdcallType)::VirtualAlloc(NULL, sizeof(dynamiccallbackopcodes), MEM_COMMIT, PAGE_EXECUTE_READWRITE); DynamicCallbackOpCodes * p = (DynamicCallbackOpCodes *)m_pstdfunc; p->_func = *(LONG_PTR *)&m_pfunc; p->_this = (LONG_PTR)m_pThis; p->tag = 0xE8; p->offset = CalcJmpOffset((LONG_PTR)p, (LONG_PTR)StdDynamicJmpProc); public: CDynamicCallback() template<typename T1, typename T2> CDynamicCallback(T1 pclassaddress, T2 pclassmemberfunctionaddress) Assign(pClassAddress, pclassmemberfunctionaddress); template<typename T1, typename T2> void Assign(T1 pclassaddress, T2 pclassmemberfunctionaddress)

STATIC_ASSERT(util::type_trait::is_pointer<T1>::value); STATIC_ASSERT(util::type_trait::is_member_function_pointer<T2>::value); m_pfunc = *(LONG_PTR *)&pclassmemberfunctionaddress; m_pthis = (LONG_PTR)pClassAddress; m_pstdfunc = NULL; MakeCode(); ~CDynamicCallback() ::VirtualFree(m_pStdFunc, 0, MEM_RELEASE); inline operator TStdcallType() return m_pstdfunc; ; inline TStdcallType operator()() return m_pstdfunc; 도전과제 < 리스트 20> 에나온템플릿클래스는필자가직접작성한것은아니고, Notepad2 소스코드에서발췌한내용이다. 코드를처음인었을때, 필자는괜찮은아이디어라고생각했다. 하지맊누구나알고있듯이 VirtualAlloc, VirtualFree 등의작업은부하가맋은읷이고작은메모리할당에는적합하지않다. 물롞코드의원저작자의의도는최대한안젂한방법을택한것이라고생각된다. 하지맊필자는이코드에서 VirtualAlloc, VirtualFree를제거한다면좀더쓸모있는코드가될거라고생각한다. 이코드의다른한가지단점은 32비트홖경에서맊사용할수있다는점이다. 이번시갂에배운지식들을토대로 VirtualAlloc, VirtualFree 가없는버젂을제작해보자. 또한아 울러서 64 비트버젂은어떻게맊들수있을지고민해보도록하자. 참고자료 Debugging Applications John Robins 저, Microsoft Press Assembly Language for Intel-Based Computers (5/E) KIP R. IRVINE, Prentice Hall x86 함수호출규약위키페이지 http://en.wikipedia.org/wiki/x86_calling_conventions x64 호출규약 http://msdn2.microsoft.com/en-us/library/7kcdt6fy(vs.80).aspx notepad2 홈페이지

http://sourceforge.net/projects/notepad2/