본문 바로가기
운영체제/[ecourse] Windows Programming

4-2. Process ID & Handle

by 헛둘이 2022. 9. 13.
PROCESS ID란?
  • 프로세스가 만들어지면 2개의 핸들이 생성된다.
  • PROCESS KERNEL OBJECT (PKO), THREAD KERNEL OBJECT (TKO)
  • TKO가 생기는 이유는 프로세스 생성 시 주 스레드가 자동으로 생성되기 때문

 

PID를 구하는 방법

 1. 자신의 프로세스 ID 구하기

  • GetCurrentProcessId() : 내 PROCESS ID 구하기
  • GetCurrentThreadId() : 내 THREAD ID 구하기
#include <iostream>
#include <Windows.h>

using std::cout;
using std::endl;

int main()
{
	DWORD pid = GetCurrentProcessId();
	cout << "내 pid는 " << pid << endl;
}

 

 

 

 2. 다른 프로세스의 ID 구하기

  • 프로세스 열거 : 모든 프로세스의 이름과 ID를 얻을 수 있다 (작업 관리자)
  • GetWindowThreadProcessId() : 윈도우 핸들을 이용해서 그 프로세스의 PID와 TID를 구한다.
#include <iostream>
#include <Windows.h>

using std::cout;
using std::endl;

int main()
{
	HWND hwnd = FindWindow(0, L"제목 없음 - Windows 메모장");
	
	DWORD pid;
	DWORD tid = GetWindowThreadProcessId(hwnd, &pid);

	cout << "notepad의 pid : " << pid << endl;
	cout << "notepad의 tid : " << tid << endl;
}

메모장을 다시 껐다 켜지 않는 한 같은 PID와 TID가 출력된다.
PID는 메모장이 켜져있는 시점에 한해서는 변하지 않는다.

 

 

 


프로세스 핸들 구하기
  • pid로는 별로 할 수 있는 게 없고, 우리는 윈도우에게 이 pid로 핸들을 발급해달라고 요청해야 한다.
  • 그러면 발급된 핸들로 여러 작업들을 할 수 있다.
  • pid는 누구나 구할 수 있지만 , 핸들은 보안체크를 해서 발급하므로 프로세스마다 다를 수 밖에 없다.
  • pid로 핸들을 구하려면 OpenProcess 함수를 사용하면 된다.

 

 

  • dwDesiredAccess 인자의 경우 이 핸들을 통해 사용할 수 있는 권한을 의미한다.
  • PROCESS_ALL_ACCESS는 모든 권한을 의미하는데, 환경에 따라 거부될 수 있다.

 

- OpenProcess를 호출하면 호출자의 프로그램의 커널 오브젝트 테이블에 항목을 만들고 그
인덱스를 통해 저장된 주소로 접근한다.

사용한 후에는 CloseHandle로 닫아주어야 한다.
  • 이를 눈으로 확인하려면 ProcessExplorer로 확인 가능하다.

OpenProcess 실행 전-

 

 

OpenProcess 실행 후-

 

  • Process가 커널 오브젝트 테이블에 추가된 것을 확인할 수 있다.

 

 

 

 

 


다른 프로세스의 가상 메모리 읽기
  • A 프로세스가 문자열을 선언하고 그 문자열의 주소를 B 프로세스에게 공유하면 ?
  • 프로세스는 각각의 가상 메모리를 가지고 있기 때문에 서로의 메모리 공간에 접근할 수 없다.
  • 하지만 ReadProcessMemory를 사용하면 다른 프로세스의 가상 메모리를 읽을 수 있다.

 

#include <iostream>
#include <Windows.h>

using std::cin;
using std::cout;
using std::endl;

int main()
{
	char charPtr[256];
	printf("%p", charPtr);
	cout << endl << GetCurrentProcessId() << endl;
	strcpy_s(charPtr, 256, "Hello World!");

	char charArr[256];

	int count = 5;
	while (count)
	{
		getchar();
		cin >> charArr;
		strcpy_s(charPtr, 256, charArr);
		count--;
	}
}
  • 읽혀질 파일

 

 

 

#include <iostream>
#include <Windows.h>

