외규장각 도서 환수 모금 캠페인

Controlling Memory Space of Other Process

Posted 2007. 6. 19. 14:55, Filed under: Study/Computer Science
================================================================================
Title : Controlling Memory Space of Other Process

Author : proXima
Contact : proxima ]at[ postech.ac.kr
Date : 2006/11/15 - 2006/11/15
================================================================================

요즘의 OS들은 대부분 보호모드(Protected Mode)를 지원한다.
보호모드라는 것은, 프로세스 내에서 메모리를 액세스 할 때에 자신의 가상 메모리 공간을 자신의 영역이라고 간주하도록 하는 것이다. 무슨 이야기냐 하면, OS는 프로세스 간에 메모리를 건드리지 못하게 보호해 준다는 것이다.
하지만 MS Windows에서는 디버깅을 위해 다른 프로세스의 메모리를 건드릴 수 있게 해 주는 API를 제공해 주는데, 그것들은 다음과 같다.



BOOL __stdcall ReadProcessMemory(
 HANDLE hProcess,
 LPCVOID lpBaseAddress,
 LPVOID lpBuffer,
 SIZE_T nSize,
 SIZE_T* lpNumberOfBytesRead
);


BOOL __stdcall WriteProcessMemory(
 HANDLE hProcess,
 LPVOID lpBaseAddress,
 LPCVOID lpBuffer,
 SIZE_T nSize,
 SIZE_T* lpNumberOfBytesWritten
);


 
함수의 이름만 봐도, 뭐 하는 함수인지를 뻔히 알 수가 있다.
이 두 함수는 다른 프로세스의 메모리 공간에 있는 데이터를 읽어오거나(ReadProcessMemory) 혹은 다른 프로세스 메모리 공간에 데이터를 쓸 수 있게(WriteProcessMemory) 해 준다.

위에서 사용되는 인자 중 hProcess는 OpenProcess 혹은 CreateProcess를 통해 얻어온 타겟 프로세스의 핸들값이어야 한다. 그리고 lpBaseAddress는 타겟 프로세스의 가상메모리 주소여야 한다.
뭐 나머지는 별로 문제 될만한 건 없어 보인다.

그러면 간단하게 남의 프로세스 메모리를 읽어오는 프로그램을 만들어 보자.


HANDLE hTargetProcess = OpenProcess(PROCESS_VM_READ, FALSE, pid);
ReadProcessMemory(hTargetProcess, (LPCVOID)BaseAddr, (LPVOID)buf, \
                  sizeof(buf), (SIZE_T*)&nWritten);
CloseHandle(hTargetProcess);


 
간단하지만, 실수의 여지가 조금 있다. OpenProcess로부터 받아온 HANDLE값을 CloseHandle로 닫아주지 않을 경우가 생길 수 있다. 그러면 핸들이 계속 남아있게 된다.
항상 저렇게 3줄을 세트로 사용한다면 문제 없겠지만, 실수를 원천봉쇄하는 방법이 있다.
바로 Toolhelp 함수를 사용하는 것인데, 다음과 같다.


BOOL __stdcall Toolhelp32ReadProcessMemory(
  DWORD th32ProcessID,
  LPCVOID lpBaseAddress,
  LPVOID lpBuffer,
  SIZE_T cbRead,
  SIZE_T lpNumberOfBytesRead
);
 
아쉽게도, ReadProcessMemory만 된다. WriteProcessMemory에 대한 것은 없지만, 직접 만들어서 사용하여도 무방할 듯 하다.
아주 간단하게 Toolhelp32ReadProcessMemory처럼 동작하는 WriteProcessMemory에 대한 함수도 만들 수 있는데, 일단 Toolhelp32ReadProcessMemory 함수의 내부를 좀 보자.


MOV EDI,EDI
PUSH EBP
MOV EBP,ESP
PUSH ESI
PUSH DWORD PTR SS:[EBP+8]
PUSH 0
PUSH 10
CALL kernel32.OpenProcess
MOV ESI,EAX
TEST ESI,ESI
JE SHORT kernel32.7C863D5A
PUSH EDI
PUSH DWORD PTR SS:[EBP+18]
PUSH DWORD PTR SS:[EBP+14]
PUSH DWORD PTR SS:[EBP+10]
PUSH DWORD PTR SS:[EBP+C]
PUSH ESI
CALL kernel32.ReadProcessMemory
PUSH ESI
MOV EDI,EAX
CALL kernel32.CloseHandle
MOV EAX,EDI
POP EDI
POP ESI
POP EBP
RETN 14


 
디버거에서 코드를 가져다가 붙이는 바람에, JE SHORT kernel32.7C863D5A 라는 코드가 그대로 들어갔는데, 실제로 7C863D5A의 주소에 있는 명령은 밑에서 세 번째 줄에 있는 POP ESI이다.
이 코드가 조금 복잡해 보이더라도, 실제로 보면 굉장히 간단하다. 이 코드는 위에 C코드로 설명된 OpenProcess -> ReadProcessMemory -> CloseHandle 의 과정을 하나의 함수에 담아 실행되도록 만들어 둔 것을 뜻한다.
OpenProcess를 실행하기 전에 PUSH 10 을 하는 이유는, 그것이 PROCESS_VM_READ 플래그이기 때문이다.

아래에 MyWriteProcessMemory를 한 번 만들어 보도록 하겠다.
다른 부분은 다 같지만, PROCESS_VM_READ 대신 PROCESS_VM_WRITE를 통해 프로세스를 열어야 한다는 점이 다를 수 있다.


BOOL __stdcall MyWriteProcessMemory(
 DWORD pid,
 LPVOID lpBaseAddress,
 LPCVOID lpBuffer,
 SIZE_T cbWritten,
 SIZE_T* lpNumberOfBytesWritten)
{
    BOOL bRet = FALSE;

    HANDLE hTargetProcess = OpenProcess(PROCESS_VM_WRITE, FALSE, pid);
    if(hTargetProcess == NULL)
    {
        return bRet;
    }

    bRet = WriteProcessMemory(hTargetProcess, lpBaseAddress, lpBuffer, \
                              cbWritten, lpNumberOfBytesWritten);
    CloseHandle(hTargetProcess);

    return bRet;
}
 
이런 API들을 이용하여 게임핵처럼 다른 프로세스의 메모리를 읽고 쓰고 하는 프로그램들을 만들 수 있다. 물론 그런 프로그램들을 만드는 것이 다는 아니다.
총 여섯 편으로 계획한 연재 내용에는 없지만, 나중에 시간이 허락한다면 다른 프로세스의 메모리를 읽어서 그 프로세스의 메모리 공간에 떠 있는 모듈들이 어떤 함수들을 import하고 export하는 지를 알아내는 과정에 대해 조금 더 설명하도록 하겠다.
또한, 이와 같이 다른 프로세스의 메모리에 쓰기가 가능하기 때문에 'DLL Injection' 이라는 매우 강력한 무엇인가를 할 수 있게 되는데, DLL Injection에 대해서는 다음 글에서 자세히 다루도록 하겠다.

------------
dll 인젝션 찾다가 같이 찾은 자료들.
이런 내용들을 나도 모르는 것은 아니지만 이렇게 체계적으로 정리하고 깊이 파고드는 자세가 부족했던 것 같다.

상당히 자극 받았음 ㅋ
Response : ,


Recent Posts

Recent Comments

Recent Trackbacks

Total hit (Today , Yesterday )

Admin Write Post