Kernel to User land(APC 인젝션)

커널모드에서 유저 프로세스로 코드를 인젝션시키는 두가지 방법이 존재

1) Queue user APC to alertable thread
타겟 프로세스의 어느 스레드가 alertable state인지를 알수 있는 코드가 필요하다. 각 스레드 ETHREAD 구조체를 검사해야 한다. thread가 alertable인지 설정되는 flag가 존재한다. 
이 방법은 다소 신뢰할수 없고 MS가 해당 구조체를 무작위로 변경하기 쉽다는게 일반적 생각입니다.

2) Queue user APC to new thread
스레드가 시작하기 전에 스레드에 도달하는 방법이 필요합니다. 기본적으로 CreateThreadNotifyRoutine 등록함으로서 달성됩니다. 해당 콜백이 호출되면 두개의 시나리오중 하나가 발생될 수 있습니다.

2.1 스레드가 죽고 있음. 콜백이 죽는 스레드 컨텍스트에서 호출됨
2.2 스레드가 생성되고 있음.

CNTR 콜백이 스레드 생성시 호출된것을 확인한 후에, 해당 스레드가 관심 프로세스에서 생성중인지를 알아보기 위해 몇가지 검사를 수행할 수 있다. CNTR에서 실행코드에서 기억할 중요한 사항으로는 각각의 CNTR이 끝나기전까지는 새로운 스레드가 만들어질 수 없다는 것이다. 만약 CNTR 코드가 실행하는데 1분이 걸린다면, 스레드 생성이 병목현상이 생긴다. 무엇을 하든지간에 그것이 빠르다는 것을 확인해라.

생성 스레드에 APC를 큐잉하기 위해, 커널코드가 신규 스레드에 ETHREAD 구조체를 갖을 필요가 있다. Win7에서 ETHREAD 구조체 핸들을 얻기 위해 PsLookupThreadByThreadId 호출할 수 있다. XP에서 코드검사가 실패해서 동작하지 않습니다. XP에서는 이 시점에 ETHREAD 구조체 얻기에 실패하는것으로 생각됩니다. 왜냐하면 오브젝트 리스트에 완전히 만들어지지 않기 때문에

다음으로, 2개의 파라미터를 갖는 KeStackAttachProcess 사용해 프로세스 컨텍스트에 붙일 필요가 있다. 첫 파라미터는 EPROCESS 구조체 핸들이다. 두번째 파라미터는 KAPC 구조체로 할당된 메모리 포인터이다.(ExAllocatePool 사용)

붙인후 ZwAllocateVirtualMemory를 두번 사용해야 한다. APC 파라미터 메모리를 할당하기 위해 한번. 예를 들면, 당신의 APC가 타겟프로세스에 DLL을 인젝션할거라면, 인젝트할 DLL 이름에 대한 메모리를 할당할 필요가 있다. 그리고 LoadLibaryA 주소. 두번째 메모리 할당은 당신의 user APC code이다. 당신의 APC code 크기를 얻기 위해 다음과 같은 트릭을 사용할 수 있다.

VOID UserAPC(PVOID context, PVOID arg1, PVOID agr2)
{

}

void UserAPC_end(){}

ULONG CalcApcSize()
{
  return ((ULONG_PTR)UserAPC_end - (ULONG_PTR)UserAPC);
}

typedef struct _CONTEXT
{
 ULONGLONG LoadLibraryPtr;
 CHAR DllName[MAX_PATH];
} CONTEXT, *PCONTEXT;


댓글

이 블로그의 인기 게시물

[WinAPI] 모달리스 다이얼로그 설명

[WinDbg] Debugging a stack overflow

[WinDbg] first-chance, second-chance Exception