
크로스 플랫폼 시스템 콜 기반 임플란트 & C2 — 직접 시스템 콜 (Win), 원시 시스템 콜 (Linux), HTTPS/DNS/ICMP 채널. winapi 계층 없음.
English | 简体中文 | 조선어 | Русский
이 문서는 조선어(문화어) 버전입니다. 정화한 기술 술어는 원문(영문)을 기준으로 삼습니다.
WinAPI 셋 다리 없다. 모든 NT 원시 령(primitive)은 실행 시기에 기억장치에 적재된 ntdll에서 동적으로 해석되며——Plan9 어셈블리 루틴 조각을 통하여 SYSCALL 명령이 직접 나간다. 리입표(import table)가 없고 ntdll 사용자구역 이동수(钩子)에도 손을 대지 않는다. C2 통신은 HTTPS / DNS / ICMP 망을 돌며,매개 메시지는 AES-256-GCM AEAD로 봉하고 메시지마다 독립된 nonce를 쓴다. 삽입체(implant)는 Windows 고유로 만들고 Linux / macOS 서버 짓기는 그대로 보존한다.
이것은 syscall 포장자료실이 아니라 **온전한 삽입 골격(frame)**이다. 몸체 끼워넣기, 화일 읽기, 등록표(registry) 지속까지 모든 조작이 시동 때 현재 ntdll도출표에서 해석한 원시 Nt* syscall로만 진행된다. 바이너리 안에는 kernel32/advapi32/user32/win32u 리입이 하나도 없고,사용자구역 이동수(인라인 훅、IAT 훅、콜백)는 걸어볼 곳이 없다. 함수 이름은 일체 djb2 해시로만 존재하고,역어셈블리 관찰에서도 읽을 수 있는 문자열이 나타나지 않는다——32비트 해시와 SYSCALL 니모닉뿐이다.
ReadGSBase ──GS:[0x60]──▶ TEB ──+0x60──▶ PEB ──+0x18──▶ PEB_LDR_DATA
│
+0x10 InMemoryOrderModuleList ◀── 련결
│
련결을 따라가며 DllBase에서 PE 머리를 읽고 djb2로 ntdll을 가른다
│
IMAGE_DOS_HEADER.e_lfanew ──▶ IMAGE_NT_HEADERS
│
DataDirectory[0].VirtualAddress ──▶ 도출표
│
AddressOfNames[]을 하나씩 djb2 → 꼬리표 → AddressOfFunctions[]
│
함수 주소 ──▶ 머리글 단락 검사 ──▶ SSN 끄집어냄
h = 5381; h = h*33 + c。함수 이름 해시는 편집(compile) 시기에 끝나(hash.go,가락+UTF-16 두 형),바이너리에는 uint32 상수만 남는다。resolve.go):함수 앞 64바이트 창 안에서 아래 꼴을 맞추되 jmp 밀대(跳板)를 참는다(EB xx / E9 xx xx xx xx를 타고 이어서 검사):
B8 xx xx 00 00 0F 05(mov eax, SSN; syscall)B8 xx xx 00 00 C3(mov eax, SSN; ret)4C 8B D1 B8 xx xx 00 00 0F 05(mov r10, rdx; mov eax, SSN; syscall)B8 뒤 4바이트 소단위,低16비트가 SSN。map[uint32]uint16(함수 해시 → SSN)에 보존。NtUser*/NtGdi* 계열은 win32u.dll로 해석하며,SSN 공간이 ntdll과 따로다——따로 해석,따로 보존。asm_amd64.s:
| 단계 | 레지스터 동작 |
|---|---|
| 인자 배치 | 제1~3 인수는 CX/R8/R9,제4 인수는 R10(필수:SYSCALL이 RCX/R11을 망가뜨린다) |
| SSN 주입 | movl $ssn, AX |
| 발동 | SYSCALL → KiSystemCall64(MSR_LSTAR) |
| 복귀 | NTSTATUS는 RAX;NtDeviceIoControlFile류는 출력 가리키는 쪽을 RDX에 |
Syscall(≤7 인수)+ 새 Syscall9(9 인수,틀 $0-88)가 NtGdiBitBlt 같은 큰 인수 syscall을 덮는다.
직접 syscall의 호출갈림장에는 삽입 자신의 틀만 보인다——EDR은 갈림장만 되돌려도 곧장 발각한다. 간접 꼴은 ntdll .text 안에서 0F 05 C3(syscall; ret)가젯을 찾아 그곳으로 떨어뜨린다:
syscall이 든 페이지는 EDR이 비실행으로 바꾸는 경우가 많다——검사는 첫바이트를 피해 옆 0F 05 끝을 이용한다。SSN은 불안정한 인터페이스: 10.0.19041(20H1)→10.0.26100(24H2) 사이 함수 번호가 자주 뜬다. fingerprint.go가 현재 ntdll의 모든 해석된 SSN을 정렬+해시하여 64비트 지문을 만든다:
EDR은 ntdll .text를 RWX로 바꿔 5~14바이트 밀대(jmp r11류)를 박는 수법을 쓴다. 흐름:
NtCreateFile(\SystemRoot\System32\ntdll.dll) → NtMapViewOfSection(SEC_IMAGE)
→ 새 .text 로 기억부의 .text 를 바이트 단위로 겨뤄봄
→ NtProtectVirtualMemory(.text → RWX) → memcpy(깨끗한 낱장)
→ 보존(RX) → FlushInstructionCache → SSN 보존 없앰
매 신호(beacon) 앞머리에서 무조건 CheckAndUnhook()를 돈다——EDR이 다시 걸어도 다음 신호 전에 또 지워진다.
신호 돌이 돌아가며 4가지 끼워넣기 방식을 엇갈아 쓴다. 두 번의 끼워넣기가 같은 기억부 배치、같은 떨어진 곳、같은 스레드 경로를 보여주지 않는다.
| 방법 | 원시어 연쇄 | 숨김 분석 |
|---|---|---|
| Section 사상 | NtCreateSection → NtMapViewOfSection(본지 쓰기→먼 곳 읽기)→ NtCreateThreadEx | Section이 디스크 화일에 붙었고 VAD에 PAGE_EXECUTE_READWRITE가 없다;먼 곳 개인 낱장은 PAGE_EXECUTE_READ로만 보인다 |
| 과정 알맹이(进程镂空) | NtCreateUserProcess(걸어) → NtSuspendProcess → 그림 베이스를 비움 → NtWriteVirtualMemory(shellcode) → NtSetContextThread(RIP=짐) → NtResumeProcess | 과제 관리자에서 온당한 svchost.exe로 보인다——길,PID,PPID 다 참 |
| APC 줄세우기 | NtQuerySystemInformation로 스레드 헤아림 → NtOpenThread → NtQueueApcThread | 새 스레드、새 TEB、새 쌓기 없음;목적 스레드가 일깨워질 기다림에 들면 발동 |
| 모듈 덮기 | 목적 과정에 나눠줌 → 최소 PE 머리+shellcode → 입구점에서 NtCreateThreadEx | 모듈 목록에 타당하게 보이는 DLL 이름;PE 머리 벼리는 모듈 셈만 속이기에 족하다 |
모든 검측을 돌고 내는 것은 점수 위협 보고서。임계(Critical)일 때 자동소멸(SelfDel+NtTerminateProcess)。
| 검측 | 메커니즘 세부 |
|---|---|
| 가상기계 | CPUID(0x40000000) 12바이트 초관리기 서명(VMwareVMware、Microsoft Hv、KVMKVMKVM、XenVMM、QEMU、VBoxVBoxVBox…);0x40000001으로 되돌림 |
| 모래주머니(沙箱) | 논리 처리기 수;등록표 자국(VMware Tools、VBoxGuestAdditions);30+ 과정 이름(wireshark、procmon、x64dbg、idaq、dumpcap…) |
| 디버거 | PEB.BeingDebugged、NtGlobalFlag、쌓기 디버그 기、ProcessDebugPort/ObjectHandle/Flags、물리적 멈춤점 DR0~DR7、RDTSC 한 발 걸음 검사 |
| 시간수 이상 | NtQuerySystemInformation 지연 50샘플,평균 μ/표준차 σ;>10% 샘플이 μ±3σ 밖이면 계측기 분(EDR 훅 지연 특징) |
| PEB 숨김 | GS 축 읽음+NtWriteVirtualMemory로 BeingDebugged、NtGlobalFlag、ProcessHeap、DebugPort를 고치고,스레드 차례에 NtSetInformationThread(ThreadHideFromDebugger=0x11) |
| KUSER_SHARED_DATA | 0xFFFFF78000000000 상수 구역의 온당한 구멍(TickCount、SystemTime、PhysicalPages…)만 읽어 evasion 0x09로 보고 |
EnablePrivilege(index) — 아무 권한을 LUID로 올려놓기EnableAllTokenPrivileges() — 한 번에 20권한(SeDebug、SeImpersonate、SeTcb…)GetProcessTokenIntegrityLevel() — 강제 온전성 급StealProcessToken(pid) — NtOpenProcessToken→NtDuplicateTokenImpersonateThread() / RevertToSelf()EnumVirtualMemory()、FindWritableExecRegions()(PAGE_EXECUTE_READWRITE 찾기)、HideRegion()/UnhideRegion()(PAGE_NOACCESS)。
NtCreateFile→NtReadFile/NtWriteFile→NtClose。CreateFileA 등이 하나도 없다。NtCreateKey→NtSetValueKey;AddRunKeyPersistence()(자기 경로는 NtQueryInformationProcess(ProcessImageFileName=27))와 RemoveRunKeyPersistence();Advapi32 없음。EnumerateSystemHandles()、FindEDRHandles()、CloseEDRHandles()、IsProcessMonitored()。NtFreeVirtualMemory。PatchAMSI()(AmsiScanBuffer→xor eax,eax; ret)、PatchETW()(EtwEventWrite→ret)、PatchNtTraceEvent()、PatchDbgUiRemoteBreakin()、PatchInstrumentationCallbacks()。
| 통로 | 선형식 | 앞조건 |
|---|---|---|
| HTTPS | 이진 POST,틀 머리:16B 회기ID + 1B 종류 + 짐;UA/길 임의화 | TLS 인증서 |
| DNS | <idx>-<total>-<base32> 子도메인 알맹이,TXT 회답 | 공식 NS |
| ICMPv4 | 짐을 echo 요청/회답의 ID+seq에 나누어 심음 | 원막대소켓(root/Admin) |
모두 Channel 인터페이스 구현:NTP / DoH / TURN을 더하려면 인터페이스만 짜면 되고 agent는 가만。신호 돌이는 매 신호 앞에서 통로를 엇바꿔 돈다(round-robin)。
틀 선형식: