10가지 프로세스 인젝션 기법: 일반적으로 사용되며 최근 주목받는 프로세스 인젝션 기법에 대한 기술적 조사
편집자 주: Elastic은 2019년 10월 Endgame과 협력하게 되었으며, Endgame 블로그 콘텐츠 일부를 elastic.co로 이전했습니다. 통합 보안 솔루션에 관한 자세한 내용은 Elastic Security를 참조하세요
프로세스 인젝션은 멀웨어 및 파일리스 공격자의 공격 기법에서 자주 사용되는 광범위한 방어 회피 기법으로, 다른 프로세스의 주소 공간 내에서 사용자 지정 코드를 실행하는 것을 말합니다. 프로세스 인젝션은 은밀성을 높이며, 일부 기법은 지속성도 확보합니다. 다양한 프로세스 인젝션 기법이 있지만, 이 글에서는 다른 프로세스가 멀웨어 코드를 실행하도록 하는 실제 공격에서 관찰된 10가지 기법을 소개합니다. 또한 이러한 일반적인 기법을 탐지하고 방어하는 데 도움이 되도록, 여러 기법의 스크린샷을 제공하여 리버스 엔지니어링과 멀웨어 분석을 지원합니다.
1. CreateRemoteThread 및 LoadLibrary를 통한 기존 DLL 인젝션
이 기법은 멀웨어를 다른 프로세스에 인젝션하는 데 사용되는 가장 일반적인 기법 중 하나입니다. 멀웨어는 악성 동적 링크 라이브러리(DLL)의 경로를 다른 프로세스의 가상 주소 공간에 기록한 후, 대상 프로세스에 원격 스레드를 생성하여 원격 프로세스가 해당 DLL을 로드하도록 합니다.

멀웨어는 먼저 인젝션 대상 프로세스(예: svchost.exe)를 찾아야 합니다. 일반적으로 CreateToolhelp32Snapshot, Process32First 및 Process32Next라는 세 가지 애플리케이션 프로그래밍 인터페이스(API)를 호출해 프로세스를 검색합니다. CreateToolhelp32Snapshot은 지정된 프로세스 또는 모든 프로세스의 힙이나 모듈 상태를 열거하고 스냅샷을 반환하는 API입니다. Process32First는 스냅샷에서 첫 번째 프로세스의 정보를 가져오며, 이후 Process32Next를 루프에서 사용하여 각 프로세스를 순회합니다. 대상 프로세스를 찾으면 멀웨어는 OpenProcess를 호출하여 대상 프로세스의 핸들을 가져옵니다.
그림 1에서 볼 수 있듯이 멀웨어는 DLL 경로를 기록할 공간을 확보하기 위해 VirtualAllocEx를 호출합니다. 이후 WriteProcessMemory를 호출하여 할당된 메모리에 경로를 기록합니다. 마지막으로 다른 프로세스에서 코드가 실행되도록 CreateRemoteThread, NtCreateThreadEx 또는 RtlCreateUserThread와 같은 API를 호출합니다. 후자의 두 API는 문서화되어 있지 않습니다. 하지만 일반적인 개념은 LoadLibrary의 주소를 이러한 API 중 하나에 전달하여 원격 프로세스가 멀웨어를 대신해 DLL을 실행하도록 하는 것입니다.
CreateRemoteThread는 여러 보안 제품에서 추적되고 플래그 지정됩니다. 또한 디스크에 악성 DLL이 있어야 하므로 탐지될 수 있습니다. 공격자가 방어를 회피하기 위해 코드를 인젝션하는 경우가 가장 많다는 점을 고려하면, 정교한 공격자는 이 방식을 사용하지 않을 가능성이 높습니다. 아래 스크린샷은 Rebhip이라는 멀웨어가 이 기법을 수행하는 모습을 보여 줍니다.

