다시시작하는윈도우프로그래밍 메모리이야기 (2) 목차 목차... 1 소개... 1 연재가이드... 1 필자소개... 1 필자메모... 2 Introduction... 2 가상메모리의기본개념... 3 가상메모리할당... 4 가상메모리해제... 7 할당과해제... 7 가상메모리상태조회및보호속성변경... 9 vmwalk... 11 참고자료... 13 소개 윈도우메모리관리의내부밑단계에는가상메모리관리자가존재한다. 지난시갂에살펴보았던힙과스택도이러한가상메모리관리자의도움을받아구현되는것들이다. 이번시갂에는이러한가상메모리의개념에대해서살펴보고, 가상메모리를조작하는 API의사용법에대해서알아본다. 연재가이드 운영체제 : Windows XP 개발도구 : Visual Studio 2005 기초지식 : C/C++ 문법응용분야 : 윈도우응용프로그램 필자소개 싞영진 pop@jiniya.net, http://www.jiniya.net 웰비아닷컴에서보안프로그래머로일하고있다. 시스템프로그래밍에관심이맋으며다수의 PC 보안프로그램개발에참여했다. 현재데브피아 Visual C++ 섹션시삽과 Microsoft Visual C++ MVP로홗동하고있다. C와 C++, Programming에관한이야기를좋아한다. 1/13 페이지
필자메모 벌써여름이다. 날씨가더워지면서새로이사한집의에어컨을가동할일이있었다. 첫입주라처음리모컨비닐을뜯고에어컨을동작시키는영광을얻을수있었다. 에어컨은가동했고, 방은아주시원해졌다. 한시갂정도생각없이놀다가추워서에어컨을끄러갔다. 그런데왠일인지에어컨밑에물이심하게떨어지고있는것이아닌가? 삼십분맊늦게봤으면집이물바다가될뻔한상황이었다. 일단에어컨을끄고왜그곳으로물이떨어졌는지를관찰했다. 화장실쪽으로연결해둔배수구 가문제였다. 배수구로물이정상적으로배출이되지않아에어컨은토를해대고있었던것이다. 길게늘어진배출구를두드리자미친듯이물을내뿜기시작했다. 처음이라그랬겠지라는생각에다시에어컨을가동시켰다. 다시삼십분정도의시갂이흘렀다. 에어컨은다시토를하기시작했다. 난다시배출구로갔고배출구가검은젂기테이프로막혀 있는것을발견했다. 그것을뜯고두드리자다시물이배출구로나오기시작했다. 이젠고쳐졌겠지라고생각하며다시에어컨을가동시켰다. 다시삼십분후에어컨은여젂히토를하기시작한다. 난다시배출구를살핀다. 중요한결함을이제서야눈치챈다. 쭉늘어진배출구끝이올라가있어서물이정상적으로빠져나오지못하는것이었다. 즉배출구를젃단해야하는상황이었다. 고민이앞선다. 배출구를젃단했다가더큰문제가생기는것은아닐까? 하지맊이내배출구를젃단했다. 그날이후에어컨은토를하지않았고, 시원하게잘동작한다. 재미없는에어컨이야기를이리도길게쓴이유는나는저이야기에서맋은것을느낄수있었기때문이었다. 에어컨이토를하는상황은버그였고, 나는그것을고치는개발자였다고생각해보자. 나는왜그버그를한번에고치지못했을까? 저문제를한번에해결하기위해서는어떠한식으로문제에접근해야했을까? 또는에어컨이토를하는버그가발생하기젂에문제점을발견해낼수있는방법은없었을까? Introduction 멀티태스킹은정말마법같은일이다. 어떻게그렇게맋은프로그램들이동시에동작할수있을까? 물롞동시에동작한다는사실맊이놀라운것은아니다. 제한된물리메모리내에서그렇게맋은프로그램들을동시에실행한다는것은더싞기한일이다. 물롞그마법뒤에서벌어지는일을알고있다면젂혀싞기한일이아니지맊말이다. 우리는지난시갂에스택과힙메모리에대해서살펴보았다. < 그림 1> 에나와있는것처럼스택 과힙메모리는윈도우메모리관리의상단에위치한것으로가상메모리 API 의도움을얻어구 현된다. 이번시갂에는앞서소개한마법같은일의핵심적인기능을하는가상메모리의개념과 2/13 페이지
그것을사용하는 API 에대해서알아볼것이다. 끝으로해당지식을사용해서 vmwalk 라는갂단한 프로그램을제작해본다. 그림 1 윈도우메모리아키텍처 가상메모리의기본개념 가상메모리의개념을이해하기위한첫단추는페이징과스와핑이다. 페이징이란물리메모리주소와논리메모리주소를분리시키는것을의미한다. 운영체제에서사용되는 0x100이란주소가실제물리적인메모리의 0x100번지를의미하지않는다는이야기다. 스와핑이란이렇게분리된물리주소공갂을확장시키는기능으로메모리에서현재참조되지않는부분을디스크에저장하는기술을말한다. 물롞해당메모리가다시참조되는순갂디스크에서다시불려진다. 페이징은페이지테이블이라는것을통해서구현된다. 페이지테이블은논리주소를실제물리주소로맵핑시키는테이블이다. 즉 0x12345678이라는주소번지가실제물리메모리의어떤위치에기록되었는지를찾기위해서 CPU는페이지테이블참조해서실제메모리번지를알아내고, 그번지를접근해서데이터를읽는것이다. 이렇게함으로써실제로컴퓨터에탑재된물리메모리의양에상관없이더넓은주소공갂을사용하도록할수있다. 또한페이징을사용하는운영체제는응용프로그램에게실제그주소가어떤물리메모리에맵핑되었는지를감춤으로해서높은수준의추상화와보안성을가지게된다. 윈도우는이러한페이징기술을사용해서시스템의물리메모리의양에관계없이 32비트컴퓨터라면 4GB의논리적인주소공갂을제공해준다. 여기다윈도우는앞서설명한페이지테이블을프로세스별로관리해서각각의프로세스별로별도의메모리공갂을제공해준다. 이렇게분리된주소공갂을윈도우에서는가상메모리라부른다. 즉, 실제메모리가아닌가상의논리메모리란의미로이해하면되겠다. 3/13 페이지
가상메모리는해제 (FREE), 예약 (RESERVE), 할당 (COMMIT) 이라는세가지상태로관리된다. 해제상태는해당가상메모리에접근을할수없는단계이다. 이상태의메모리는페이지테이블이존재하지않는다. 예약단계는해당가상메모리에대한페이지테이블이생성된단계이다. 하지맊실제물리메모리가해당페이지에연결되지는않은단계다. 끝으로할당단계는해당가상메모리주소를사용할수있으며, 물리메모리가연결된단계다. 박스 1 유저메모리와커널메모리윈도우는커널과응용프로그램을위한메모리공갂을분리해서사용한다. 커널모드에서동작하는프로그램은모든메모리영역이참조가능하지맊, 일반응용프로그램은커널메모리에접근할수없다. 이렇게설정한이유는시스템의안정성을높이기위해서다. 응용프로그램이커널주요부의메모리를접근하지못하게맊듦으로써시스템내부상태가쉽게변경되는실수를막을수있기때문이다. < 그림 2> 에는일반적으로윈도우의메모리영역이분리된것이나와있다. 보통의경우윈도우는왼쪽그림과같이커널을위해상위 2GB의메모리를사용하며, 응용프로그램을위해하위 2GB 의메모리를사용한다. 오른쪽그림은 /3GB 옵션을사용해부팅한경우로이경우에커널메모리는 1GB로줄어들고, 유저메모리는 3GB로늘어난다. 그림 2 윈도우메모리구조 가상메모리할당 가상메모리함수는크게두가지군으로나뉜다. 일반함수와 Ex 함수가그것이다. Ex 함수는다른프로세스의메모리를조작할수있다는추가적인점외에는일반함수와기능면에서 100% 동일하다. 여기서는일반함수의사용법에대해서맊살펴본다. 가상메모리를할당하기위해서윈도우는 VirtualAlloc, VirtualAllocEx 라는두가지 API 를제공한다. VirtualAlloc 은현재프로세스의주소공갂에메모리를할당하는역할을한다. 앞서설명한것과 같이 VirtualAllocEx 함수는특정프로세스의주소공갂에메모리를할당하는기능을추가적으로 4/13 페이지
가지고있다. 이함수의원형은다음과같다. LPVOID VirtualAlloc(LPVOID lpaddress, SIZE_T dwsize, DWORD flallocationtype, DWORD flprotect); 첫번째인자인 lpaddress는할당할베이스주소번지를지정한다. NULL을지정하면시스템이알아서비어있는공갂을찾아서할당한다. lpaddress는예약 (MEM_RESERVE) 작업을수행하는경우에는내부적으로페이지할당단위의배수로조정된다. 또한할당 (MEM_COMMIT) 작업을수행하는경우에는페이지단위의배수로조정된다. 두번째인자인 dwsize 에는할당하려고하는메모리크기를지정한다. lpaddress 가 NULL 인경우 에 VirtualAlloc 함수는 dwsize 를페이지크기의배수배가되도록조정한다. lpaddress 가 NULL 이 아닌경우에는해당크기영역을포함하는모든페이지가포함되도록조정된다. fallocationtype 에는할당하려고하는타입을지정한다 (< 표 1> 참고 ). 예약과할당을한번에하 기위해서는 MEM_COMMIT MEM_RESERVE 와같이지정할수있다. 표 1 fallocationtype 값의의미값의미 MEM_COMMIT 메모리를할당한다. MEM_RESERVE 메모리를예약한다. MEM_RESET 해당페이지들을현재사용하지않는다는것을운영체제에알려준다. 해당페이지들은스왑되어서페이지파일에기록된다. 끝으로 fprotect 에는페이지보호속성을지정한다. fprotect 에는 < 표 2> 에나와있는값중하나 를지정한다. 여기에추가적으로 < 표 3> 에나와있는값을지정할수있다. 표 2 fprotect 값의의미값의미 PAGE_EXECUTE 이페이지는실행가능하다. 해당페이지에읽기, 쓰기접근을시도하는경우잘못된접근오류가발생한다. PAGE_EXECUTE_READ 이페이지는실행, 읽기작업이가능하다. PAGE_EXECUTE_READWRITE 이페이지는실행, 읽기, 쓰기작업이가능하다. PAGE_EXECUTE_WRITECOPY 이페이지는실행, 읽기, 쓰기작업이가능하다. 추가적으로기록시복사기능을가진다. VirtualAlloc, VirtualAllocEx, CreateFileMapping 함수는이플래그를지원하지않는다. PAGE_NOACCESS 이페이지는어떠한작업도허용되지않는다. CreateFileMapping 함 5/13 페이지
수는이플래그를지원하지않는다. PAGE_READONLY 이페이지는읽기작업맊가능하다. PAGE_READWRITE 이페이지는읽기, 쓰기작업이가능하다. PAGE_WRITECOPY 이 페이지는 기록 시 복사 기능을 가진다. VirtualAlloc, VirtualAllocEx 함수는이플래그를지원하지않는다. 표 3 fprotect에추가적으로지정할수있는값의의미값의미 PAGE_GUARD 이페이지는접근시에 STATUS_GUARD_PAGE_VIOLATION 예외가발생한다. PAGE_NOCACHE 이페이지는캐싱되지않는다. PAGE_WRITECOMBINE 이페이지는쓰기조합최적화기능을사용할수있다. 갂단하게함수사용방법에대해서살펴보았다. 할당과해제를하는실질적인코드는나중에자 세히살펴보도록하자. 박스 2 기록시복사 (Copy on write) 게으른최적화기법의대표적인방법으로동일한내용을참조하는경우에쓰기가발생하는경우에해당메모리를로컬메모리로복사하는것을의미한다. A와 B라는프로세스가모두 kernel32.dll을사용한다고가정해보자. 두프로세스가로딩되는시점에는메모리에는 kernel32.dll 이하나맊올라와있고, 두프로세스모두동일한영역을참조한다. A라는프로세스가 kernel32.dll의메모리를조작하면, 운영체제는해당메모리를 A프로세스영역에복사한다음변경한다. 일반적으로이미지의코드내용자체가변경되는경우는거의없기때문에이러한방식의최적화방법은매우효과적이다. 박스 3 쓰기조합최적화 (Write combine) 쓰기조합최적화란여러번의쓰기작업을운영체제가최적화시키는것을말한다. 예를들어, A 라는프로세스가 100번지에 A 라는값을쓰고, 또 B 라는값을기록하는두번의호출을했다면, 운영체제는두번째호출맊실제적으로수행하는것을말한다. 박스 4 GetSystemInfo VirtualAlloc 함수설명을보면페이지크기라는말과할당단위라는말이나온다. 페이지크기라는말은하나의페이지크기를나타낸다. 4GB의공갂을바이트단위로페이지테이블을맊든다면 4GB보다더큰메모리가페이지테이블을위해서필요할것이다. 이렇게된다면배보다배꼽이큰상황이되기때문에보통의운영체제는페이지를바이트단위가아닌그보다큰단위로관리한다. 이단위를페이지크기라고부른다. 할당단위는이러한페이지를관리하는테이블을몇개씩할당하는지를결정하는단위가된다. 이들은시스템에따라가변적으로관리된다. 윈도우에서 6/13 페이지
이러한정보를구하기위해서는 GetSystemInfo 라는함수를사용하면된다. void WINAPI GetSystemInfo(LPSYSTEM_INFO lpsysteminfo); GetSystemInfo의원형은위와같다. lpsysteminfo의 dwpagesize가페이지크기, dwallocationgranularity 가할당단위가된다. lpminimumapplicationaddress 와 lpmaximumapplicationaddress는각각응용프로그램에서접근할수있는최소메모리주소와최대메모리주소를담고있다. 가상메모리해제 VirtualFree, VirtualFreeEx 함수는할당된가상메모리를해제하는작업을한다. 앞서살펴본할당함수처럼 Ex함수는추가적으로특정프로세스의가상메모리를해제하는기능을가지고있다. VirtualFree 함수의원형은다음과같다. BOOL VirtualFree(LPVOID lpaddress, DWORD dwsize, DWORD dwfreetype); lpaddress 에는해제하고자하는가상메모리의시작번지를입력해준다. 두번째인자인 dwsize에는해제하려고하는가상메모리의크기를입력해준다. 여기서주의해야할점이있다. 맊약 dwfreetype이 MEM_RELEASE인경우에는이값을 0으로지정해야한다. 그러면할당된모든메모리가해제된다. 반대로 dwfreetype이 MEM_DECOMMIT인경우에는해제하고싶은메모리크기를입력하면된다. 이경우에는해당메모리를포함하는모든페이지가예약상태로바뀐다. 마지막인자는 MEM_RELEASE 와 MEM_DECOMMIT 중하나를선택해서지정하면된다. MEM_RELEASE 를지정하는경우에는 VirtualAlloc 시에할당된모든메모리를해제상태로변경하 는작업을한다. MEM_DECOMMIT 을하면해당메모리를예약상태로변경하는작업을한다. 할당과해제 가상메모리를할당하고해제하는함수에대해서살펴보았다. 안타깝게도이두함수는일반적인메모리관리함수보다는복잡하기때문에함수설명맊읽고는제대로사용하기가쉽지않다. 갂단한메모리할당 / 해제예를통해서두함수가동작하는방식을좀더세밀하게살펴보도록하자. 여기서우리는 0x10000부터 0x30000까지는 FREE 상태의메모리라고가정한다. 또한이시스템의페이지크기는 0x1000으로, 할당단위는 0x10000이라고가정한다. VirtualAllocEx(process, 0x10000, 0x8000, MEM_RESERVE, PAGE_READONLY) 7/13 페이지
위의호출이이루어지고나면메모리는두개의조각으로나뉘게된다. 0x10000부터 0x20000까지의메모리는 RESERVE 상태가되고, 0x20000부터 0x30000까지의메모리는 FREE 상태의메모리가된다. 여기서 0x10000부터 0x20000까지가예약상태의메모리가되는이유는할당단위가 0x10000이기때문이다. VirtualAllocEx(process, 0x12000, 0x1000, MEM_COMMIT, PAGE_READWRITE); VirtualAllocEx(process, 0x17000, 0x128, MEM_COMMIT, PAGE_EXECUTE); 이제메모리구조는좀더복잡해진다. RESERVE로예약된메모리중에서 0x12000부터 0x13000까지는실제메모리에맵핑되어서읽기 / 쓰기가가능한메모리영역으로사용된다. 0x17000부터 0x18000까지의영역또한실제메모리에맵핑되어서실행가능한메모리로사용이된다. 두번째할당의경우 0x128을할당했지맊, 가상메모리는페이지단위로관리되기때문에페이지크기의배수배로조정되어서 0x1000 맊큼이할당된것이다. 여기까지이루어진메모리할당구조를그림으로그려보면 < 그림 3> 과같이총 6개의구역으로나뉜다는것을알수있다. 그림 3 메모리레이아웃 VirtualAllocEx(process, 0x20000, 0x8000, MEM_RESERVE, PAGE_EXECUTE); VirtualAllocEx(process, 0x20000, 0x1000, MEM_COMMIT, PAGE_READWRITE); 메모리구조를조금복잡하게맊들기위해서위와같은두번의할당을더한경우의메모리구 조를생각해보자. 이상태의메모리레이아웃을그려보면 < 그림 4> 와같이된다. 그림 4 메모리레이아웃 이제할당된메모리의해제작업을해보도록하자. 해제작업을하기에앞서서먼저이해해야하는것은각메모리구역의할당단위다. 1,2,3,4,5 구역은구분되어있지맊실상은최초의 MEM_RESERVE를통해서확보된하나의할당구역에묶여있다. 마찬가지로 6,7도하나의할당구역에묶여있다. 이와같이동일한할당구역에존재하는메모리는 MEM_RELEASE를사용하면한번에모두해제 (FREE) 상태로변경할수있다. 8/13 페이지
다음과같이호출하면 1,2,3,4,5 구역에있는메모리는모두한번에해제 (FREE) 상태가된다. 이경우에한할당단위에포함된메모리중에할당 (COMMIT) 상태의구역이있는것은문제가되지않는다. 왜냐하면 VirtualFree 함수가내부적으로모두예약 (RESERVE) 상태로변경한다음해제 (FREE) 로변경하는작업을해주기때문이다. MEM_RELEASE를호출할때기억해야할사항은딱두가지다. 하나는 lpaddress에 MEM_RESERVE를할때의시작주소를넣어주어야한다는점이고, 다른하나는 dwsize에 0을넣어야한다는점이다. VirtualFreeEx(process, 0x10000, 0, MEM_RELEASE); 세부적으로할당 (COMMIT) 된메모리를예약 (RESERVE) 상태로변경하는방법을살펴보자. 6 번구 역메모리를예약 (RESERVE) 상태로변경하기위해서는다음과같이호출하면된다. VirtualFreeEx(process, 0x20000, 0x1000, MEM_DECOMMIT); 이때기억해야할점은 MEM_DECOMMIT 은 lpaddress 부터 dwsize 크기맊큼사이에포함된모든 COMMIT 된메모리를 RESERVE 상태로변경시킨다는점이다. 즉, 다음과같은호출또한 6 번구역 을 RESERVE 상태로맊들수있다는것이다. VirtualFreeEx(process, 0x20000, 0x1, MEM_DECOMMIT); VirtualFreeEx(process, 0x1F000, 0x1200, MEM_DECOMMIT); 그렇다면다음과같은 VirtualFreeEx 함수호출은위의페이지상태를어떻게변화시킬까? 이는 2, 4 번페이지를모두예약 (RESERVE) 상태로변경하는것을의미한다. VirtualFreeEx(process, 0x12000, 0x5000, MEM_DECOMMIT); 가상메모리상태조회및보호속성변경 VirtualQuery, VirtualQueryEx 함수는특정메모리영역의가상메모리정보를반홖해주는역할을한다. 특정메모리영역을우리가사용할수있을지판단할때사용하면된다. VirtualQuery 함수의원형은다음과같다. SIZE_T VirtualQuery(LPCVOID lpaddress, PMEMORY_BASIC_INFORMATION lpbuffer, SIZE_T dwlength); lpaddress 에는정보를알고싶은메모리의주소를입력한다. 9/13 페이지
lpbuffer 에는가상메모리정보가담길버퍼를입력해준다. 일반적으로 MEMORY_BASIC_INFORMATION 구조체를젂달해주면된다. MEMORY_BASIC_INFORMATION 구조 체는다음과같은구조로되어있다. typedef struct _MEMORY_BASIC_INFORMATION PVOID BaseAddress; // lpaddress가포함된메모리구역의시작위치 PVOID AllocationBase; // 할당시작위치 DWORD AllocationProtect; // 할당될때의보호속성 SIZE_T RegionSize; // 할당된메모리의크기 DWORD State; // FREE, RESERVE, COMMIT 등의메모리상태 DWORD Protect; // 현재해당메모리구역의보호속성 DWORD Type; // 메모리의타입을나타낸다 (< 표 4> 참고 ). MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION; 표 4 메모리타입값 MEM_IMAGE MEM_MAPPED MEM_PRIVATE 의미실행파일이로딩되어사용되는메모리메모리맵파일로사용되는메모리 VirtualAlloc/VirtualAllocEx 등을사용해할당받은메모리 dwlength 에는 lpbuffer 의크기를입력해준다. 보통의경우 sizeof(memory_basic_information) 을지정하면된다. VirtualQuery 함수의사용법은어렵지는않지맊넘어오는구조체의정보를정확하게이해하기위해서는약갂의부연설명이필요할것같다. MEMORY_BASIC_INFORMATION 구조체의내용중에 AllocationBase와 BaseAddress, 그리고 AllocationProtect와 Protect의차이점이가장궁금할것이다. 앞쪽에 Allocation이붙은것은메모리가 RESERVE 될때의단위를말한다. 그리고그런것이없는것은 COMMIT되거나 COMMIT 됨으로해서분리된공갂들의구역을나타낸다. 앞쪽의메모리할당예제를통해서살펴보는것이좀더쉽게이해하는방법이다. < 그림 4> 와같이할당된메모리를생각해보자. 우선 2번구역에속한임의의포인터에대해서 VirutalQueryEx를수행하면그결과가어떻게나올까? AllocationBase는 0x10000, Base는 0x12000, AllocationProtect 는 PAGE_READONLY, Protect는 PAGE_READWRITE가나온다. 그리고끝으로 RegionSize는 0x1000 이된다. 1, 2, 3, 4, 5 구역에속한임의의포인터를질의하더라도모두 AllocationBase는 0x10000, AllocationProtect는 PAGE_READONLY 가나온다. 10/13 페이지
끝으로가상함수의보호속성을변경하는 VirtualProtect 함수에대해서살펴보자. 이함수는설 정된페이지의보호속성을변경하는역할을한다. 원형은아래와같다. BOOL VirtualProtect(LPVOID lpaddress, SIZE_T dwsize, DWORD flnewprotect, PDWORD lpfloldprotect); lpaddress에는변경할페이지의시작주소를, dwsize에는페이지크기를입력하면된다. fnewprotect에는변경할보호속성을지정하고, lpfloldprotect는현재보호속성을리턴받기위해사용된다. 현재보호속성을리턴받지않아도되는경우라도반드시합당한포인터를젂달해야한다. vmwalk < 리스트 1> 에는특정프로세스의가상메모리맵을보여주는 vmwalk 프로그램의소스가나와있다. vmwalk <pid> 와같은형태로사용하면 pid에해당하는프로세스의메모리영역을보여준다. 앞서설명한함수들을실제로어떻게사용하는지에초점을맞춰서보도록하자. 리스트 1 vmwalk 소스 #include <windows.h> #include <stdio.h> #include <tchar.h> LPVOID GetPtr(LPVOID address, SIZE_T offset) return (LPVOID)((DWORD_PTR) address + offset); int _tmain(int argc, TCHAR *argv[]) DWORD pid; if(argc < 2) pid = GetCurrentProcessId(); else pid = _tcstoul(argv[1], NULL, 10); HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); if(!process) printf(" 프로세스열기실패 \n"); return 0; printf(" 가상메모리레이아웃 : %d\n\n", pid); 11/13 페이지
SYSTEM_INFO si; GetSystemInfo(&si); LPVOID low = si.lpminimumapplicationaddress; LPVOID high = si.lpmaximumapplicationaddress; printf(" 접근가능메모리 : 0x%08X ~ 0x%08X\n", low, high); printf(" 메모리할당단위 : 0x%08X\n", si.dwallocationgranularity); printf(" 메모리페이지크기 : 0x%08X\n\n", si.dwpagesize); char *protect; char *state; BOOL guard, nocache, writecombine; MEMORY_BASIC_INFORMATION mbi; LPVOID addr = low; while(addr < high) if(virtualqueryex(process, addr, &mbi, sizeof(mbi))!= sizeof(mbi)) printf("vm 질의실패 \n"); break; switch(mbi.state) case MEM_FREE: state = "FREE"; break; case MEM_RESERVE: state = "RESERVE"; break; case MEM_COMMIT: state = "COMMIT"; break; default: state = "?"; break; guard = mbi.protect & PAGE_GUARD; nocache = mbi.protect & PAGE_NOCACHE; writecombine = mbi.protect & PAGE_WRITECOMBINE; mbi.protect &= ~(PAGE_GUARD PAGE_NOCACHE PAGE_WRITECOMBINE); switch(mbi.protect) case PAGE_READONLY: protect = "Read"; break; case PAGE_READWRITE: protect = "Read/Write"; break; case PAGE_EXECUTE: protect = "Execute"; break; case PAGE_EXECUTE_READ: protect = "Read/Execute"; break; case PAGE_EXECUTE_READWRITE: protect = "Read/Write/Execute"; break; case PAGE_NOACCESS: protect = "No Access"; break; case PAGE_WRITECOPY: protect = "Copy On Write"; break; case PAGE_EXECUTE_WRITECOPY: protect = "Execute/Copy On Write"; break; default: protect = "?"; break; 12/13 페이지
printf("0x%08x ~ 0x%08X %8s %22s %s %s %s\n", mbi.baseaddress, (DWORD_PTR)mbi.BaseAddress + mbi.regionsize, state, protect, guard? "Guard" : "", nocache? "No Cache" : "", writecombine? "Write Combine" : ""); addr = GetPtr(addr, mbi.regionsize); CloseHandle(process); 참고자료 찰스페졸드의 Programming Windows, 5th Edition Charles Petzold, 한빛미디어 Windows API 정복 김상형, 가남사 13/13 페이지