33. '스텔스' 프로세스
API코드 패치는 프로세스 메모리에 로딩된 라이브러리 이미지에서 후킹을원하는 API 코드 자체를 수정하는 방법API코드 패치는 프로세스 메모리에 로딩된 라이브러리 이미지에서 후킹을원하는 API 코드 자체를 수정하는 방법임
API 후킹에서 가장 널리 사용되는 방법 왤까.. 대부분의 API를 후킹할 수 있으니까
API 후킹과 프로세스 은닉
API 후킹의 기본
API 후킹은 시스템 API 호출을 가로채 동작을 변조하거나 데이터를 필터링하는 기술이다.
운영 체제는 API를 통해 시스템 정보를 제공하며, 후킹 기술을 사용하면 이러한 정보의 결과를 조작할 수 있다.
ZwQuerySystemInformation API 후킹
- ZwQuerySystemInformation API는 시스템의 프로세스 정보를 반환하는 데 사용되는 대표적인 API이다.
- 스텔스 기술은 이 API를 후킹하여 특정 프로세스를 목록에서 제거해 탐지를 우회한다.
후킹 과정
- API 호출 가로채기
- ZwQuerySystemInformation API의 원래 코드를 후킹하여 동작을 가로챈다
- 후킹된 코드는 호출자의 요청이 들어오면 프로세스 정보를 필터링한 후 반환한다
- 프로세스 필터링
- 시스템에서 반환된 프로세스 목록(연결 리스트 구조)을 탐색한다
- 은닉하려는 프로세스 이름(예: notepad.exe)을 찾고, 이를 연결 리스트에서 제거한다
- 결과 수정
- 수정된 연결 리스트를 반환하여 원래 요청자가 숨겨진 프로세스를 감지하지 못하게 한다
프로세스 은닉의 구현 목표
- 특정 프로세스가 탐지 도구(예: Task Manager, Process Explorer)에서 보이지 않도록 한다
- 보안 소프트웨어가 스텔스 기술이 적용된 프로세스를 탐지하지 못하게 한다
API 코드 패치 동작 원리
IAT 후킹은 프로세스의 특정 IAT 값을 조작해서 후킹하는 방식
먼저 대상 API 주소 찾음 -> GetModuleHandle와 GetProcAddress를 사용해 대상 DLL과 API의 메모리 주소를 얻는다.
FARPROC pfnOrg = GetProcAddress(GetModuleHandleA("ntdll.dll"), "ZwQuerySystemInformation");
실제 API함수의 엔트리 포인트..
그리고 원본 코드 백업함
: 패치하기 전에 API의 원래 코드를 안전한 메모리 공간에 백업 예시로 JMP 명령어 삽입 전 5바이트 백업
memcpy(pOrgBytes, pfnOrg, 5);
그러고 나서JMP 명령어 생성
- JMP 명령어는 대상 API의 원래 코드 대신 새로운 함수로 흐름을 변경한다.
- x86 구조에서는 다음과 같은 형식으로 JMP 명령어를 생성한다
E9 {점프할 상태 오프셋(목표주소-현재주소-5)} 이렇게..
BYTE pBuf[5] = {0xE9, 0, 0, 0, 0};
DWORD dwAddress = (DWORD)pfnNew - (DWORD)pfnOrg - 5;
memcpy(&pBuf[1], &dwAddress, 4);
JMP 명령어삽입
- 생성된 JMP 명령어를 API의 엔트리 포인트에 삽입한다.
- 메모리 보호를 해제하고(JMP 명령어 쓰기 가능하도록), 삽입한 후 다시 보호를 설정한다
VirtualProtect(pfnOrg, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy(pfnOrg, pBuf, 5);
VirtualProtect(pfnOrg, 5, dwOldProtect, &dwOldProtect);
새로운 함수 실행
- API 호출이 발생하면 원래 API 코드 대신 새로운 함수가 호출된다.
- 새로운 함수는 추가 작업(예: 특정 데이터 필터링)을 수행하거나, 원래 API를 호출한 후 결과를 변조하여 반환한다
NTSTATUS NewZwQuerySystemInformation(...) {
// 원래 API 호출
status = ((PFZWQUERYSYSTEMINFORMATION)pFunc)(...);
// 결과 변조
if (SystemInformationClass == SystemProcessInformation) {
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
while (pCur) {
// 특정 프로세스 필터링
}
}
return status;
}
API 코드 패치의 주요 예시
예: ZwQuerySystemInformation 패치
- 목표: ZwQuerySystemInformation의 결과에서 특정 프로세스 숨기기
- 동작:
- 원래 API를 호출하여 프로세스 목록을 가져옴
- 연결 리스트 형태의 데이터 구조에서 숨기고자 하는 프로세스 이름을 찾아 필터링
- 수정된 결과를 호출자에게 반환
원래 코드 복구하는거..
VirtualProtect(pfnOrg, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy(pfnOrg, pOrgBytes, 5);
VirtualProtect(pfnOrg, 5, dwOldProtect, &dwOldProtect);
장점은 API 동작 제어 가능하고 탐지 우회하거나 원하는 작업하기에 편하다는거..
DLL 주입과 프로세스 은닉
DLL 주입 개요
DLL 주입은 특정 프로세스에 외부 DLL 파일을 삽입하여 코드 실행 흐름을 조작하는 기술이다
주입 방식
- CreateRemoteThread API 사용
- 대상 프로세스의 메모리에 DLL을 로드하고, 새로운 스레드를 생성하여 DLL의 코드를 실행한다
- 레지스트리 기반 주입
- AppInit_DLLs 레지스트리 키를 수정하여 특정 DLL을 모든 프로세스에 로드하도록 설정한다
stealth.dll의 역할
- stealth.dll은 대상 프로세스에 주입된 후 동작하며, 프로세스를 은닉하는 데 사용된다
- DLL 내부에는 은닉할 프로세스 이름이 공유 메모리 섹션에 저장되어 후킹된 API에서 참조된다
- 사용 예시
- notepad.exe, procexp.exe 등 은닉하고자 하는 프로세스를 설정
NewZwQuerySystemInformation 함수
동작 원리
- 기존 ZwQuerySystemInformation API의 동작을 수정하여 은닉하려는 프로세스를 필터링한다.
- 연결 리스트 형태로 관리되는 프로세스 목록을 탐색하며, 숨기고자 하는 프로세스를 찾아 리스트에서 제거한다.
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
while(TRUE) {
if(!_tcsicmp((PWSTR)pCur->Reserved2[1], g_szProcName)) {
pPrev->NextEntryOffset += pCur->NextEntryOffset;
}
if(pCur->NextEntryOffset == 0) break;
pPrev = pCur;
pCur = (PSYSTEM_PROCESS_INFORMATION)((ULONG)pCur + pCur->NextEntryOffset);
}
결과적으로 호출자에게 반환된 목록에는 은닉된 프로세스가 포함되지 않음
스텔스 기능 실행 절차..
HideProc.exe실행.. 여기서 은닉 명령어 전달한다.
HideProc.exe -hide notepad.exe stealth.dll
하고나서 stealth.dll이 주입된 후 API를 후킹해서 프로세스 은닉 기능 키고 수정된 프로세스 목록반환돼서 탐지도구에서 안보이게 된다..