Sha256: SHA256:07b8f25e7b536f5b6f686c12d04edc37e11347c8acd5c53f98a174723078c365
2. PE(Portable Executable) 인젝션
멀웨어는 LoadLibrary의 주소를 전달하는 대신, 기존에 열려 있는 프로세스에 악성 코드를 복사하고 이를 실행하도록 할 수 있습니다. 작은 셸코드를 사용하거나 CreateRemoteThread를 호출하는 방식이 이에 해당합니다. LoadLibrary 기법과 비교한 PE 인젝션의 한 가지 장점은 멀웨어가 디스크에 악성 DLL을 드롭할 필요가 없다는 점입니다. 첫 번째 기법과 마찬가지로 멀웨어는 호스트 프로세스에서 메모리를 할당하고(예: VirtualAllocEx), DLL 경로를 기록하는 대신 WriteProcessMemory를 호출하여 악성 코드를 기록합니다.하지만 이 방식에는 복사한 이미지의 기준 주소가 변경된다는 문제가 있습니다. 멀웨어가 PE를 다른 프로세스에 인젝션하면 예측할 수 없는 새 기준 주소를 갖게 되므로, PE의 고정 주소를 동적으로 다시 계산해야 합니다. 이를 해결하려면 멀웨어는 호스트 프로세스에서 재배치 테이블 주소를 찾고, 재배치 디스크립터를 순회하여 복사된 이미지의 절대 주소를 확인해야 합니다.

이 기법은 파일을 디스크에 드롭하지 않는다는 점에서 리플렉티브 DLL 인젝션 및 메모리 모듈과 같은 다른 기법과 유사합니다. 하지만 메모리 모듈과 리플렉티브 DLL 인젝션 방식은 더욱 은밀합니다. 메모리 내에서 자체적으로 로드 및 실행되므로 추가 Windows API(예: CreateRemoteThread 또는 LoadLibrary)에 의존하지 않습니다. 리플렉티브 DLL 인젝션은 Windows 로더에 의존하는 대신, 실행 시 자체적으로 메모리에 매핑되는 DLL을 생성하는 방식입니다. 메모리 모듈은 DLL이 스스로를 매핑하는 대신 인젝터 또는 로더가 대상 DLL을 메모리에 매핑한다는 점에서 리플렉티브 DLL 인젝션과 유사합니다. 이전 블로그 글에서는 이 두 가지 메모리 내 방식에 대해 자세히 다뤘습니다.
PE 인젝션을 분석할 때는 CreateRemoteThread 호출 전에 루프를 발견하는 경우가 매우 많습니다. 일반적으로 하나의 'for' 루프 안에 다른 'for' 루프가 중첩되어 있습니다. 이 기법은 크립터, 즉 멀웨어를 암호화하고 난독화하는 소프트웨어에서 매우 널리 사용됩니다. 그림 2에서 샘플 단위 테스트는 이 기법을 활용합니다. 코드에는 WriteProcessMemory 및 CreateRemoteThread 호출 전에 확인할 수 있는, 재배치 테이블을 조정하기 위한 두 개의 중첩 루프가 있습니다. 'and 0x0fff' 명령도 좋은 지표입니다. 이는 처음 12비트를 사용하여 포함된 재배치 블록의 가상 주소에 대한 오프셋을 가져온다는 것을 보여 줍니다. 이제 멀웨어는 필요한 모든 주소를 다시 계산했으므로, 시작 주소를 CreateRemoteThread에 전달하고 실행하기만 하면 됩니다.

Sha256: ce8d7590182db2e51372a4a04d6a0927a65b2640739f9ec01cfd6c143b1110da
3. 프로세스 할로잉(프로세스 교체 및 RunPE라고도 함)
DLL 인젝션처럼 호스트 프로그램에 코드를 인젝션하는 대신, 멀웨어는 프로세스 할로잉이라는 기법을 수행할 수 있습니다. 프로세스 할로잉은 멀웨어가 대상 프로세스의 메모리에서 정상 코드를 매핑 해제하여 비우고, 대상 프로세스(예: svchost.exe)의 메모리 공간을 악성 실행 파일로 덮어쓰는 방식입니다.

그다음 멀웨어는 정상 파일의 콘텐츠를 악성 페이로드로 교체해야 합니다. ZwUnmapViewOfSection 또는 NtUnmapViewOfSection을 호출하여 대상 프로세스의 메모리를 매핑 해제함으로써 이 작업을 수행합니다. 이 두 API는 기본적으로 섹션이 가리키는 모든 메모리를 해제합니다. 메모리가 매핑 해제되면 로더는 VirtualAllocEx를 실행하여 멀웨어에 새 메모리를 할당하고, WriteProcessMemory를 사용하여 멀웨어의 각 섹션을 대상 프로세스 공간에 기록합니다. 멀웨어는 SetThreadContext를 호출하여 진입점을 새로 기록한 코드 섹션으로 지정합니다. 마지막으로 ResumeThread를 호출하여 일시 중단된 스레드를 재개하고 프로세스의 일시 중단 상태를 해제합니다.

