본문 바로가기

해킹

리버싱 이야기, 어떻게 메모리 영역 브레이크포인트가 가능할까?

안녕하세요. 오늘은 windbg에는 없고 ollydbg에는 있는 메모리 영역 브레이크포인트에 대해 쓰려고 합니다.

windbg와 ollydbg에는 코드영역에 software breakpoint와 hardware breakpoint가 존재합니다. 

그러나 ollydbg에는 메모리 영역 전체를 breakpoint 걸 수 있는 방법이 있으나, windbg에는 기능이 없죠.

어떻게 메모리 영역 브레이크포인트가 가능할까요?

 

+  2780000  2781000     1000 MEM_MAPPED  MEM_COMMIT  PAGE_READONLY                      MappedFile "PageFile"
+  2781000  2790000     f000             MEM_FREE    PAGE_NOACCESS                      Free       
+  2790000  2796000     6000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Heap       [ID: 2; Handle: 02790000; Type: Segment]
   2796000  279f000     9000 MEM_PRIVATE MEM_RESERVE                                    Heap       [ID: 2; Handle: 02790000; Type: Segment]
   279f000  27a0000     1000 MEM_PRIVATE MEM_RESERVE                                    <unknown>  
+  27a0000  289a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack      [~4; 3b64.2adc]
   289a000  289c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack      [~4; 3b64.2adc]
   289c000  28a0000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack      [~4; 3b64.2adc]
+  28a0000  296f000    cf000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Heap       [ID: 0; Handle: 008c0000; Type: Segment]
   296f000  2971000     2000 MEM_PRIVATE MEM_RESERVE                                    Heap       [ID: 0; Handle: 008c0000; Type: Segment]
   2971000  299f000    2e000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Heap       [ID: 0; Handle: 008c0000; Type: Segment]
   299f000  29a0000     1000 MEM_PRIVATE MEM_RESERVE                                    <unknown>  
+  29a0000  2a41000    a1000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     <unknown>  [.............s..]
   2a41000  2aa0000    5f000 MEM_PRIVATE MEM_RESERVE                                    <unknown>  
+  2aa0000  3010000   570000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     <unknown>  [................]
   3010000  4aa0000  1a90000 MEM_PRIVATE MEM_RESERVE                                    <unknown>  
+  4aa0000  4aa1000     1000 MEM_MAPPED  MEM_COMMIT  PAGE_READONLY                      MappedFile "PageFile"

windbg에서 !address 명령을 사용하면 위와 같이 메모리 정보들을 가져올 수 있습니다. 

메모리는 크게 힙메모리, 스택이 있으며 각각 용도에 맞는 권한들을 포함하고 있습니다. 

보통 메모리는 READ(읽기), WRITE(쓰기) EXECUTE(실행) 권한 중 최소 하나를 가지게 됩니다.

 

자, 다시 본론으로 돌아와서 어떻게 ollydbg는 메모리 영역에 대해 브레이크포인트를 걸 수 있는 것 일까요?

그 정답은 방금 설명드린 메모리의 권한에 있습니다. 

만약 어떤 메모리 영역에서 실행권한이 없는데 실행된다면 어떻게 될까요?

 

만약 실행권한이 없는 메모리영역이 실행되게 된다면 프로세스는 access violation 에러가 나면서 종료되게 됩니다.

그러나 디버깅 중이라면 프로세스가 종료되지 않고 디버거에서 access violation을 처리 할 수 있게 되고

해당 메모리가 실행되는 곳에서 멈추게 되는 것입니다.

 

위에서는 실행 브레이크 포인트에 대해서만 예를 들었지만

메모리 읽기, 쓰기 브레이크포인트도 똑같은 방식으로 동작합니다.

얼마 전 작성한 windbg 스크립트를 조금 바꾸면 메모리 영역 브레이크 포인트 스크립트를 만들 수 있습니다.

 

import sys 
import pykd 
import re 
from ctypes import *
import argparse 

parser = argparse.ArgumentParser()
parser.add_argument('option')
parser.add_argument('address', help='memory address to set up breakpoint')
parser.add_argument('protection')
args = parser.parse_args() 

PAGE_NOACCESS = 0x01
PAGE_READONLY = 0x02
PAGE_READWRITE = 0x04
PAGE_WRITECOPY = 0x08
PAGE_EXECUTE = 0x10
PAGE_EXECUTE_READ = 0x20
PAGE_EXECUTE_READWRITE = 0x40
PAGE_EXECUTE_WRITECOPY = 0x80
PAGE_GUARD = 0x100 

PROCESS_VM_READ = 0x10 
PROCESS_VM_WRITE = 0x20
PROCESS_ALL_ACCESS = 0x1F0FFF

breakPoints = dict() 

def getProcessName():
    moduleList = pykd.dbgCommand("!peb").split('Base TimeStamp                     Module')[1].strip().split('\n')
    return moduleList[0].split(' ')[-1]

def getPid():
    return int(pykd.dbgCommand("!teb").split("ClientId:")[1].split('\n')[0].strip().split(' . ')[0],16)
    
def getPageGuard():
    heap = pykd.dbgCommand("!address").split("\n")
    result = list()
    for h in heap:
        if( h.find("GUARD") >= 0):
            result.append(h)
    return result
    
