Process Injection Teknikleri

Berhan Bingöl
11 min readJul 5, 2021

--

Selamlar ben Berhan bu benim ilk yazım, bu yazımda Process nedir ve Process Injenction teknikleri nelerdir anlatmaya çalışacağım.

Let’s get started.

Process Nedir

Process, bellekte(RAM) bulunun ve çalışan her koda(Uygulama, program vs..) process(işlem) denir. Her çalışan uygulamanın veya kodun bellekte kendi process alanı vardır.

Process’in bellekteki çalışma mantığı. Eğer aklınızda Process in Memory hakkında bir soru işareti var ise buradan ve buradan da daha detaylı kaynaklara bakabilirsiniz.

Process kavramına değinip Thread kavramına değinmemek olmaz.

Thread

Thread kavramını şöyle bir benzetme ile anlatmak istiyorum Process’i kolumuz olarak düşünürsek Thread’ler bu kolun parmakları olarak düşünebiliriz.(Tabi örnekte hata aranmaz:)

Bir Process’in birden fazla iş yapabilmesine olanak sağlar ve Thread’ler aynı anda sadece tek bir iş yapabilir. X adet thread X adet iş.

STP, tek Thread ile çalışan Process. MTP, çok Thread ile çalışan bir Process. Günümüzde en çok kullanılan işletim sistemi mimarilerinden biridir.

Burada en basit gösterimi ile Google Chrome bir process ve biz bu process’i açtığımız zaman altında çalışan threadleri görebiliyoruz.

Process Environment Block

PEB, Process Environment Block bir Process’in adı, process kimliği (PID), yüklenen modüller, process’in kendisi ve DLL’leri dahil olmak üzere belleğe yüklenen tüm PE(Portable Executable) dosyalarının bilgilerini depolar ve çok daha fazlası hakkında yararlı bilgiler içerir. PEB bellekte sabit bir alana sahiptir ve her process’in kendi PEB alanı vardır. İşletim sistemi tarafından yürütülür, kernel tarafından çağrılır ve çalıştırılır. PEB TEB den gelir.

Thread Environment Block

TEB, Thread Environment Block mevcut bir Thread hakkında veri tutmaktan sorumludur. Her Thread’ın kendi TEB’i vardır.

Process Injection

Process Injection, kendi kodumuzu bellekte bulunan başka bir kodun içerisinde çalıştırmaktır, amacı ise genellikle antivirüs programları tarafından yakalanmamak için kullanılıyor diyebiliriz.

Kavram tanımı hatırlatması yaptığımıza göre başlayabiliriz.

Process Injection Teknikleri

Process Injection Teknikleri ise bir çok teknikten oluşmaktadır. Aralarındaki en temel fark ise bazı yöntemlerin güncel olması ve bazı yöntemlerin de farklı API kullanıyor olmasıdır. Tekniklerini sıralarsak:

  1. Process Doppelgänging
  2. Reflective DLL Injection
  3. Process Hollowing
  4. Classic DLL Injection
  5. ATOMBOMBING
  6. PE Injection
  7. Thread Execution Hijacking
  8. SHIMS
  9. APC DLL Injection
  10. REMOTE DLL Injection

Gibi teknikler bulunuyor.

Bu yazıda Classic DLL Injection, Process Hollowing, Process Doppelgänging ve Reflective DLL Injection tekniklerini anlatacağım.

İlk olarak Classic DLL Injeciton işlemine bakalım.

Classic DLL Injection

Classic DLL Injection ise kendi DLL’imizi başka bir process’in adres alanına yükleyip çalıştırma işlemidir.

DLL Nedir?

DLL( Dynamic Link Library) bir program tarafından fonksiyonları çağırmak için kullanılan Windows kütüphane dosyasıdır. Şöylede açıklanabilir bir uygulamayı veya programı tek bir dosaya haline çalışmasın olanak sağlayan bir yapıdır. DLL kullanımı daha fazla performans, daha az bellek kullanımı gibi faydalar sağlamaktadır.

Peki bu Classic DLL Injection tekniği nasıl gerçekleşiyor?

Classic DLL İnjection işleminde adımlar bu şekildedir.