Sha256: eae72d803bf67df22526f50fc7ab84d838efb2865c27aef1a61592b1c520d144
4. 스레드 실행 하이재킹(일시 중단, 인젝션 및 재개(SIR)라고도 함)
이 기법은 앞서 설명한 프로세스 할로잉 기법과 일부 유사점이 있습니다. 스레드 실행 하이재킹에서 멀웨어는 프로세스의 기존 스레드를 대상으로 하며, 눈에 띄는 프로세스 또는 스레드 생성 작업을 피합니다. 따라서 분석 중에는 CreateToolhelp32Snapshot 및 Thread32First 호출 뒤에 OpenThread 호출이 이어지는 것을 볼 가능성이 높습니다.

대상 스레드의 핸들을 가져온 후 멀웨어는 인젝션을 수행하기 위해 SuspendThread를 호출하여 스레드를 일시 중단 모드로 전환합니다. 이후 VirtualAllocEx 및 WriteProcessMemory를 호출하여 메모리를 할당하고 코드를 인젝션합니다. 이 코드에는 셸코드, 악성 DLL의 경로 및 LoadLibrary의 주소가 포함될 수 있습니다.
그림 4는 이 기법을 사용하는 일반적인 트로이 목마를 보여 줍니다. 멀웨어는 스레드 실행을 하이재킹하기 위해 SetThreadContext를 호출하여 대상 스레드의 EIP 레지스터, 즉 다음 명령의 주소를 포함하는 레지스터를 수정합니다. 이후 멀웨어는 스레드를 재개하여 호스트 프로세스에 기록한 셸코드를 실행합니다. 공격자의 관점에서 SIR 방식은 시스템 호출 중간에 스레드를 일시 중단했다가 재개하면 시스템 충돌이 발생할 수 있으므로 문제가 될 수 있습니다. 이를 방지하기 위해 더 정교한 멀웨어는 EIP 레지스터가 NTDLL.dll 범위 안에 있으면 나중에 스레드를 재개하고 다시 시도합니다.

Sha256: 787cbc8a6d1bc58ea169e51e1ad029a637f22560660cc129ab8a099a745bd50e
5. SetWindowsHookEx를 통한 후크 인젝션
후킹은 함수 호출을 가로채는 데 사용되는 기법입니다. 멀웨어는 특정 스레드에서 이벤트가 발생할 때 악성 DLL이 로드되도록 후킹 기능을 활용할 수 있습니다. 일반적으로 SetWindowsHookEx를 호출하여 후크 체인에 후크 루틴을 설치하는 방식으로 수행됩니다. SetWindowsHookEx 함수는 네 개의 인수를 사용합니다. 첫 번째 인수는 이벤트 유형입니다. 이벤트는 후크 유형의 범위를 반영하며, 키보드의 키 입력(WH_KEYBOARD)부터 마우스 입력(WH_MOUSE), CBT 등에 이르기까지 다양합니다. 두 번째 인수는 이벤트 실행 시 멀웨어가 호출하려는 함수에 대한 포인터입니다. 세 번째 인수는 해당 함수를 포함하는 모듈입니다. 따라서 SetWindowsHookEx를 호출하기 전에 LoadLibrary 및 GetProcAddress 호출이 나타나는 경우가 매우 많습니다. 이 함수의 마지막 인수는 후크 프로시저가 연결될 스레드입니다. 이 값을 0으로 설정하면 이벤트 발생 시 모든 스레드가 작업을 수행합니다. 그러나 멀웨어는 일반적으로 노출을 줄이기 위해 하나의 스레드를 대상으로 하므로, 단일 스레드를 찾아 대상으로 지정하기 위해 SetWindowsHookEx 전에 CreateToolhelp32Snapshot 및 Thread32Next 호출이 나타날 수도 있습니다. DLL이 인젝션되면 멀웨어는 SetWindowsHookEx 함수에 threadId가 전달된 프로세스를 대신하여 악성 코드를 실행합니다. 그림 5에서 Locky 랜섬웨어는 이 기법을 구현합니다.