def fixPageGuard(handle, addr, size, attribute):
    org = pointer(c_int(0))
    r = cdll.kernel32.VirtualProtectEx(handle, addr, size, PAGE_READWRITE, org)
    if( r == 0 ):
        print("VirtualProtectEx Error! %d" % cdll.kernel32.GetLastError())
    return r

def fixAllPageGuards():
    handle = openProcess()
    pageGuard = getPageGuard()
    for p in pageGuard:
        print(p)
        memory = re.sub("[\s]+", " ", p)[1:].split(' ')
        address = int(memory[0],16)
        size = int(memory[2], 16)
        attribute = memory[5]
        r = fixPageGuard(handle, address, size, attribute)
    return 
    
def openProcess():
    handle = cdll.kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, getPid())
    if( handle == 0 ):
        print('OpenProcess Error! %d' % cdll.kernel32.GetLastError())
    return handle 

def readProcessMemory(handle, addr, size):
    result = c_char_p(size)
    retSize = 0
    ret = cdll.kernel32.ReadProcessMemory(handle, addr, result, size, retSzie)
    if( ret == 0):
        print('ReadProcessMemory Error! %d' % cdll.kernel32.GetLastError())
    return result

def getMemoryInfo(address):
    memory = pykd.dbgCommand("!address").split("\n")
    for memInfo in memory[3:]:
        memInfo = re.sub('[\s]+', ' ', memInfo)
        memInfo = memInfo.split(' ')
        addrInfo = int(memInfo[1], 16)
        addrSize = int(memInfo[3], 16)
        protection = memInfo[6]
        if(address >= addrInfo and (address - addrInfo) < addrSize):
            return (addrInfo, addrSize, memInfo, protection)
    return None 
    
def clearBreakPointOnMemory(address, verbose=True):
    if( hex(address) in breakPoints):
        mInfo = getMemoryInfo(address)
        (addrInfo, addrSize, memInfo, protection) = mInfo
        if( mInfo == None ):
            if( verbose == True ):
                print( "Memory address is not found !!")
            return None   
        org = pointer(c_int(0))   
        handle = openProcess()
        if( handle == 0):
            return 
        ret = cdll.kernel32.VirtualProtectEx(handle, addrInfo, addrSize, breakPoints[hex(addrInfo)], org)
        if( ret == 0 ):
            print("VirtualProtectEx Error! %d" % cdll.kernel32.GetLastError())
        cdll.kernel32.CloseHandle(handle)
        return ret 
    if( verbose == True ):
        print("Breakpoint is not found!!! %x" % address)
    return None
    
def breakPointOnMemory(address, type="r"):
    clearBreakPointOnMemory(address, False)
    mInfo = getMemoryInfo(address)
    if( mInfo == None ):
        print( "Memory address is not found !!")
        return None 
        
    (addrInfo, addrSize, memInfo, protection) = mInfo

    prot = 0
    toProtection = 0 
    org = pointer(c_int(0)) 
    if( protection == 'PAGE_NOACCESS' ):
        prot = PAGE_NOACCESS
    elif( protection == 'PAGE_READONLY'):
        prot = PAGE_READONLY
    elif( protection == 'PAGE_READWRITE'):
        prot = PAGE_READWRITE
    elif( protection == 'PAGE_WRITECOPY'):
        prot = PAGE_WRITECOPY
    elif( protection == 'PAGE_EXECUTE'):
        prot = PAGE_EXECUTE
    elif( protection == 'PAGE_EXECUTE_READ'):
        prot = PAGE_EXECUTE_READ
    elif( protection == 'PAGE_EXECUTE_READWRITE'):
        prot = PAGE_EXECUTE_READWRITE
    elif( protection == 'PAGE_EXECUTE_WRITECOPY'):
        prot = PAGE_EXECUTE_WRITECOPY
      
    if( prot == PAGE_NOACCESS ):
        return   
    
    if( type == 'r' ):
        if( prot < 0x10 ):
            toProtection = prot ^ PAGE_GUARD
        else:
            toProtection = PAGE_EXECUTE
    if( type == 'w'):
        if( prot < 0x10 ):
            toProtection = PAGE_READONLY
        else:
            toProtection = PAGE_EXECUTE_READ
    if( type == 'e'):
        if( prot < 0x10 ):
            return
        else:
            toProtection = prot >> 4
        
    handle = openProcess()
    if( handle == 0):
        return 
    print( hex(addrInfo), hex(addrSize), hex(toProtection))
    ret = cdll.kernel32.VirtualProtectEx(handle, addrInfo, addrSize, toProtection, org)
    error = cdll.kernel32.GetLastError()
    cdll.kernel32.CloseHandle(handle)
    if( ret == 0 ):
        print("VirtualProtectEx Error! %d" % error)
        return 
        
    breakPoints[hex(addrInfo)] = prot 
    print("VirtualProtectEx Success !! %x" % addrInfo)
    return ret 
    
if( args.option == 'bp'):
    breakPointOnMemory( int(args.address, 16), args.protection)
elif( args.option == 'bc'):
    clearBreakPointOnMemory(int(args.address, 16))

 

특정 메모리 영역에 breakpoint를 거는 스크립트입니다. 

사용방법은 처음 windbg를 attach하거나 프로세스를 열고

.load pykd 명령어 실행 후 !py script.py 주소 권한(r,w,e)를 넣으시면 됩니다. 

성공 시 아래와 같이 나옵니다.

0:031> !py script.py bp 186e000  r
0x186e000 0x2000 0x104
VirtualProtectEx Success !! 186e000