티스토리 뷰
1.프로세스 정보 얻어오기
2.CreateToolhelp32Snapshot
3.Handle에 대한 설명
4.processentry32 구조체 명세
5.CreateToolhelp 함수 설명
6.ntdll.dll의 정의, 역할
7.OpenProcess
8.ReadProcessMemory
9.해석안되는 문자표
10.SuspendProcess / ResumeProcess
Q.질문
1.프로세스 정보 얻어오기
프로세스들의 정보를 순간적으로 가져오는 Snapshot과 정보를 추출, 그 정보를 얻어오는 방법?
1. 프로세스의 나열.
1) 먼저 프로세스 정보를 얻기위한 핸들을 생성한다.
이를 사용하기 위해선 헤더 파일을 포함한다.
HANDLE hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
2) 프로세스 정보 나열을 위해 Process32First(), Process32Next() 함수를 사용한다.
PROCESSENTRY32 구조체에는 프로세스에 관한 다양한 정보를 담고있다.
PROCESSENTRY32 entry;
Process32First( hSnap , &en );
do {
.... // 정보를 적절히 사용.
} while( Process32Next( hSnap , &en ) );
3) 사용을 끝내고 핸들은 꼭 닫아주자.
CloseHandle( hSnap );
2. 프로세스가 사용하는 메모리 얻기.
1) PROCESSENTRY32 구조체 멤버중 프로세스ID 를 가지고 있는
th32ProcessID 멤버변수를 사용해 포로세스의 핸들을 얻는다.
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, th32ProcessID );
2) 포로세스 핸들을 구하고, ReadProcessMemory(), WriteProcessMemory() 함수를
사용하여 메모리를 access 할 수 있다.
3) 핸들은 꼭 닫자.. ^^;
2.CreateToolhelp32Snapshot : 현재 프로세스 캡쳐
CreateToolhelp32Snapshot : 현재 프로세스 캡쳐
Process32First 로 시작해 Process32Next 가 널을 반환할때까지 돌면서 핸들 얻음
Module32First 로 한프로세스에 첫번째 모듈 부터 Module32Next이 널을 반환 할때까지 순회하시면
전 프로세스에 인젝션된 모든 모듈 정보를 구하실수 있구요. 다음 모듈의 헨들을 구하시고자 하신다면.
MODULEENTRY32 구조체에 hModule가 되겠습니다. 프로세스의 헨들을 구하시고자 한다면
WinfindProc 함수의 콜벡함수를 선언하셔서 여차저차 하시면 됩니다.
이부분이 궁금하시면 제가 예전에 질문 올렸었는데, 다른분이 답변하신게 있을겁니다. PID로 헨들을 구하는방법.... 아무튼 대략의 방법 이었습니다. 아...필요로하는 헤더파일은 tlhelp32.h 입니다.
3.Handle에 대한 설명
운영체제에 의해서 관리되는 장치나 이러한 장치들을 사용하기 위해서 필요한 정보들을 운영체제의 '리소스'라고 한다.
리소스는 보통의 경우 장치를 사용할 수 있게 도와주는 정보 혹은 해당 장치에 설정이 되어있는 상태 값을 의미하는 경우가 많다.
따라서, 사용자 단의 응용 프로그램이 컴퓨터의 어떤 장치를 사용하고 싶다면 해당 장치와 관련된 운영체제 리소스에 접근해서 원하는 작업을 수행하게 된다.
이러한 리소스는 메모리로 구성되어 있어 주소를 가지고 있다.
만약 응용프로그램에서 포인터를 사용하여 이 주소에 접근한다면 작업을 쉽게 완료할 수 있겠지만..
맘대로 건들다가 망가지기라도 하면 어떻게해?
그래서 막아놨다.
3.1.CloseHandle()
hObject - 사용을 끝내고, 시스템 메모리에서 해제할 개체를 입력합니다.
API 설명:
CreateFile, CreateMutex, CreateProcess 등의 API로부터 만들어진 핸들을 메모리에서 해제할 때 사용합니다.
이 함수로 메모리에서 해제할 핸들의 목록은 '참고' 단의 MSDN 페이지를 참고하시기 바랍니다.
할당한 핸들을 메모리에서 해제하지 않을 경우, 메모리 누수가 발생할 수 있으니,
사용이 끝난 핸들은 반드시 메모리에서 해제하여야 합니다.
4.processentry32 구조체 명세
프로세스들의 상태를 스냅한다.상태 정보를 담을 구조체를 선언한다.
프로세스 상태 정보를 담을 구조체를 선언합니다.
struct processentry 32 {
구조체의 크기 ,
프로세스 식별자,
프로세스에 의해서 시작된 실행 스레드의 수,
부모 프로세스의 식별자,
이 프로세스에 의해서 실행된 스레드의 기본 우선순위
프로세스의 실행 파일명
}
5.CreateToolhelp 함수 설명
DWORD dwFlags :: ================= 어떤 정보를 가지고 올거야?
//TH32CS_SNAPPROCESS 모든 프로세스를 스냅
TH32CS_INHERIT // 상속 가능한 모든것!
TH32CS_SNAPHEAPLIST //Heap 안에 있는 모든 것!
TH32CS_SNAPMODULE // 프로세스 모듈정보!
TH32CS_SNAPTHREAD // 프로세스 스래드
TH32CS_SNAPALL // 시스템에 있는 모든 프로세스, 모듈, 스레드!
TH32CS_SNAPHEAPLIST, TH32CS_SNAPMODULE, TH32CS_SNAPMODULE32, TH32CS_SNAPALL 이 4개의 옵션을 입력한 경우에만 th32ProcessID 매개 변수가 유효합니다.
그 외의 경우엔 th32ProcessID 매개 변수에 대개 0을 입력합니다. 또한, TH32CS_SNAPMODULE32 옵션의 경우 64비트 윈도우에서만 사용이 가능합니다.
DWORD th32ProcessID :: 프로세스 ID 스냅
스냅하고자 하는 process의 ID를 넣는다.
0을 매개변수로 전달 시, 현재 실행중인 모든 프로세스를 스냅한다.
6.
커널에게 리소스 자원 요청시 쓰이는 dll.
7.OpenProcess
프로세스를 직접 다루기 위해서는 Handle이 필요한데, 이를 위해서 호출할 함수는..OpenProcess
하지만 프로세스를 직접적으로 다루기 위해서는 HANDLE이 필요하다.
핸들을 얻기 위해 사용하는 함수
HANDLE OpenProcess(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwProcessID
);
DWORD dwDesiredAccess :: 반환되는 핸들의 성격을 결정짓는다.
// PROCESS_ALL_ACCESS //핸들에 모든 접근을 허용한다.
// PROCESS_QUERY_INFORMATION
// PROCESS_VM_READ
BOOL bInheritHandle :: //TRUE / FALSE로 상속 성격 지정
DWORD dwProcessID :: //원하는 Process의 ID (PID)를 입력한다.
두번째 인자는 PID로 접근한 object (process)를 현재 이 함수를 실행하고있는 프로세스에 상속할지 결정하는 인자.
8.ReadProcessMemory
지정된 프로세스의 메모리 영역에서 데이터를 읽는다.
읽을 전체 영역에 액세스 할 수 한다.그렇지 않으면 작업이 실패한다.
BOOL ReadProcessMemory(
HANDLE hProcess, 읽을 메모리가 있는 프로세스의 핸들. 핸들은 프로세스에 PROCESS_VM_READ 액세스 권한을 가져와야 한다 .
LPCVOID lpBaseAddress, 읽을 메모리 주소 , 권한 없으면 에러
LPVOID lpBuffer, 메모리에서 읽은 데이터를 저장할 버퍼의 위치
SIZE_T nSize,읽을 데이터 크기
SIZE_T *lpNumberOfBytesRead 읽힌 데이터 크기. 안써도 됨.
);
프로세스의 메모리를 읽을 경우에는 반드시 PROCESS_VM_READ 권한을 명시하여 연 프로세스의 핸들이 필요합니다.
만약 프로세스 핸들을 열었을 때 PROCESS_VM_READ 권한을 명시하지 않은 경우 이 API는 ERROR_ACCESS_DENIED 오류로 실패합니다.
읽으려는 프로세스의 메모리 주소는 반드시 읽을 수 있어야 합니다.
만약 0x00001000 ~ 0x00002000 주소를 읽으려는 경우 중간에 단 한 바이트라도 읽기 권한이 없을 경우 이 API는 ERROR_PARTIAL_COPY 오류 또는 기타 다른 오류로 실패합니다.
VB.NET에서는 포인터를 사용할 수 없으므로 Marshal 클래스를 이용한 모든 작업을 해야합니다.
9.해석안되는 문자표
sz NULL Terrninated NULL 종료 문자열
cb Count of Bytes 바이트수
dw doubleword 부호없는 long 형 징수
h handle 윈도우,비트업,파일 등의 핸들.
sz NULL Terrninated NULL 종료 문자열
ch Character 문자형
a Array 배열
w Word 부호 없는 정수형.
i Integer 정수형
p,lp long pointer 포인터형
b Bool 논리형
10.SuspendProcess / ResumeProcess
NtSuspendProcess : 프로세스를 일시정지시킨다.
NTSYSAPI
NTSTATUS
NTAPI
NtSuspendProcess(
IN HANDLE ProcessHandle);
ProcessHandle : 프로세스의 핸들.
주의할 점 : 성공했다 할지라도, SuspendCount가 0을 넘지 않으면 프로세스는 일시정지되지 않는다.
성공시, 0 이하의 값을 반환한다.
NtResumeProcess : 일시정지된 프로세스를 재개시킨다.
NTSYSAPI
NTSTATUS
NTAPI
NtResumeProcess(
IN HANDLE ProcessHandle);
ProcessHandle : 프로세스의 핸들.
성공시, 0 이하의 값을 반환한다.
Q.질문
NtSuspendProcess()는 리턴값으로 NT_STATUS 를 사용하므로... 성공하면 0 값을 리턴하게 됩니다.
커널 내부 자료구조에서 카운트를 유지하고 있어서 NtSuspendProcess()를 여러번 호출했다면 프로세스를 Resume 시키기
위해선 NtSuspendProcess()를 호출한 횟수 만큼 NtResumeProcess()를 호출해줘야 합니다.
그에 반해서...
NtResumeProcess()는 여러번 호출해도 상관 없습니다. NtSuspendProcess()를 호출한 수 만큼 NtResumeProcess()를
호출하면 프로세스가 Resume 상태가 되고, 그 후에 또 다시 NtResumeProcess()를 호출해도 카운트 되는 것은 없다는 거죠.
카운트 값은 커널 내부 자료구조에서 갖고 있으므로...
위 두개의 API를 이용해서 처리할 경우.. 타겟 프로세스 상태가 Suspend상태인지 아닌지를 알아내서 Suspend가 풀릴 때 까지
NtResumeProcess()를 계속 호출하는 구조로 로직을 구성해야 합니다.
타겟 프로세스의 Resume/Suspended 상태는 NtQuerySystemInformation()를 이용해서 알아 낼 수 있습니다.
어떤 프로세스의 SYSTEM_THREAD 라는 자료구조의 WaitReason 필드 값이 '5'이면 Suspended 상태 입니다.
SuspendThread()를 이용하는 것 보다 NtSuspendProcess()을 이용하는 게 유리한 것은...
하나의 프로세스는 여러개의 쓰레드를 가질 수 있고, 각기의 쓰레드를 개별적으로 Resume/Suspended 처리를 하게 되면
쓰레드 간의 종속관계로 인해서 문제가 발생할 수도 있기 때문 입니다. 잘못하면 데드락 상황이 발생할 수도 있고요.
NtQuerySystemInformation(), NtSuspendProcess(), NtResumeProcess() 등의 함수는 Undocumented API 이지만...
XP 이후부터 현재의 Windows 10 에 이르기 까지 계속 사용되고 있습니다. 커널 변경에 따라서 함수의 내부 Implementation이
바뀔 수는 있어도... Win32 레이어가 존재하는 한... 이 API들이 사라질 가능성은 ... 전혀 없다고 봐도 무방 하지요.
시스템 유틸리티 프로그램을 만들 때... 상당히 자주 사용되는 API이기도 합니다.(MS에서도 자신들이 만든 프로그램에서 자주 사용하기도 합니다.) Undocumented API라고 해서 꺼릴 필요는 없다는 겁니다.