lhywk 님의 블로그

[악성코드 분석] 프로세스/코드 인젝션 본문

Malware Analysis

[악성코드 분석] 프로세스/코드 인젝션

lhywk 2026. 6. 24. 21:20

DLL 인젝션

강제 로드: 사용자가 만든 임의의 DLL 파일을 현재 실행 중인 다른 프로세스(Target Process)의 메모리 영역에 강제로 불러오게 만드는 기술이다.

기생(Parasite): 마치 기생충이 숙주에 들어가듯, 악성 코드가 정상 프로그램의 메모리 공간 안에 자리를 잡는 것이다.

메모리 보호 정책과 그 우회

기본 원칙 (보호): 현대 운영체제(Windows 등)는 프로세스 간의 독립성을 보장하기 위해, A 프로세스가 B 프로세스의 메모리에 함부로 접근하는 것을 차단한다.

우회 (Injection): DLL 인젝션은 이러한 보호 장벽을 우회하는 기법이다. 일단 DLL이 해당 프로세스 내부에 로드되면, 그 프로세스의 내부 구성원으로 취급받기 때문에 메모리 접근 권한을 획득하게 된다.

정당한 접근 권한 획득의 메커니즘

  • 외부인에서 내부자로: 프로세스 밖에서 접근하려 하면 운영체제가 차단하지만, 일단 DLL 인젝션을 통해 프로세스 메모리 내부로 들어오면 해당 프로세스의 '구성원'으로 인정받는다.
  • 메모리 공유: My.dll이 기존의 .text(코드), .data(데이터) 영역 및 kernel32.dll과 같은 공간에 공존하게 된다.
  • 권한: 같은 메모리 공간을 쓰기 때문에, My.dll은 별도의 제약 없이 타겟 프로세스의 전역 변수(.data)를 읽거나 함수 코드(.text)를 변조할 수 있는 정당한 접근 권한(Access Right)을 갖게 된다.

공격의 목적

다른 프로세스의 메모리 영역에 접근할 수 있게 되면 다음과 같은 작업이 가능하다.

  • 데이터 수정: 게임 내 돈이나 생명력을 조작하거나(Game Hack), 인증 과정을 무력화한다.
  • 기능 변조: 프로그램의 원래 동작을 가로채거나(Hooking) 변경한다.

핵심 기술 - LoadLibrary()

공격자는 타겟 프로세스에게 "이 DLL을 로드해라"라고 명령을 내리는데, 이때 타겟 프로세스 내부에서 LoadLibrary() 함수가 호출되도록 유도한다.

DLL 인젝션 5단계 (실제 API 호출 순서)

1. 대상 프로세스 핸들 구하기 - OpenProcess

  • 운영체제에게 "저 프로세스(dwPID) 좀 건드리고 싶으니 권한(Handle)을 달라"고 요청
  • PROCESS_ALL_ACCESS: 읽기, 쓰기, 종료 등 모든 권한을 요청
  • 결과: hProcess라는 핸들(열쇠)을 획득. 이후 모든 조작은 이 핸들을 통해 이루어진다.

2. 메모리 공간 할당 - VirtualAllocEx

  • VirtualAllocEx를 호출하여 타겟 프로세스(hProcess)에 dwBufSize만큼의 공간을 빌리고, 그 시작 주소를 pRemoteBuf에 저장한다.
  • 중요한 것은 단순히 공간만 만드는 게 아니라, 코드가 실행될 수 있도록 실행 권한 옵션을 함께 설정해야 한다는 점이다 (직접 코드 인젝션의 경우. DLL 경로 문자열만 쓸 때는 쓰기 권한이면 충분).

3. 데이터 쓰기 - WriteProcessMemory

  • 어디에? hProcess의 pRemoteBuf 위치에.
  • 무엇을? 내 프로세스에 있는 szDllName(DLL 경로 문자열)을.
  • 얼마나? dwBufSize 크기만큼.

4. 로드 함수 주소 찾기 - GetProcAddress

  • LoadLibrary를 직접 타겟 프로세스 안에서 실행시키려면, 타겟 프로세스 메모리 안에서 그 함수가 정확히 어디 있는지 알아야 한다.
  • 그런데 윈도우 운영체제 구조상, 핵심 시스템 DLL인 kernel32.dll은 모든 프로세스에서 동일한 메모리 주소에 로딩된다.
  • 따라서 공격자는 자기 자신의 프로세스에서 GetProcAddress를 호출하여 LoadLibrary의 주소를 구하고, 이 주소를 타겟 프로세스에서도 그대로 사용할 수 있다.

5. 원격 스레드 실행 - CreateRemoteThread

  • 타겟 프로세스(hProcess) 내부에 새로운 스레드를 생성하여 실행시킨다.
  • lpStartAddress(실행할 함수): 4에서 구한 LoadLibraryW의 주소(pThread)
  • lpParameter(함수의 인자): 3에서 경로를 써둔 메모리 공간의 주소(pRemoteBuf)
  • 결과: 타겟 프로세스가 스스로 LoadLibrary("악성DLL")를 호출하게 되며, 악성 DLL이 메모리에 로드된다.
  •  

 

API 후킹

DLL을 인젝션하는 것만으로는 아직 "조용히 들어와 앉아있는" 상태일 뿐이다. 본격적인 감시/조작을 하려면 API 후킹이 필요하다.