Classic Dll Injection işleminde temel çalışma mantığına adım adım bakalım.

  • Zararlımız injection işlemeni gerçekleştirebilmesi için ilk adım olarak CreateToolhelp32Snapshot(), Process32First() ve Process32Next() API’lerini kullanarak hedefin yerini belirlemesi lazım.
  • CreateToolhelp32Snapshot(): Belirli bir işlemin yada işlemlerin yığın veya modül durumlarını numaralandırır ve Snapshot(Processin anlık görüntüsünü alır) olarak geri döner.
  • Process32First(): Snapshopt içinde yer alan ilk işlem hakkında bilgi getirir.
  • Process32Next(): Bunları yinelemek için bir döngü (loop) içerisinde kullanır. Process32First() ve Process32Next() API’lerinin birlikte kullanımı Process Walking adında bir tekniği de temsil etmektedir.
  • Hedefimizdeki process’in process id’si (PID) OpenProcess() fonksiyonuna verilerek o process’e erişim için kullanılacak bir handle değeri elde edilir.
  • Classic DLL Injection işlemi yaparken en başta zararlı yazılımların bellekte bir alana tahsis edilmesi gerekir. Bunun için bellekte kendine yer ayırması lazım. Bunu gerçekleştirebilmesi için VirtualAllocEx işlemini kullanırlar.
  • Kullandığımız bu işlem bellekte bulunan hedef process içerisinde kendine yer ayırır.
  • Şimdi kendi DLL adresimizi yazma vakti geldi.
  • WriteProcessMemory İşlemi ile kendine tahsis edilen alana DLL’in adını yazar.
  • Artık hedef processin içinde kendi DLL’imizin adı yazıldığına göre şimdi yapmamız gereken ise DLL’in hedef process içerisine yüklenmesini sağlamak. Bunu yapmamıza olanak sağlayan ise LoadLibraryA fonksiyonudur.
  • Bu fonksiyon Kernel32.dll tarafından export ediliyor.
  • Bu sebeple öncelikle GetModuleHandle fonksiyonu ile Kernel32.dll’e erişim için kullanılacak handle değeri elde edilir.
  • GetModuleHandle fonksiyonunun geri dönüş değeri GetProcAddress fonksiyonuna verilerek Kernel32.dll içerisinde LoadLibraryA fonksiyonunun adres değeri elde edilir.
  • Şuan elimizde Thread oluşturacak ve bu işlemi çalıştırabilecek kadar değer bulunmaktadır. Bu 3 adet değerimiz ise, hedef Process’e ait handle değeri, LoadLibraryA fonksiyonun adresi ve yüklenecek zararlı DLL’in adının adresidir.
  • CreateRemoteThread fonksiyonu ise bir Process’in sanal bellek alanında thread oluşturur.
  • Bu fonksiyon kullanılarak LoadLibraryA fonksiyonu, process’in ayrılan sanal bellek alanında bir Thread olarak çalıştırılır. Ve aslında burada spesifik bir uygulama yapmadığımız için işlemimiz bitmiş bulunuyor.
  • Eğer biz bir shellcode veya PE enjekte etmek istersek yine aynı işlemleri yapacaktık.
  • Allocate ettiğimiz alana shellcode yazıp CrateRemoteThread’e start adres olarak ayarladığımız alanın adresini verirsek shellcode olur.
  • PE dosyası yazıp entry pointini verirsek PE olur.
  • DLL’in yolunu yazıp start adres olarak LoadLibraryA API’ının adresini argüman olarak da yazdığımız yolu verdiğimiz zaman DLL’i çalıştırır ve işlem biter.

Örnek bir PoC koduna bakalım.

dllmain.cpp

Pek bir numarası olmayan enjecte edilecek dll dosyamız.

injection.cpp

Burada OpenProcess’e hedef process’in adresini biz kendimiz atıyoruz.

Bir programı her açıp kapattığımızda farklı bir PID adresi atanıyor, bu yüzden ilk olarak notepad’i açıp PID değerini koda dahil ediyorum.

VirtualAllocEx ile bellekte kendine yer açar ve WriteProcessMemory ile kendisine yer açılan yere dll path adresini yazdırıp CrateRemoteThread fonksiyonu ile thread oluşturur.

Farklı olarak WaitForSingleObject fonksiyonu ile thread execute işleminin bitmesini bekletir.

Ardından VirtualFreeEx ile dll dosyasını belleğe yüklenmesine izin verilir. Hadi çalıştıralım.

User Input Dll Injector

Bu da benim yapmış olduğum Dll Injector

https://github.com/xyafes/user-input-dll-injector

Process Hollowing

Process Hollowing(RunPE), tekniğinin temel amacı ise legal bir process’in belleğine zararlı executable’ın enjekte edilmesidir. Bunu uygulanma şekli ise, ilk olarak legal bir process’i ayağı kaldırılıyor sonra kaldırdığı bu uygulamayı suspend ediyor, suspend ettikten sonra kendi kodlarını o işlemin orjinal kodlarıyla değiştirip, son olarak da durdurduğu işlemi devam ettiriyor.

