본문 바로가기

My Study/Programming&Theory

VirtualQuery 동작

VirtualQuery 함수에 특정 주소를 넘기면 해당 주소를 포함하는 첫 번째 페이지의 속성과 페이지 기본 주소를 가져오고 


메모리 상태

MEM_COMMIT

MEM_RESERVE

MEM_FREE

MEM_PRIVATE

MEM_MAPPED

MEM_IMAGE


엑세스 권한

PAGE_READONLY

PAGE_READWRITE

PAGE_NOACCESS

PAGE_WRITECOPY

PAGE_EXECUTE

PAGE_EXECUTE_READ

PAGE_EXECUTE_READWRITE

PAGE_EXECUTE_WRITECOPY

PAGE_GUARD

PAGE_NOCACHE


위 2개 나열한 메모리 상태 및 엑세스 권한이 다를 때까지 메모리를 스캔 후 해당 사이즈를 바이트 단위로 가져온다.

사이즈는 VirtualQuery에 넘긴 주소를 포함하는 페이지 기본 주소부터 스캔 완료된 시점까지의 크기이다.

즉, 0x00000000~0x00005FFF 이 상태 및 권한이 같은 경우, 0x00002000 주소를 넘기면 0x00004000 사이즈를 리턴한다.


예시코드


위 코드를 돌리면!

before [0x00000020] 0x00401000 0x00008000

before [0x00000020] 0x00402000 0x00007000

before [0x00000020] 0x00403000 0x00006000

before [0x00000020] 0x00404000 0x00005000

before [0x00000020] 0x00405000 0x00004000

before [0x00000020] 0x00406000 0x00003000

before [0x00000020] 0x00407000 0x00002000

before [0x00000020] 0x00408000 0x00001000

after [0x00000080] 0x00401000 0x00008000

after [0x00000080] 0x00402000 0x00007000

after [0x00000080] 0x00403000 0x00006000

after [0x00000080] 0x00404000 0x00005000

after [0x00000080] 0x00405000 0x00004000

after [0x00000080] 0x00406000 0x00003000

after [0x00000080] 0x00407000 0x00002000

after [0x00000080] 0x00408000 0x00001000


와 같은 결과가 나온다. 


또 다른 정보..

MEM_IMAGE 영역을 PAGE_EXECUTE_READWRITE 권한으로 바꾸면 PAGE_EXECUTE_WRITECOPY 권한이 되지만 해당 영역에 Write가 발생하게 되면 PAGE_EXEUCTE_READWRITE 권한으로 바뀌게 된다. copy-on-write 이므로 당연한 결과~

commit된 copy-on-write페이지에 write가 시도되면 프로세스에 해당 페이지가 private 영역에 복사본이 생기고 해당 영역은 PAGE_EXECUTE_READWRITE 가 된다. 그리고 변경 사항은 새로운 페이지에 기록된다.

그렇다고 VirtualQuery 했을 때 그 영역 타입이 MEM_PRIVATE이 되는건 아님.. MEM_IMAGE 그대로..


주저리주저리 썼지만.. 이놈의 VirtualQuery 때문에 애먹은게 짱나서 대충이라도 정리..


아래는 찾은 ReactOS 관련 소스코드.. 슥 보면 아하?!

빨간색으로 된 부분 보시면 어떠한 조건일 때 빠져오는지.. 영역 크기는 어떻게 구해지는지 알 수 있다.

      /* Build the initial information block */
        Address = PAGE_ALIGN(BaseAddress);
        MemoryInfo.BaseAddress = Address;
        MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
        MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
        MemoryInfo.Type = MEM_PRIVATE;

        /* Find the largest chunk of memory which has the same state and protection mask */
        MemoryInfo.State = MiQueryAddressState(Address,
                                               Vad,
                                               TargetProcess,
                                               &MemoryInfo.Protect,
                                               &NextAddress);
        Address = NextAddress;
        while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
        {
            /* Keep going unless the state or protection mask changed */
            NewState = MiQueryAddressState(Address,Vad,TargetProcess,&NewProtect,&NextAddress);
            if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
            Address = NextAddress;
        }

        /* Now that we know the last VA address, calculate the region size */
        MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);