Sha256: 5d6ddb8458ee5ab99f3e7d9a21490ff4e5bc9808e18b9e20b6dc2c5b27927ba1
6. 레지스트리 수정을 통한 인젝션 및 지속성(AppInit_DLLs, AppCertDlls, IFEO 등)
AppInit_DLLs, AppCertDlls 및 IFEO(Image File Execution Options)는 모두 멀웨어가 인젝션과 지속성 모두를 위해 사용하는 레지스트리 키입니다. 항목은 다음 위치에 있습니다.
HKLM\Software\Microsoft\Windows NT\CurrentVersion\Windows\Appinit_DllsHKLM\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows\Appinit_DllsHKLM\System\CurrentControlSet\Control\Session Manager\AppCertDllsHKLM\Software\Microsoft\Windows NT\currentversion\image file execution options
AppInit_DLLs
멀웨어는 다른 프로세스가 자신의 라이브러리를 로드하도록 Appinit_Dlls 레지스트리 키 아래에 악성 라이브러리의 위치를 삽입할 수 있습니다. 이 레지스트리 키 아래의 모든 라이브러리는 User32.dll을 로드하는 모든 프로세스에 로드됩니다. User32.dll은 대화 상자 같은 그래픽 요소를 저장하는 데 사용되는 매우 일반적인 라이브러리입니다. 따라서 멀웨어가 이 하위 키를 수정하면 대부분의 프로세스가 악성 라이브러리를 로드합니다. 그림 6은 Ginwui 트로이 목마가 인젝션 및 지속성을 위해 이 방식을 사용하는 모습을 보여 줍니다. 이 멀웨어는 RegCreateKeyEx를 호출하여 Appinit_Dlls 레지스트리 키를 열고, RegSetValueEx를 호출하여 값을 수정합니다.

Sha256: 9f10ec2786a10971eddc919a5e87a927c652e1655ddbbae72d376856d30fa27c
AppCertDlls
이 방식은 AppInit_DLLs 방식과 매우 유사하지만, 이 레지스트리 키 아래의 DLL은 CreateProcess, CreateProcessAsUser, CreateProcessWithLogonW, CreateProcessWithTokenW 및 WinExec Win32 API 함수를 호출하는 모든 프로세스에 로드된다는 차이가 있습니다.
Image File Execution Options(IFEO)
IFEO는 일반적으로 디버깅 목적으로 사용됩니다. 개발자는 이 레지스트리 키 아래의 'Debugger Value'를 설정하여 디버깅을 위해 프로그램을 다른 실행 파일에 연결할 수 있습니다. 따라서 해당 실행 파일이 시작될 때마다 연결된 프로그램도 시작됩니다. 이 기능을 사용하려면 디버거 경로를 지정하고 분석할 실행 파일에 연결하면 됩니다. 멀웨어는 이 레지스트리 키를 수정하여 대상 실행 파일에 자신을 인젝션할 수 있습니다. 그림 7에서 Diztakun 트로이 목마는 작업 관리자의 디버거 값을 수정하여 이 기법을 구현합니다.

Sha256:
7. APC 인젝션 및 AtomBombing
멀웨어는 비동기 프로시저 호출(APC)을 활용하여 대상 스레드의 APC 큐에 사용자 지정 코드를 연결함으로써 다른 스레드가 이를 실행하도록 할 수 있습니다. 각 스레드에는 대상 스레드가 경고 가능 상태에 진입할 때 실행되기를 기다리는 APC 큐가 있습니다. 스레드는 SleepEx, SignalObjectAndWait, MsgWaitForMultipleObjectsEx, WaitForMultipleObjectsEx 또는 WaitForSingleObjectEx 함수를 호출하면 경고 가능 상태에 진입합니다. 멀웨어는 일반적으로 경고 가능 상태에 있는 스레드를 찾은 후 OpenThread 및 QueueUserAPC를 호출하여 해당 스레드에 APC를 대기열에 추가합니다. QueueUserAPC에는 대상 스레드에 대한 핸들, 멀웨어가 실행하려는 함수에 대한 포인터 및 해당 함수 포인터에 전달되는 매개변수라는 세 가지 인수가 필요합니다. 그림 8에서 Amanahe 멀웨어는 먼저 OpenThread를 호출해 다른 스레드의 핸들을 획득한 다음, LoadLibraryA를 함수 포인터로 지정하여 QueueUserAPC를 호출함으로써 다른 스레드에 악성 DLL을 인젝션합니다.
AtomBombing은 enSilo 연구진이 처음 소개하고 이후 Dridex V4에서 사용된 기법입니다. 이전 글에서 자세히 설명했듯이, 이 기법도 APC 인젝션에 의존합니다. 하지만 다른 프로세스의 메모리에 기록하기 위해 atom 테이블을 사용합니다.