Peki oluşturma süreci nasıl ilerliyor, bakalım.

  1. İlk olarak CreateProcess — CRATE_SUSPENDED ile hedef uygulama suspend şekilde başlatılıyor.

2. Ardından NtUnmapViewOfSection fonksiyonu ile az sonra belleğe taşıyacağımız zararlı için ortamı hazırlanır.

3. Elimizde RAW data halde bulunan zararlımızı Windows üzerinde koşturabilmek için hafızada yerimizi belirlememiz gerekir. Bu tahsis işlemi için VirtualAllocEx fonksiyonunu kullanıyoruz.

4. Zararlımız PE formatında ve her bölümü hedef işlemin hafızasına yazılması gerekmektedir. WriteProcessMemory.

5. İlgili Process’i devam ettirmeden önce EP(Entry Point)’i güncellememiz lazım. SetThreadContext.

Son olarak da zararlı suspend mod içerisindeki işlemi aktif edebilmek içinde ResumeThread fonksiyonunu çağırır.

Burada hedefimiz svchost olarak belirliyoruz.

Örnek kodumuzu deneyelim.

Process Hollowing Completed

Process Doppelgänging

Process Doppelgänging tekniği ilk olarak 2017 yılında BlackHat’te enSilo şirketinde çalışan 2 güvenlik araştırmacısı tarafından sunuldu.

Process Doppelgänging, Windows 10 dahil olmak üzere tüm Windows sürümlerinde başarıyla çalışmasından dolayı büyük bir öneme sahiptir. Process Hollowing ile benzerlik gösterse de, Process Hollowing’den kesin olarak ayrılan yönleri vardır.

Temel farklardan birisi ise, Windows sistemlerinde kullanılan dosya üzerinde veri girişi çıkışlarını daha güvenli hale getirmek için kullanılan TxF — Transactional NTFS adında bir yöntemi kullanmasıdır.

Peki Nedir bu Process Doppelgänging?

Bu teknik temelde 4 adım dan oluşmaktadır.

  1. Transact
  2. Load
  3. Rollback
  4. Animate

Görsel biraz karışık gelebilir, açıklamak gerekirse ilk olarak zararlı process bir transact oluşturuyor ve bu transcat işlemi de bir adet dosya oluşturuyor. Oluşturulan bu dosyaya zararlı process tarafından payload yazılıyor. Yazılan bu payload’lı dosya bellekte yerini tahsis ediyor ve zararlı process dosyada bulunan payloadı rollback işlemi ile siliyor. Dosyanın process’i oluşturulur ve silme işlemi sayesinde zararlı kod sadece bellekte çalışmakta ve tespiti ise sadece bellek analizinde görülmektedir.

Adımları biraz daha detaylı açıklamak gerekirse.

Transact

Teknik bu kısımda TxF denen mekanizmayı kullanmakta. Bu mekanizmanın ve sağladığı API’lerin aslında temel amacı çoklu işlemleri tek ele almak ve dosya güncellemeleri gibi durumlar için kullanmak.

İlk olarak CreateTransaction() API’si kullanılarak yeni transaction(işlem) oluşturulur.

CreateFileTransactedW() API’si ile işlem görmüş bir handle elde edilir. Bu handle gereken tüm dosya işlemleri için kullanılabilir.

Legal dosyanın üzerine WriteFile() API’si ile zararlımızın içeriği yazılır.

Load

Bu aşamada ise ilk aşamada üzerine yazma işlemi yapılarak değiştirilen dosyadan bir memory bölümü oluşturulur. NtCreateSection() API’si ile işlem yapılan dosyadan bir bir bölüm oluşturulur. Bu bölüm, zararlı dosyaya işaret edecektir. Kısaca NtCreateSection ile dosyamızı hafızaya yüklüyor diyelim.

Rollback

Bir başka TxF nimeti olan Rollback sayesinde bu aşamada ise yapılan tüm değişiklikler geri alınır. Orijinal dosyayı diskte bırakır ve RollbackTransaction() API’si ile bu işlemi gerçekleştirmektedir.

Animate

Bu adımda NtCreateProcessEx ile bir process yaratılıyor ve işlem esnasında hafızada oluşturulan section kullanılıyor.

Hadi PoC kodumuzu deneyelim.

Burada cmd.exe üzerinden doppelgänging injection.exe dosyasının yolunu ve cmd.exe yi hedef aldığımız için cmd.exe’nin yolunu cmd’ye giriyoruz ve doppelgänging tekniği ile cmd.exe’yi çağırmış oluyoruz.

Reflective DLL Injection

