0. 목차
Contents
1. APC Injection이란
- Remote DLL Injection과 유사하지만,
CreateRemoteThread()
를 사용하는 대신비동기 프로시저 호출(APC, Asynchronous Procedure Calls)
을 사용해 타겟 프로세스의 스레드가 악성 DLL을 로드하도록 한다.
비동기 프로시저 호출 (APC)
- APC함수는 일반적으로 사용하는 parameter를 전달받아 바로 호출하는
SPC(Synchronous Procedure Calls)
함수와 달리 특정 스레드의 콘텍스트에서 비동기적으로 실행되는 함수이다.
- 각 스레드는 타겟 스레드가
알람 가능한 상태(Alertable State)
가 될 때 실행되는APC Queue
를 포함하며QueueUserAPC()
를 통해 유저 정의 코드를 해당 스레드의APC Queue
에 넣는다.
APC Queue
- 쓰레드가
알람 가능한 상태(Alertable State)
가 되었을 때 호출할 콜백 함수들을 모아둔Queue
이다.
- 쓰레드가
Alertable State란?
스레드에는 생성되어 없어질 때까지 다음과 같이 자신의 상태를 가지고 있다.
Waiting
- thread가 특정 event를 받기 이전까지 아무것도 하지 않는 상태
- CPU scheduling 대상에서 해당 thread는 제외된다.
Ready
- CPU scheduling 대상이긴 하나 아직 CPU time을 받지 못한 상태
- 이 경우 CPU time만 받으면 바로 실행 상태(Running)로 변경된다.
Running
- CPU가 현재 이 thread를 수행하고 있는 상태
위 3가지의 상태 외에 다른 상태가 존재하는 데 바로 Alertable State
이다.
만약 Waiting
상태에서 event를 기다리는 thread는 특정 event가 발생하면 Ready
상태로 변경해주고 Scheduler에 의해 CPU Time을 받게 되면 Running
상태로 진입하게 된다.
하지만, Alertable State
는 사용자가 조정할 수 있는 유일한 thread의 상태로 다른 작업을 진행하다가 특정 작업이 완료되면 다른 작업들을 제처두고 완료된 작업을 처리하는 함수(CR, Completion Routine
)의 우선순위를 높여줄 수 있는 상태이다. 이를 통해 사용자가 유연하게 소프트웨어를 디자인할 수 있게 도와준다.
(즉, Alertable State
가 되어야 CR함수가 실행될 수 있다.)
앞서 말한 APC Queue
에 있는 함수는 쓰레드가 Alertable State
가 되면 큐에 있는 함수들이 호출된다.
다음 함수는 Thread의 상태를 Alertable State
로 변경할 수 있는 함수들이다.
SleepEx()
SignalObjectAndWait()
MsgWaitForMultipleObjectsEx()
WaitForMultipleObjectsEx()
WaitForSingleObjectEx()
Thread가 Alertable
상태로 진입하게 되면 APC Queue
에 Queueing된 정보가 있는지 확인을 하게 된다. (Queueing된 정보란 QueueUserAPC()
에 의해서 전달된 정보를 만한다.)
QueueUserAPC
pfnAPC
- APC procedure의 function pointer
hThread
- 스레드에 대한 핸들 값이며 핸들에는 THREAD_SET_CONTEXT 액세스 권한이 있어야 한다.
- 사용자가 정의한 APC procedure를 어떤 thread가 호출할 것인지
dwData
- pfnAPC가 가리키는 function에 넘겨주는 parameter 값
앞서 말한 내용들을 정리해보자
(참고로, 모든 스레드들은 자신만의 APC Queue를 가진다.)① QueueUserAPC()
를 통해서 특정 함수를 APC Queue
에 넣는다.
② 다른 로직들을 처리한다.
③ Thread를 알람 가능한 상태(Alertable State
)로 만들어 준다.
④ APC Queue
에 있던 함수들이 다 호출된다.
2. 동작 과정
APC Injection
기술이 동작하는 방법은 악성코드 프로세스가 알람 가능한 상태에 있거나 알람 가능한 상태로 전환될 것 같은 타겟 프로세스의 스레드를 식별한다. 그러면 유저 정의 코드를 타겟 스레드의 APC Queue
에 QueueUserAPC()
를 통해 저장한다. 만일, 타겟 스레드가 알람 가능한 상태에 들어가면 유저 정의 코드가 APC Queue
에서 선택되어 타겟 프로세스의 스레드에 의해 실행될 것이다.
① OpenProcess()를 통해 타겟 프로세스의 핸들을 구한다.
② VirtualAllocEx()를 통해 타겟 프로세스의 공간을 할당받는다.(인젝션할 위치)
③ WriteProcessMemory()를 통해 데이터를 작성한다.(쉘 코드, dll 등...)
④ OpenThread()를 통해 타겟 프로세스읲 스레드 핸들을 구한다.
⑤ QueueUserAPC()를 통해 타겟 스레드에 실행할 APC 함수를 큐잉한다.
이후에, 스레드가 알람 가능한 상태로 들어가면 스레드는 APC 큐에서 APC함수가 실행될 것이다.
3. APC DLL Injection 디버깅
다음 코드를 컴파일하여 디버깅 해보자
추가로 인젝션할 dll도 만들어 주자
msg.dll - 인젝션되는 DLL
#include <Windows.h>
void main() {
MessageBoxA(NULL, "Hello!", "Pwned", NULL);
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call){
case DLL_PROCESS_ATTACH:
main();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
- 타겟 프로세스에 해당 dll이 로드되면 메시지 박스를 띄우는 코드이다.
- 코드 흐름은 간단하게 3부분으로 나뉜다.
- 먼저
FindProcess()
를 이용하여(사용자 정의 함수임) 타겟 프로세스를 찾고OpenProcess()
를 통해 핸들 값을 가져온다.
- 그 다음 인젝션할
dll path
값이 들어있는 buffer를VirtualAllocEx()
와WriteProcessMemory()
의 파라미터로 줘서 타겟 프로세스에dll path
값을 저장한다.
- 마지막으로
GetProcAddress()
를 통해 얻을 수 있는kernel32.dll
에 있는LoadLibraryW()
의 주소 값과OpenThread()
를 통해 얻을 수 있는 타겟 프로세스의 핸들 값을QueueUserAPC()
의 파라미터로 넘겨준다.
- 이를 통해 타겟 스레드의
APC Queue
에LoadLibraryW()
와 파라미터인dll path
값이 전달되었으며 타겟 스레드의 상태가Alertable
상태로 변하는 즉시, dll이 로드되어dllMain
에 있는 메시지 박스가 호출될 것이다.
다음은 디버깅 화면을 통해 동작 과정을 살펴보자
APCInjector.exe
프로세스에서 타겟 프로세스인sublime_text.exe
에 메모리 할당 후WriteProcessMemoy()
를 통해 할당받은 영역에msg.dll
path값을 쓴다.
sublime_text.exe
프로세스에서 아까 할당받은 영역을 확인해보면 값이 잘 들어간 것을 확인
- 이제 타겟 프로세스의 스레드 핸들 값을 가져온다. →
0xB0
ProcessExplorer
를 통해 확인
QueueUserAPC()
를 호출하기 전에 파라미터 값을 세팅해준다.
- 파라미터에는 앞서 1.
GetProcAddress()
를 통해 구한LoadLibraryW()
의 주소 값과 2. 타겟 프로세스의 스레드 핸들 값, 3. 마지막으로 아까 타겟 프로세스에 작성한msg.dll
path값이 위치한 주소를 준다.
- 실행시켜주면 타겟 스레드의
APC Queue
에LoadLibraryW("msg.dll path")
가 들어가고 로드되었는지 바로MessageBox
가 뜬다.
- 실제로
sublime_text.exe
에는msg.dll
이 로드된 것을 확인할 수 있다.
위에서는 QueueUserAPC()
를 실행시키자마자 바로 뜬 것을 통해 sublime_text.exe
의 타겟 스레드가 Alertable State
로 전환하는 로직이 있거나 아니면 타겟 스레드가 Alertable State
인 상황인 것 같다.
다시 한 번 APCInjector
를 실행시켜서 확인을 해보자.
- QueueUserAPC()에게 전달하는 타겟 스레드 핸들 값 :
0x98
- 타겟 스레드 ID :
15580
sublime_text.exe
의 스레드 중에TID
값이 15580인 스레드를 확인해보면UserRequest
상태이다.
UserRequest
상태가 무엇인지 몰라서 찾아보니...
두둥...
앞서 설명한 스레드가 Alertable State
로 전환할 수 있게 도와주는 함수(WaitForSingleObjectEx
, WaitForMultipleObjectsEx
, ...)를 사용하여 타겟 스레드가 Alertable State
인 상황인 것을 알 수 있다.
끗.
Uploaded by Notion2Tistory v1.1.0