Sha256: f74399cc0be275376dad23151e3d0c2e2a1c966e6db6a695a05ec1a30551c0ad
8. SetWindowLong을 통한 추가 창 메모리 인젝션(EWMI)
EWMI는 Explorer 트레이 창의 추가 창 메모리에 인젝션하는 방식에 의존하며, Gapz 및 PowerLoader와 같은 멀웨어 계열에서 몇 차례 사용되었습니다. 애플리케이션은 창 클래스를 등록할 때 추가 창 메모리(EWM)라고 하는 추가 메모리 바이트 수를 지정할 수 있습니다. 하지만 EWM의 공간은 많지 않습니다. 이 제한을 우회하기 위해 멀웨어는 explorer.exe의 공유 섹션에 코드를 기록하고, SetWindowLong 및 SendNotifyMessage를 사용하여 함수 포인터가 셸코드를 가리키도록 한 뒤 이를 실행합니다.
멀웨어가 공유 섹션에 기록하는 방법에는 두 가지가 있습니다. 공유 섹션을 생성하여 자신과 다른 프로세스(예: explorer.exe) 모두에 매핑하거나, 이미 존재하는 공유 섹션을 열 수 있습니다. 전자는 힙 공간을 할당하고 몇 가지 다른 API 호출 외에도 NTMapViewOfSection을 호출해야 하므로 오버헤드가 있습니다. 따라서 후자의 방식이 더 자주 사용됩니다. 멀웨어는 공유 섹션에 셸코드를 기록한 후 GetWindowLong 및 SetWindowLong을 사용하여 'Shell_TrayWnd'의 추가 창 메모리에 액세스하고 이를 수정합니다. GetWindowLong은 창 클래스 객체의 추가 창 메모리에서 지정된 오프셋의 32비트 값을 가져오는 API이며, SetWindowLong은 지정된 오프셋의 값을 변경하는 데 사용됩니다. 이를 통해 멀웨어는 창 클래스의 함수 포인터 오프셋을 간단히 변경하고, 공유 섹션에 기록된 셸코드를 가리키도록 할 수 있습니다.
위에서 언급한 대부분의 다른 기법과 마찬가지로, 멀웨어는 기록한 코드를 트리거해야 합니다. 앞서 설명한 기법에서 멀웨어는 CreateRemoteThread, QueueUserAPC 또는 SetThreadContext와 같은 API를 호출하여 이를 수행했습니다. 이 방식에서는 SendNotifyMessage를 호출하여 인젝션된 코드를 트리거합니다. SendNotifyMessage가 실행되면 Shell_TrayWnd는 SetWindowLong이 이전에 설정한 값이 가리키는 주소로 제어를 전달합니다. 그림 9에서 PowerLoader라는 멀웨어는 이 기법을 사용합니다.