DLL Injection tekniklerinden biri olan Reflective DLL Injection teknik ise farklı olarak, bir saldırganın hedef process içerisine disk yerine bellekten DLL eklemesine olanak sağlayan bir tekniktir.

Aslında Classic Dll Injection işleminde biz kendi processimizi hedef process tarafından çağırıyoruz, bu çağırma işlemini uygulamak için LoadLibrary fonksiyonunu kullanıyoruz bu fonksiyon da disk üzerinden yine kendi dll dosyamızı yazıyor, işin içine yine bir data kullanımı giriyor. Reflective Dll Tekniği ise kendisini belleğe yazdıktan sonra LoadLibrary fonksiyonunu kullanmıyor. Kısaca kendisini hedef process üzerine diski dahil etmeden direk bellekten yazıyor diyebiliriz.

İnceleyeceğimiz PoC kodu Stephen Fewer tarafından yazılmış ve kendisi Reflective Dll injection işlemini sunan kişidir.

Bu yöntem Classic yöntemden daha karışık gelebilir, basitçe anlatmam gerekirse.

Zararlı dosyamız çalıştığı zaman processimiz ilk olarak bir handle değeri elde edilir.

Daha sonra bellekte ana(host) process üzerinden yer ayırıyor ve bu yere DLL’in adresini kopyalanır. (ReflectiveLoader.c)

DLL'de hem kendisinin hem de kernel32.dll'nin LoadLibrary, GetProceAddress ve VirtualAlloc işlevinin yerini bulan ReflectiveLoader() işlevini çağırıyor.

ReflectiveLoader, bellekte kendine yeni yer ayıracak ve kendi image dosyasını yükleyecektir.

Kitaplığın resmi yükü esasen ana bilgisayar işleminde DLL’nin iki kopyasına sahip olacağı anlamına gelir.

ReflectiveLoader(), dll_process_attach ile yüklenen dll'nin Dllmain'ini çağırır.

DllMain

Aslında ReflectiveLoader’ın temelde 8 tane görev var bunları sıralarsak.

  1. Memory de yerini bulur.

2. Processin ihtiyaç duyduğu fonksiyonlar‘ı kernel tarafından export eder.(Get PEB).

3. İmajı için bellekte yeni bir yer ayırır.

4. Tüm sectionları yükler.

5. Process üzerinden image dosyasını import işlemini yapar.

6. Tüm image dosyalarımızı yer değiştirme processi gerçekleştirir.

7. İmage dosyalarının EP adresini arar.

8. EP adresini return edin böylece bizi her ne çağırdıysa DllMaini’de ihtiyacı varsa çağırabilir.

Biraz daha açıklayıcı şekilde ve adım adım ilerliyelim.

1) Kütüphaneni imajı hafızada rastgele bir konumda mevcut olacağından, ReflectiveLoader daha sonra kullanmak üzere kendi başlıklarını ayrıştırabilmek için önce kendi imajının hafızadaki mevcut yerini hesaplayacaktır.
2) ReflectiveLoader, yükleyici(Loader) tarafından gereken üç işlevin, yani LoadLibraryA, GetProcAddress ve VirtualAlloc’un adreslerini hesaplamak için ana bilgisayar işlemleri kernel32.dll dışa aktarma tablosunu ayrıştırır.
3) ReflectiveLoader, şimdi içine kendi görüntüsünü yüklemeye devam edeceği kalıcı bir bellek bölgesi tahsis edecektir.
Yükleyici daha sonra görüntüyü doğru bir şekilde yeniden konumlandıracağından konum önemli değildir.
4) Kütüphanenin başlıkları ve bölümleri bellekteki yeni konumlarına yüklenir. 5) ReflectiveLoader daha sonra imagesını içe aktarma tablosunun yeni yüklenen kopyasını işleyecek, ek kitaplıkları yükleyecek ve içe aktarılan ilgili işlev adreslerini çözecektir.
6) ReflectiveLoader daha sonra image dosyasının yer değiştirme tablosunun yeni yüklenen kopyasını işleyecektir.
7) ReflectiveLoader daha sonra yeni yüklenen image görüntünün giriş noktası olan işlevini DLL_PROCESS_ATTACH fonksiyonu ile DllMain’i çağırır.
Kitaplık belleğe başarıyla yüklendi.
8) Son olarak, ReflectiveLoader, yürütmeyi kendisini çağıran ilk bootstrap’ı shellcode döndürür veya CreateRemoteThread aracılığıyla çağrıldıysa, threat parçacığı sonlandırılır.

Reflective DLL Injection işlemi de bu şekildedir.

Başka bir yazıda görüşmek üzere.

But let’s move on..

Kaynaklar:

--

--