using namespace std;
int main()
{
    char* addr = (char*)0x010FFC24;
    
    HWND hwnd = FindWindow(0, L"C:\\Cpp\\CPP_Tistory\\[ecourse] Windows Programming\\ECOURSE\\Debug\\Chapter04.exe");
   // HWND hwnd = FindWindow(L"Chapter04", 0);
    cout << hwnd << endl;

    DWORD pid;

    GetWindowThreadProcessId(hwnd, &pid);

    HANDLE hProcess = OpenProcess(
        PROCESS_VM_READ,
        0,
        pid
    );

    char buff[256] = {};
    DWORD len = 0;


    if (hProcess)
    {
        int count = 5;

        
        while (count) {

            getchar();
            ReadProcessMemory(
                hProcess,
                addr,
                buff,
                256,
                &len
            );

      
        printf("%s", buff);


            count--;
        }
    }

    if (hProcess)
        CloseHandle(hProcess);
    
}
  • Chapter04-1.cpp (읽을 파일)
  • 시도 결과 Chapter04의 핸들값을 가져오고 ReadProcessMemory를 실행하는 곳 까진 성공했으나
  • 실제 문자열을 가져오진 못했다. 왜??

 

 

 

 

 

 


되는 케이스 (강의에 있는 예제 그대로)
#include <iostream>
#include <Windows.h>

using std::cin;
using std::cout;
using std::endl;

int main()
{
	DWORD pid = GetCurrentProcessId();

	char passwd[256] = { 0 };

	printf("PID : %d, buffer addr : %p\n", pid, passwd);

	while (1)
	{
		gets_s(passwd);
	}
}
  • 읽히는 쪽

 

 

 

#include <iostream>
#include <Windows.h>

using namespace std;
int main()
{
    char buff[256] = { 0 };

    DWORD pid = 2648;
    char* addr = (char*)0x004FF744;

    HANDLE hProcess = OpenProcess(PROCESS_VM_READ,
        0, pid);

    while (1)
    {
        getchar();
        DWORD len;

        ReadProcessMemory(hProcess, addr, buff, 256, &len);

        printf("읽어온 메모리 : %s\n", buff);
    }
    
}
  • 읽는 쪽

 

 

 

되는건 왜 되고 안되는건 왜 안될까?

문자열을 버퍼에 집어넣을 때 버퍼 하나를 거쳐서 strcpy_s로 넣은 것과 gets_s로 넣은 것의 차이가 아닐까 의심되어서, 되는 예제에 gets_s가 아니라 strcpy_s로 집어넣었다.
그랬더니 안된다...! 이걸 기뻐해야 할지..ㄷㄷ

됐다..!

  • 반대로, 안되던 예제에 읽히는 쪽을 gets_s로 바꾸니까 됐다. 근데.....왜 됐지?
  • 왜 됐는지에 대한 해답은 빠른 시일 내에 업데이트하도록 하겠다..

 

 

 

 

 


수정한 기존 코드
#include <iostream>
#include <Windows.h>

using std::cin;
using std::cout;
using std::endl;

int main()
{
	char charPtr[256];
	printf("%p", charPtr);
	cout << endl << GetCurrentProcessId() << endl;
	strcpy_s(charPtr, 256, "Hello World!");

	char charArr[256];

	int count = 5;
	while (count)
	{
		gets_s(charPtr);
		count--;
	}
}
  • 읽히는 쪽

 

 

 

#include <iostream>
#include <Windows.h>

using namespace std;
int main()
{
    char* addr = (char*)0x0039F758;

    //HWND hwnd = FindWindow(0, L"C:\\Cpp\\CPP_Tistory\\[ecourse] Windows Programming\\ECOURSE\\Debug\\Chapter04.exe");
    //// HWND hwnd = FindWindow(L"Chapter04", 0);
    //cout << hwnd << endl;

    DWORD pid = 17172;

    //GetWindowThreadProcessId(hwnd, &pid);

    HANDLE hProcess = OpenProcess(
        PROCESS_VM_READ,
        0,
        pid
    );

    char buff[256] = {};
    DWORD len = 0;


    if (hProcess)
    {
        int count = 5;


        while (count) {

            getchar();
            ReadProcessMemory(
                hProcess,
                addr,
                buff,
                256,
                &len
            );


            printf("%s", buff);


            count--;
        }
    }

    if (hProcess)
        CloseHandle(hProcess);

}
  • 읽는 쪽

'운영체제 > [ecourse] Windows Programming' 카테고리의 다른 글

5-2. Virtual Memory Allocation  (0) 2022.09.18
5-1. Virtual Address Space  (1) 2022.09.16
4-1. Kernel Object  (0) 2022.09.13
3-3. 가상 주소 공간과 DLL  (0) 2022.09.12
3-2 Dynamic Library  (0) 2022.09.11

댓글