후킹의 전제 조건: 다른 프로세스의 API 호출을 가로채려면(Hooking), 내 코드가 그 프로세스 안에 있어야 한다. 따라서 코드 인젝션(DLL 인젝션)은 API 후킹을 수행하기 위한 사전 단계 역할을 한다. DLL을 주입한 후 SetWindowsHookEx 등의 함수를 실행하여 본격적인 후킹을 건다.

후킹의 정상적 활용

  • 기능 개선 및 패치: 소스 코드가 없거나 재컴파일이 불가능한 오래된 프로그램에 코드 인젝션으로 직접 오류를 수정(Hot Patching)하거나 새 기능을 덧붙인다.
  • 시스템 관리 및 감시: 기업이나 PC방 등에서 사용자를 관리하거나 특정 프로그램의 오작동을 모니터링하는 감시 코드를 추가한다.

후킹의 악의적 활용 - 키보드/마우스 후킹 (키로깅)

가장 대표적인 예가 키보드/마우스 입력을 가로채는 키로거다.

 

DLL 인젝션과 코드 인젝션의 관계

코드 인젝션(Code Injection): 실행 가능한 기계어 코드(Opcode)나 함수를 현재 실행 중인 다른 프로세스의 메모리 영역에 강제로 삽입하여 실행시키는 더 넓은 의미의 기법이다.

포함 관계: 코드 인젝션은 넓은 의미의 기술 용어이고, DLL 인젝션은 코드 인젝션을 구현하는 가장 대표적이고 일반적인 방법 중 하나다.

구분 DLL 인젝션 쓰레드(직접 코드) 인젝션
실행할 코드의 형태 완성된 .dll 파일 (디스크에 존재) 어셈블리어 코드 조각(Shellcode), 파일로 존재하지 않음
구현 난이도 비교적 쉬움 높음
탐지 가능성 백신이 디스크의 악성 DLL 파일을 스캔하여 쉽게 잡아낼 수 있음 디스크에 흔적을 남기지 않아 탐지가 훨씬 어려움 (Fileless)
보안 위협 관점 전통적인 파일 기반 진단으로 탐지 가능 EDR 등 메모리 행위 기반(Memory Forensics) 탐지가 필요

 

프로세스 인젝션

프로세스 할로잉을 이해하기 전에, 그 상위 개념인 프로세스 인젝션을 먼저 짚어야 한다.

정의: 대상 프로세스에 외부의 코드 자체를 삽입하고, 해당 코드를 강제로 실행시키는 기법이다.

동작 방식: 단순히 DLL을 로드하거나 스레드를 생성하는 형태(5-1장의 DLL 인젝션)가 아니다. 메인 프로세스 영역에 코드를 직접 추가하거나 기존 코드를 변조하여 실행 흐름을 바꾼다.

실행 조건: 프로세스의 구조를 변경해야 하므로, 해당 프로세스가 중지(Suspended) 상태에서만 작업이 가능하다.

프로세스 인젝션의 주요 기법 두 가지

  1. 프로세스 할로잉 (Process Hollowing): 정상 프로세스를 중지 상태로 생성한 뒤, 메모리의 정상 코드를 비워내고(Hollow) 그 자리에 악성 코드를 채워 넣는 기법 
  2. 프로세스 도플갱잉 (Process Doppelgänging): NTFS 트랜잭션 기능을 악용하여 악성 코드를 정상 파일처럼 보이게 한 뒤 로드하는 기법

 

프로세스 할로잉

정상 프로세스를 실행시킨 후, 그 내부의 원래 코드와 데이터를 파내어(Hollow) 없애고 그 자리에 악성 코드를 채워 넣는 기법이다.

위장 및 은닉: 정상 프로그램을 실행할 때 원본 코드와 데이터를 악성 코드로 바꿔치기하는 방식으로 주로 이용된다.

탐지 회피: 실행 중인 상황에서는 작업 관리자나 보안 도구에 정상 프로그램(예: 윈도우 시스템 프로세스인 svchost.exe)으로 보이기 때문에, 보안 탐지를 우회하기 위해 악성코드가 매우 빈번하게 사용한다.

상세 공격 과정

  1. CreateProcess() API로 타겟이 되는 정상 프로그램을 일시 중지 상태로 프로세스를 생성한다.
  2. GetThreadContext() API로 생성된 프로세스 메모리의 시작 위치를 파악하고 ReadProcessMemory() API로 Image Base 정보를 읽어온다.
  3. NtUnmapViewOfSection() API로 이미지 베이스 영역에 존재하는 원래의 정상 코드와 데이터를 메모리에서 해제한다.
  4. VirtualAllocEx() API로 메모리 영역에 새로운 메모리 공간을 다시 할당한다.
  5. WriteProcessMemory() API로 공격자가 준비한 악성 코드를 기록한다.
  6. SetThreadContext() API로 EIP의 값을 삽입된 악성 코드의 시작 지점으로 변경한다. 그 후 ResumeThread() API로 Suspend 상태를 풀고 프로세스를 다시 재실행한다.

 

프로세스 도플갱잉

  1. 정상 파일을 대상으로 트랜잭션을 생성해 핸들을 얻는다.
  2. 트랜잭션 내부에서 해당 파일의 내용을 악성 코드로 덮어쓴다.
  3. 트랜잭션 파일 핸들을 이용하여 메모리에 섹션 객체를 생성한다.
  4. 섹션 생성이 끝나면 즉시 트랜잭션을 롤백한다.
  5. 섹션 객체를 이용하여 새로운 프로세스를 생성한다.
  6. 프로세스의 매개변수와 PEB를 설정한다.
  7. 메인스레드를 생성하고 실행을 시작한다.