Sha256:
9. Shim을 사용한 인젝션
Microsoft는 주로 하위 호환성을 위해 개발자에게 Shim을 제공합니다. Shim을 사용하면 개발자는 코드를 다시 작성하지 않고도 프로그램에 수정 사항을 적용할 수 있습니다. 개발자는 Shim을 활용하여 운영 체제가 애플리케이션을 처리하는 방식을 지정할 수 있습니다. Shim은 본질적으로 API를 후킹하고 특정 실행 파일을 대상으로 지정하는 방법입니다. 멀웨어는 지속성과 인젝션 모두를 위해 Shim을 활용하여 실행 파일을 대상으로 지정할 수 있습니다. Windows는 바이너리를 로드할 때 적절한 수정 사항을 적용하기 위해 shimming 데이터베이스를 확인하도록 Shim Engine을 실행합니다.
적용할 수 있는 수정 사항은 많지만, 멀웨어가 선호하는 것은 DisableNX, DisableSEH, InjectDLL 등 일부 보안 관련 수정 사항입니다. 멀웨어는 shimming 데이터베이스를 설치하기 위해 다양한 방식을 사용할 수 있습니다. 예를 들어 일반적인 방식 중 하나는 sdbinst.exe를 실행하고 이를 악성 sdb 파일로 지정하는 것입니다. 그림 10에서 애드웨어인 'Search Protect by Conduit'는 지속성과 인젝션을 위해 Shim을 사용합니다. 이 애드웨어는 Google Chrome에 'InjectDLL' Shim을 적용하여 vc32loader.dll을 로드합니다. sdb 파일을 분석하기 위한 기존 도구가 몇 가지 있지만, 아래에 나열된 sdb를 분석하기 위해 python-sdb를 사용했습니다.

Sha256: 6d5048baf2c3bba85adc9ac5ffd96b21c9a27d76003c4aa657157978d7437a20
10. IAT 후킹 및 인라인 후킹(사용자 모드 루트킷이라고도 함)
IAT 후킹과 인라인 후킹은 일반적으로 사용자 모드 루트킷으로 알려져 있습니다. IAT 후킹은 멀웨어가 임포트 주소 테이블을 변경하는 기법입니다. 정상 애플리케이션이 DLL에 있는 API를 호출하면 원래 함수 대신 교체된 함수가 실행됩니다. 반면 인라인 후킹에서는 멀웨어가 API 함수 자체를 수정합니다. 그림 11에서 FinFisher 멀웨어는 CreateWindowEx가 가리키는 위치를 수정하여 IAT 후킹을 수행합니다.

Sha256: f827c92fbe832db3f09f47fe0dcaafd89b40c7064ab90833a1f418f2d1e75e8e
결론
이 글에서는 멀웨어가 다른 프로세스에서 자신의 활동을 숨기는 데 사용하는 10가지 서로 다른 기법을 다뤘습니다. 일반적으로 멀웨어는 셸코드를 다른 프로세스에 직접 인젝션하거나, 다른 프로세스가 악성 라이브러리를 로드하도록 강제합니다. 표 1에서는 다양한 기법을 분류하고, 이 글에서 다룬 각 인젝션 기법을 관찰할 때 참고할 수 있도록 샘플을 제공했습니다. 글 전체에 포함된 그림은 연구자가 멀웨어를 리버스 엔지니어링할 때 다양한 기법을 식별하는 데 도움이 됩니다.

공격자와 연구자는 인젝션을 수행하고 은밀성을 확보하기 위한 새로운 기법을 지속적으로 발견합니다. 이 글에서는 일반적이면서 새롭게 등장하는 10가지 기법을 자세히 설명했지만, COM 하이재킹과 같은 다른 기법도 있습니다. 공격자가 혁신을 멈추지 않을 것이므로, 방어자는 은밀한 프로세스 인젝션을 탐지하고 방지하는 임무를 결코 '완료'할 수 없습니다.
Endgame에서는 고급 은닉 기법을 지속적으로 연구하고 이를 방어하는 기능을 제품에 도입하고 있습니다. AppInit DLL, COM 하이재킹 등과 같이 지속성을 통해 로드되는 악성 DLL을 탐지하는 기능, 특허를 받은 셸코드 인젝션 방지 기능을 통해 여러 형태의 코드 인젝션을 실시간으로 차단하는 기능, 그리고 앞서 설명한 기법을 통해 메모리에서 실행되는 악성 인젝션 페이로드를 특허 출원 중인 파일리스 공격 탐지 기법으로 탐지하는 기능을 계층적으로 제공합니다. 이러한 접근 방식을 통해 Elastic의 플랫폼은 코드 인젝션을 방어하는 데 시장의 다른 어떤 제품보다 효과적일 수 있으며, 새롭게 등장하는 코드 인젝션 기법으로 인한 우회에 대한 복원력도 극대화합니다.