한동안 학업과 캡스톤 프로젝트 등 여러 일이 겹치면서 개발을 잠깐 멈췄었다.
특히 이번 학기에는 전공 수업에 팀 프로젝트까지 겹쳐서, 개인 프로젝트까지 병행하긴 쉽지 않았던 것 같다.
이제 종강을 했으니, 다시 조금씩 개발을 재개해보려 한다.
근데 오랜만에 이전에 작성했던 소스코드를 열어보니까
전체적인 코드 구조가 머릿속에 잘 들어오질 않더라.
그래서 본격적으로 개발을 다시 시작하기 전에,
일단 지금까지 작성해놓은 코드랑 파일 구조부터 정리하고 넘어가려고 한다.
현재 프로젝트 이름은 RealtimeMemTracker이고,
구조는 아래처럼 단순하게 되어있다:
RealtimeMemTracker
|
|---header/
| |---memory_tracker.h
| |---process_monitor.h
| |---ptrace_process.h
|
|---src/
|---memory_tracker.c
|---process_monitor.c
|---ptrace_process.c
아직 진행을 많이 하진 않아서 파일 수도 적고,
기능도 전부 구현된 건 아니지만,
다시 시작하기 전에 현재 상태를 명확히 정리해두는 게 좋을 것 같아서 이렇게 정리해본다.
달려있는 주석은 전부 영어인데, VM에서 한글지원이 안돼서 일단 영어로 주석을 달았다.
memory_tracker.c
#include <time.h>
typedef struct syscall_event
{
struct timespec timestamp;
int syscall_num;
char syscall_name[6];
size_t cur_vmsize; //current virtual memory comsumed
size_t cur_vmrss; //current real memory comsumed
size_t cur_vmdata; //heap area
size_t cur_mapped; //area that mapping by mmap
}SYSCALL_EVENT
void detect_memory_systemcall(int syscall_num, struct user_pt_regs *regs)
{
if(syscall_num == 214)
{
printf("[Detected]: brk\n");
}
else if(syscall_num == 222)
{
printf("[Detected]: mmap\n");
}
else if(syscall_num == 215)
{
printf("[Detected]: munmap\n");
}
}
이 파일은 시스템 콜 이벤트를 추적하기 위한 구조체 선언과,
그 중에서도 특정 시스템 콜만 필터링해서 감지하는 역할을 한다.
먼저 SYSCALL_EVENT라는 구조체가 정의되어 있고,
여기에는 다음과 같은 정보들이 담긴다:
- 시스템 콜 발생 시각 (timestamp)
- 시스템 콜 넘버 (syscall_num)
- 시스템 콜 이름 (syscall_name)
- 현재 프로세스의 메모리 사용 정보들:
- 가상 메모리 크기 (cur_vmsize)
- 실제 메모리 사용량 (cur_vmrss)
- 힙 영역 크기 (cur_vmdata)
- mmap 영역 크기 (cur_mapped)
그 아래 detect_memory_systemcall() 함수는 시스템 콜 넘버를 받아서,
특정 시스템 콜에 해당하는 경우만 감지해서 로그를 찍어준다.
현재 감지 대상으로 삼는 시스템 콜은 총 세 가지다:
- brk: 시스템 콜 번호 214
- munmap: 215
- mmap: 222
따라서 이 프로그램에서는 이 세 가지 시스템 콜만 필터링해서 추적하면 된다.
(다른 시스템 콜은 무시)
process_monitor.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <dirent.h>
#include <sys/types.h>
#include <unistd.h>
void monitor_all_process()
{
DIR *dir = opendir("/proc");
if(dir == NULL)
{
perror("opendir error");
exit(1);
}
struct dirent *all_process;
while((all_process = readdir(dir)) != NULL)
{
if(all_process->d_type == DT_DIR)
{
pid_t pid = atoi(all_process->d_name);
if(pid > 0)
{
printf("[Monitor] PID : %d\n", pid);
}
}
}
closedir(dir);
}
int main()
{
while(1)
{
system("clear");
monitor_all_process();
sleep(1);
}
return 0;
}
이 파일은 /proc 디렉토리를 열어서,
현재 실행 중인 모든 프로세스를 모니터링하는 역할을 한다.
monitor_all_process() 함수는 /proc 디렉토리 안에 있는 항목들을 하나씩 읽어오고,
그 중에서 디렉토리 타입(DT_DIR)이고 숫자로 된 이름(즉, 프로세스 ID)을 갖는 항목만 골라서 출력해준다.
즉, 현재 시스템에서 실행 중인 프로세스들의 PID를 1초 간격으로 계속 출력하는 기능이다.
main() 함수는 무한 루프 안에서 system("clear")를 이용해 화면을 지우고,
monitor_all_process()를 호출해서 실행 중인 프로세스 목록을 출력한다.
그 뒤 1초를 쉬는 방식으로 동작한다.
- /proc 디렉토리를 열고,
- 디렉토리 이름이 숫자인 것들만 추려서,
- 실행 중인 프로세스들의 PID를 실시간으로 출력해주는 용도다.
ptrace_process.c
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/user.h>
#include <linux/elf.h> //basic binary format in Linux
#include <string.h>
#include <sys/uio.h> //for - ptrace(PTRACE_GETREGSET) call, iovec definition
#include <asm/ptrace.h> //for - struct user_pt_regs
int status; //process status
void ptrace_attach_process(pid_t pid)
{
ptrace(PTRACE_ATTACH, pid, NULL, NULL); //chage this pid to trace mode
waitpid(pid, &status, 0); //wait till child process stop
printf("Attach to PID: %d\n", pid);
}
void get_syscall(pid_t pid)
{
struct user_pt_regs regs; //structure that register status when SystemCall ocurred
//basically SystemCall number stored to Reg[8] in arm64 Linux
struct iovec io = { //from "sys/uio.h"
.iov_base = ®s, //register pointer
.iov_len = sizeof(regs) //register size
};
ptrace(PTRACE_GETREGSET, pid, (void *)NT_PRSTATUS, &io); //NT_PRSTATUS: select register set
//copy process's register set to io
printf("Current system call number: %llu x0: %llu, x1: %llu\n", regs.regs[8], regs.regs[0], regs.regs[1]); //based on aarch64(arm64)
}
void ptrace_systemcall(pid_t pid)
{
while(WIFSTOPPED(status)) //if this process stopped because of ptrace, WIFSTOPPED(status) returns TRUE
{
ptrace(PTRACE_SYSCALL, pid, NULL, NULL); //stop when enter the SystemCall
waitpid(pid, &status, 0); //
if(WIFEXITED(status))
break;
get_syscall(pid);
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
waitpid(pid, &status, 0);
if(WIFEXITED(status))
break;
}
}
int main()
{
pid_t pid = fork();
if(pid == 0)
{
int *p = (int *)malloc(sizeof(int));
*p = 32;
printf("child\n");
}
else
{
ptrace_attach_process(pid);
ptrace_systemcall(pid);
printf("parent\n");
}
return 0;
}
ptrace_attach_process(pid_t pid)
지정한 프로세스(PID)에 PTRACE_ATTACH를 걸어서 trace모드로 전환한다.
그 후, waitpid()로 자식이 stop상태로 전환될 때까지 대기한다.
get_syscall(pid_t pid)
PTRACE_GETREGSET을 통해 해당 프로세스의 레지스터 상태를 가져옴
ARM64에서는 시스템 콜 넘버가 reg[8]에 들어있다.
ptrace_systemcall(pid_t pid)
ptrace(PTRACE_SYSCALL)을 통해 해당 프로세스가 시스템 콜을 호출할 때마다 멈추게 하고,
그때마다 레지스터를 읽어서 시스템 콜 정보를 출력하는 구조.
PTRACE_ATTACH 로 외부 실행 프로세스 감시하려고 잡아두고,
PTRACE_SYSCALL로 잡아둔 프로세스의 syscall 진입을 감시한다.
'C프로그래밍 > MemoryTracker' 카테고리의 다른 글
| RealtimeMemTracker - 4, ARM64 Linux에서 PTRACE_SYSCALL을 사용한 시스템 콜 트레이싱 (5) | 2025.03.25 |
|---|---|
| RealtimeMemTracker - 3, 시스템콜 번호 확인하기 (1) | 2025.03.23 |
| RealtimeMemTracker - 2, 현재 실행중인 프로세스 확인 (0) | 2025.03.23 |
| RealtimeMemTracker - 1, 다른 프로세스의 SystemCall 추적하기 (0) | 2025.03.20 |