Vay canına, her şeyi yapan sihirli bir araç! Gerçek olamayacak kadar iyi geliyor ..gülümsemek Çok geçmeden, li0n çalıştırılabilir deneme sürümüne bir bağlantı gönderdi ve ben de araştırmaya başladım. AutoPlay Media Studio'da yazıldığını ve bunun için çalışan bir paket açıcı olmadığını çabucak anladım.
Ve sonra mohsen tarafından bir AmsDec.exe var . Ne yazık ki, sadece bazı dosyalar için çalışıyor (sözde - v8.1, v8.2) ve Farsça tuhaf mesajlar gösteriyor. Ve bu sadece ayıklar, gerçekten bir decompiler değil _proj.dat dosyayı CDD dosyası. Ve tabii ki Reverzor için işe yaramadı.
AutoPlay Media Studio nasıl çalışır?
Öyleyse, hepsini düzgün bir şekilde açmak için ne yapmamız gerektiğini görelim. AutoPlay Media Studio'nun yazarlarının değişiklik günlüğünde yazdığı gibi :
Hepimizin bildiği gibi, yeterince kararlı olan herhangi biri, yeterli zaman ve kaynak verildiğinde herhangi bir koruma sistemini kırabilir, ancak dönen kodların kullanımı genel saldırıları etkisiz hale getirir. Artık biraz daha rahat uyuyabilirsiniz!
Doğru ... Rastgele oluşturulmuş şifrelerle korunan ZIP dosyaları kullanıyorlar ve belli ki genel saldırıların nasıl çalıştığına dair hiçbir fikirleri yok ..
Unpacker'ın EXE dosyasını analiz etmesi, doğru şifre oluşturması ve dosyaları açması gerekir. Bir cdd dosyası varsa, onu da açın. Ve bu kadar basit olduğu için, AutoPlay Media Studio'yu sıfırdan statik bir paket açıcının nasıl yazılacağını açıklayan ayrı bir blog yazısı için hedef olarak kullanacağım.gülümsemek
AutoPlay Media Studio tarafından oluşturulan dosyaları nasıl dağıtabileceğiniz birkaç seçenek olduğundan, işte hızlı bir başvuru:
Exe dosyasını unpacker'a bırakın, her şeyi otomatik olarak açacaktır. Ardından, ayıklanan veri dosyaları için uygun klasörü ve yükleme komut dosyası için _proj.dat dosyasını kontrol edin.
AutoPlay alt klasöründe application.exe ve application.cdd içeren bir klasörünüz var ;
Bu dosyalar, AutoPlay Media Studio'da "Yayınla -> Sabit sürücü klasörü" kullanılarak oluşturulur. Örnek bir dosya, örneğin, Photoshop yükleyicisi olduğunu iddia eden Rus yazılımı (kötü amaçlı yazılım?) Olabilir .
Veri dosyaları AutoPlay klasöründe ve alt klasörlerde açık olduğundan, paketten çıkarılacak çok şey yoktur. Exe dosyasını unpacker'a bırakın, cdd dosyasını otomatik olarak bulacak ve _proj.dat dahil her şeyi açacaktır .
aynı klasörde application.exe ve application.cdd dosyalarınız var ;
Bu, AutoPlay Media Studio'da "Kaynak dosyalarını yeniden adlandır" özelliği etkinleştirildiğinde gerçekleşir. Ürüne sahte güvenlik ekleyen özelliklerden biridir:
Bu seçenek, yayınlama sırasında kaynak dosyalarınızın dosya adlarını gizlemek için tasarlanmıştır.
Bu bir Kullanıcılar Sniffer vakasıdır . Önceki duruma benzer şekilde, paketten çıkarılacak pek bir şey yok. Exe dosyasını unpacker'a bırakın, cdd dosyasını otomatik olarak bulacak ve _proj.dat dahil her şeyi açacaktır .
Gelişmiş kullanım durumları
Ancak bazen işler o kadar kolay değildir. Dolayısıyla, değiştirilmiş AutoPlay Studio ile nasıl başa çıkılacağıyla ilgili birkaç olası senaryo:
application.exe paketlenmiş ve application.cdd dosyası mevcut.
Bu Xiaopang'ın blogda bahsettiği resmi bir AMS stüdyo mücadelesi vakası. İyi haber - korumalarını kırmak için bir paket açma sihirbazı olmanıza ve PCGuard'ı düzgün bir şekilde açmanıza gerek yok. EXE'i VMWare'de çalıştırmak, işlem belleğini boşaltmak ve dumped exe'yi aracıma bırakmak yeterli. PE başlığı ve bölüm tablosu doğru olduğu sürece iyi olmalıdır.
Adım adım: 1) Çalıştır ve dök: 2) Dökümü kaydederken, orijinal dosya adını koru. Aksi takdirde paket açıcım cdd dosyasını bulamayacaktır : 3) Paket açıcı ile işlem dökümü:
application.exe paketlendi ve cdd dosyası yok .
Bu Reverzor'un durumudur . Öncelikle Enigma Sanal Kutusunu açmanız gerekir - bunun için diğer paket açıcımı kullanabilirsiniz ..göz kırpmak Şimdi ikisine de sahip exe ve CDD dosyalarını ama exe dosyası hala ASPack ile doludur. Yine, ASPack'i düzgün bir şekilde açmanıza gerek yok, sadece işlem belleğini çalıştırın ve boşaltın. Sonra işlem benim açıcımla exe döküldü.
application.exe saldırıya uğradı ve cdd dosyası başka bir adla yeniden adlandırıldı;
Bu bir Avara vakası . Yazar, Otomatik Kullan motorunu hackledi ve cdd dosya uzantısını dll ile değiştirdi . Paket açıcımın bu tür senaryoları otomatik olarak kapsamasının bir yolu yok, üzgünüm. Sadece adlandırmak idler.dll için idler.cdd ve bırak idler.exe unpacker üzerinde.
Sonuç
Bu benim için küçük bir hafta sonu projesiydi. Bazı maceralarda da size yardımcı oluyorsa, mutluyum. Sana hiç yardımcı olmazsa, umurumda değil.gülümsemek
Paket açıcıyı şu adresten indirin: https://www.mediafire.com/?cyb4kagdwey0j1b
Not - .NET 3.5'e karşı derlenen teknik nedenlerden dolayı, yalnızca .NET 4.0 yüklü bir bilgisayarda çalıştırmak istiyorsanız , aşağıdaki satırlarla amsunpacker.exe.config oluşturun :
dnSpy bir hata ayıklayıcı ve .NET derleme düzenleyicisidir. Herhangi bir kaynak kodunuz olmasa bile derlemeleri düzenlemek ve hata ayıklamak için kullanabilirsiniz. Ana Özellikler:
.NET Framework, .NET ve Unity oyun derlemelerinde hata ayıklama, kaynak kodu gerekmez
Kesme noktaları ayarlayın ve herhangi bir derlemeye adım atın
Yerliler, izle, otomatik pencereler
Değişkenler pencereleri, değişkenleri (örneğin şifresi çözülmüş bayt dizileri) diske kaydetmeyi veya onları onaltılık düzenleyicide (bellek penceresi) görüntülemeyi destekler
Nesne kimlikleri
Aynı anda birden fazla işlemde hata ayıklanabilir
Modül yükünde kırılma
İzleme noktaları ve koşullu kesme noktaları
Kesme noktalarını ve izleme noktalarını dışa / içe aktarın
Çağrı yığını, iş parçacıkları, modüller, işlemler pencereleri
Atılan istisnalarda kırılma (1. şans)
Değişkenler pencereleri, C # / Visual Basic ifadelerinin değerlendirilmesini destekler
Dinamik modüllerde hata ayıklanabilir (ancak CLR sınırlamaları nedeniyle dinamik yöntemler olamaz)
Çıktı penceresi, çeşitli hata ayıklama olaylarını günlüğe kaydeder ve varsayılan olarak zaman damgalarını gösterir :)
Çalışma zamanında kendi şifresini çözen montajların hataları ayıklanabilir, dnSpy bellek içi görüntüyü kullanır. Ayrıca, dnSpy'ı disk dosyaları yerine her zaman bellek içi görüntüleri kullanmaya zorlayabilirsiniz.
Genel API, hata ayıklayıcıyı kontrol etmek için bir uzantı yazabilir veya C # Etkileşimli penceresini kullanabilirsiniz.
Montaj Düzenleyicisi
Tüm meta veriler düzenlenebilir
IntelliSense ile C # veya Visual Basic'te yöntemleri ve sınıfları düzenleyin, kaynak kodu gerekmez
C # veya Visual Basic'te yeni yöntemler, sınıflar veya üyeler ekleyin
Düşük seviyeli IL yöntemi gövde düzenleme için IL editörü
Düşük seviyeli meta veri tabloları düzenlenebilir. Bu, dahili olarak hex düzenleyiciyi kullanır.
Hex Düzenleyici
Onaltılık düzenleyicide IL koduna gitmek için derlenmiş koddaki bir adrese tıklayın
Yukarıdakinin tersi, derlenmiş koda veya bitlerin diğer yüksek seviyeli gösterimine gitmek için hex düzenleyicideki bir IL gövdesinde F12'ye basın. Bir yamanın hangi ifadeyi değiştirdiğini bulmak harika.
.NET meta veri yapılarını ve PE yapılarını vurgular
Araç ipuçları, seçili .NET meta verileri / PE alanı hakkında daha fazla bilgi gösterir
Pozisyona git, dosyaya, RVA
.NET meta veri belirtecine, yöntem gövdesine, #Blob / #Strings / #US yığın uzaklığına veya #GUID yığın dizinine gidin
Referansları takip et (Ctrl + F12)
Diğer
BAML çözücü
Mavi, açık ve koyu temalar (ve koyu yüksek kontrastlı tema)
Yer imleri
C # Etkileşimli pencere dnSpy'ı komut dosyası oluşturmak için kullanılabilir
Sınıflar, yöntemler, dizeler vb. İçin derlemeleri ara
Sınıf ve yöntem kullanımını analiz edin, arayanları bulun vb.
Birden çok sekme ve sekme grubu
Referanslar vurgulanır, sonraki referansa geçmek için Tab / Shift + Tab tuşlarını kullanın
Giriş noktasına ve modül başlatıcı komutlarına gidin
Meta veri belirtecine veya meta veri satırı komutlarına git
Kod ipuçları (C # ve Visual Basic)
Projeye aktar
DnSpy tarafından kullanılan diğer açık kaynak kitaplıkların listesi
Selamlar. Uzun zamandır yazı yazmamamın(writeuplar hariç) nedeni aslında bu yazıydı. Bir süredir .NET Framework ile yazılmış bir yazılımın JIT ile iletişime geçişini ve bunun “hooklanması” hakkında çalışıyordum. Sonunda bu yazı ortaya çıktı.
Yazıma geçmeden önce dnSpy’ın, .NET dosyalar üzerinde çalışırken bizim işimize en fazla yarayacak yazılım olduğunun ve işini en iyi yapan .NET Decompiler/Debugger olduğunu kalın harflerle yazmak istiyorum.
Yazı Amacı
Bu yazıda bir JIT hooker yazmayı planlıyoruz (C#, C/C++ kullanarak). Peki bunu neden yapıyoruz?
Bizim amacımız aslında korumaya yönelik (Anti-Tamper)… .NET Framework ile yazılmış bir dosyayı dnSpy gibi decompile yazılımları ile basitçe decompile/ildasm edebiliriz ama bu her zaman kusursuz şekilde olmaz. Hedef yazılımın üzerinde bir koruma varsa (örn. ConfuserEx) ve bu korumanın da method yapısını bozmak gibi bir özelliği varsa (Anti-Tamper vb.) dnSpy dosyayı decompile edemez çünkü method IL’leri bozulmuş olacak.
Anti-Tamper Örneği
dnSpy dosyayı okuyamıyor çünkü methodlar bozuk. Uygulamanın düzgün şekilde çalışabilmesi için modül constructor’ı olan .cctor noktasında fonksiyonları düzeltmesi gerekiyor.
Tabii ki her korumada böyle olmak zorunda değil örneğin her class’ın kendi içeriğini, class’a ait constructor’da da çözdürebilir burada anlatmak istediğim nokta; çalışacak ilk noktada düzeltmesi gerektiği.
Anti Tamper olayının daha iyi kavranması için ConfuserEx üzerinde bir örnek göstereceğim : Bir uygulamamız var. Bu uygulamanın “Main()” fonksiyonunu decompile etmek için dnSpy üzerinden modüle sağ tıklayıp entry point noktasına gidiyorum.
Entry Point noktasında hiçbir şey gözükmüyor? Bir de fonksiyon adına sağ tıklayıp Edit Method Body… seçeneğinden IL kodlarına bakmayı deneyelim.
:D Program normal bir şekilde açılıp çalışıyor. O halde bu programın Main() fonksiyonundan dahi önce çalışan bir fonksiyonda program fonksiyonlarını düzelten bir fonksiyon çalışıyor.
Bu da Constructor olacak o hâlde noktam .cctor hemen modüle sağ tıklayıp .cctor noktasına gidelim.
(Anti-Tamper korumasının detaylı analizini farklı bir yazıda yaparız şimdilik kalsın). Eğer fonksiyon çalıştıktan hemen sonra bütün fonksiyonlar düzeliyorsa normal olarak decompile edilecek anlamına geliyor.
Yapacağımız şey belli dnSpy üzerinde tespit ettiğimiz antitamper fonksiyonuna breakpoint atalım ve o breakpoint çalıştığı zaman “Step Over(F10)” seçeneğinden devam edelim. Debugger tekrar duracak bu aşamada “Modules” sekmesinden programı dumplayalım.
Anti-Tamp fonksiyonu bütün fonksiyonları eski haline çevirmiş. Ancak bu aşamada bir problem var dosya çalışmayacaktır. Bunun nedeni anti-tamper fonksiyonunu silmemiş olmamız. Dosya çalıştığı zaman method body’leri alakalı keylerle tekrar aksiyona sokunca method bodyler tekrardan bozuluyor bu sefer yazılım exception’a düşüp kapanıyor. Bunun için cctor’da çalışan fonksiyonu noplayıp kaydedebiliriz burayı geçiyorum.
API Hooking
API : Application Programming Interface, işletim sisteminin kendisi ile iletişime geçmek isteyen uygulamalara sunduğu bir dizi fonksiyona verilen isimdir.
API Hooking ise iletişim kurmak istediğimiz API ile uygulama arasına girerek API’ye giden tüm istekleri, yeniden programımız içerisinde yazdığımız API’yi birebir taklit eden sahte fonksiyona yönlendirmek oluyor. Bunu örnekler ile daha net anlatacağım.
Örnek
Bunu daha net kavramak için daha kolay bir örnek üzerinden API Hooking’i göstereyim. Bu örnekte “user32.dll” içerisinde bulunan “MessageBoxA” API’sini hooklayacağız.
Bir Hooking Nasıl Gerçekleşir?
API Hooker yazarken izleyeceğimiz yol şu şekilde olacak:
Öncelikle hooklayacağımız API’yı tamamen taklit edebilecek sahte bir fonksiyon yazalım. “MessageBoxA” API’sini taklit edecek fonksiyonu yazabilmek için API’nin MSDN sayfasına giderek fonksiyonu inceleyelim.
Bunu beraber yapalım öncelikle buradan MSDN dökümantasyonuna gidelim.
Sahte fonksiyonumuzu yazdık şimdi hooklayacağımız API’nin process’imiz üzerindeki adresini tespit edelim.
Bunu ve gerisini manual hooking yaptıktan hemen sonra yapacağız.
Buraya kadar her şey tamam. Şimdi yapacağımız şey byteları çalışma zamanında düzenlemek. API’nin adresini almıştık dolayısıyla API ile iletişime geçildiği zaman sürecin nerden yönetildiğini biliyoruz. Yapmamız gereken tek şey okunan byteları editleyerek süreci kendi fonksiyonumuza “jumplatmak” (atlatmak) jmp.
Değiştireceğimiz byteları yedeklemek (bir byte array e kopyalamak).
Hemen nedenini de açıklayalım. Yazdığımız taklit fonksiyon (bizim isteğimize göre) gerçek API ile iletişim kurması gerektiğinde sürekli patchlenmiş API fonksiyonuna geleceği için kendi içerisinde sonsuz döngüye girecek. Bunu engellemek için gerçekten iletişime geçmesi gerektiğinde hook işlemini kaldırmamız lazım yani orjinal byteları yerine yazmamız lazım ki işini yerine getirebilsin.
Manual Hooking with x32dbg
İzleyeceğimiz bütün yol bu kadar. Şimdi daha iyi anlaşılması için x32dbg ile örnek bir dosya üzerinden manual hooking işleminin nasıl yapıldığına bakalım.
Öncelikle yazdığımız dosyanın kaynak kodu bu şekilde :
x32dbg üzerinde dosyayı çalıştırdıktan sonra entry noktasına gelince kesilecektir.
Eğer kesilmezse Symbols sekmesinden .exe modülünü seçip “_main” fonksiyonuna gidilebilir.
ilk olarak gidip sahte fonksiyonumuzun adresini kopyalayalım. “Symbols” sekmesinden “sample”.exe modülünü seçip sağ kısma fonksiyonumun ismini yazıp aratıyorum ve çıkan sonucun adresini kopyalayıp bir yere not ediyorum.
“Symbols” sekmesine tekrar gidip solda görünen modüllerden “user32.dll” i seçiyorum sağa bu modülün içerdiği export fonksiyonları gelecektir. Buradan MessageBoxA fonksiyonuna gidelim.
Burda gördümüz ilk satırı jmp 0xADRES şeklinde düzenleyeceğiz ancak bu düzenleme işlemini dump üzerinde yapacağız.
Neden Assemble edip düzenlemiyoruz da dump üzerinden düzenliyoruz? Çünkü hem otomatize hooker yazarken bunu yapacağımız için hem de yapıyı tamamı ile bozacağı için. Bunu kendiniz de deneyebilirsiniz eğer direkt olarak jmp opcode’unu koyup operand kısmına da adresimizi yazarsanız 1. atlatmada problem çıkmayacak ancak düzenlemeye çalıştığınızda iyice şaşacak disasm kısmı…
jmp 0xADRES şeklinde düzenlemek yerine push 0xADRES ret şeklinde düzenleyeceğim neden?
Uygulamam tam olarak MessageBoxA fonksiyonunun adresinde durunca (bp atıyoruz buraya öncesinden) MOV EDI, EDI satırına sağ tıklayıp dökümde takip ediyoruz.
Bu arada sağ kısımda stdcall(winapi çağırılırken kullanılan) MessageBoxA fonksiyonuna gönderdiğimiz argümanları stack üzerinde görebilirsiniz.
Önce buraya gidip istediğimiz asm satırlarının byte karşılığını alalım.
0: 68 10 1c e9 00 push 0xe91c10 5: c3 ret
68 10 1C E9 00 C3
Yalnız buraya dikkat! Düzenleme işlemine geçmeden önce ne kadar byte düzenleyeceksek orjinal yedeğini almamız gerekiyor. Bizim düzenleyeceğimiz byte sayısı 6 byte dolayısıyla ilk 6 byte’ı yedekleyelim.
8B FF 55 8B EC 83
Düzenleyeceğimiz kadar byte seçip CTRL+E kombinasyonu veya resimde gördüğümüz yolu takip ederek edit panelini açalım.
Hex kısmında en sola tıklayıp kopyaladığımız byteları yapıştıralım.
Satırımız bu hale dönecek. Şimdi bir kere devam ettiriyorum start buttonundan breakpoint’e tekrar çarpınca program ne çıktı vermiş diye bakıp hook işlemini kaldıracağım.
vuhuuu çıktımız geldi. Şimdi asıl messageBox işini yapabilmesi için byte’ları yedeklediğimiz bytelara yani eski haline döndürelim.
Evet, Manual hooking bu kadardı şimdi işi otomatizeye dökelim…
Geldik yazımızın ana konusuna. Bir .NET yazılımının yapısını tam olarak burda yazamayacağım onu da farklı bir yazıya bırakalım.
Normal API Hooking işleminde hangi yolu izliyorsak aynı yolu izleyerek compileMethod() fonksiyonunu sahte fonksiyonumuza yönlendirip gelen ILBody’i almak ve yazdırmak.
.NET Dilleri .NET bytecode’ları şeklinde compile edilir. Runtime şekilde CLR sanal makinesinde yorumlanır ve JIT ile çalıştırılır. JIT içerisindeki compileMethod() fonksiyonu, gelen ILBody’i native dile çevirmek için kullanılıyor…
Bu işlem eğer hedef uygulamanın framework sürümü 4.0’dan yüksekse “clrjit.dll”, eğer düşükse “mscorjit.dll” içerisinde runtime şekilde yapılıyor. Yazacağımız örnek dosyanın framework sürümü de 4.0’dan yüksek olduğu için işlemlerimizi clrjit.dll üstünden yürüteceğiz.
CFF Explorer yardımı ile clrjit.dll dosyasının hangi fonksiyonları extern ettiğine bakalım detaylı analizi GitHub reposu üzerinden yapacağız. 2 adet fonksiyon extern ediyor “getJit” ve “jitStartup” Hangisini hooklayacağımız konusuna karar verebilmek için repoyu inceleyelim. dotnet/runtime
satırda extern ettiği “getJit” fonksiyonunu 88. satırda da jitStartup fonksiyonunu görüyoruz. jitStartup fonksiyonu void bir fonksiyon iken getJit ICorJitCompiler* döndürüyor. Yapıyı inceleyelim.
extern"C"ICorJitCompiler*__stdcallgetJit();// #EEToJitInterface// ICorJitCompiler is the interface that the EE uses to get IL bytecode converted to native code. Note that// to accomplish this the JIT has to call back to the EE to get symbolic information. The code:ICorJitInfo// type passed as 'comp' to compileMethod is the mechanism to get this information. This is often the more// interesting interface.////classICorJitCompiler{public:// compileMethod is the main routine to ask the JIT Compiler to create native code for a method. The// method to be compiled is passed in the 'info' parameter, and the code:ICorJitInfo is used to allow the// JIT to resolve tokens, and make any other callbacks needed to create the code. nativeEntry, and// nativeSizeOfCode are just for convenience because the JIT asks the EE for the memory to emit code into// (see code:ICorJitInfo.allocMem), so really the EE already knows where the method starts and how big// it is (in fact, it could be in more than one chunk).//// * In the 32 bit jit this is implemented by code:CILJit.compileMethod// * For the 64 bit jit this is implemented by code:PreJit.compileMethod//// Note: Obfuscators that are hacking the JIT depend on this method having __stdcall calling conventionvirtualCorJitResult__stdcallcompileMethod(ICorJitInfo*comp,/* IN */structCORINFO_METHOD_INFO*info,/* IN */unsigned/* code:CorJitFlag */flags,/* IN */BYTE**nativeEntry,/* OUT */ULONG*nativeSizeOfCode/* OUT */)=0;...
compileMethod fonksiyonunu bulduk. Hedefimiz getJit(). Yorum satırlarında yazılmış kısımları da özetleyerek compileMethod’un amacını iyice oturtalım.
compileMethod, JIT derleyicisinden bir method için native kod oluşturmasını isteyen ana fonksiyondur. Derlenecek method ‘info’ parametresinde “CORINFO_METHOD_INFO” struct yapısı olarak geçirilir.
Bu işlem için de izleyeceğimiz yolu yazalım. Yazımızın başında yaptığımız API hooking işleminden birazcık farklı. Burada byte’ları doğrudan düzenlemeyeceğiz. getJit() fonksiyonunun döndürdüğü pointer, bir VTable döndürüyor bizim yapacağımız şey de bu VTable’ın gösterdiği ilk pointer’ı patchlemek, kendi fonksiyonumuzun adresi ile değiştirmek.
Çünkü VTable’daki ilk pointer compileMethod fonksiyonumuzu işaret ediyor.
CORINFO_METHOD_INFO yapısını da bırakalım şöyle:
structCORINFO_METHOD_INFO{CORINFO_METHOD_HANDLEftn;CORINFO_MODULE_HANDLEscope;BYTE*ILCode;unsignedILCodeSize;unsignedmaxStack;unsignedEHcount;CorInfoOptionsoptions;CorInfoRegionKindregionKind;CORINFO_SIG_INFOargs;CORINFO_SIG_INFOlocals;};structCORINFO_SIG_INFO{CorInfoCallConvcallConv;CORINFO_CLASS_HANDLEretTypeClass;// if the return type is a value class, this is its handle (enums are normalized)CORINFO_CLASS_HANDLEretTypeSigClass;// returns the value class as it is in the sig (enums are not converted to primitives)CorInfoTyperetType:8;unsignedflags:8;// used by IL stubs codeunsignednumArgs:16;structCORINFO_SIG_INSTsigInst;// information about how type variables are being instantiated in generic codeCORINFO_ARG_LIST_HANDLEargs;PCCOR_SIGNATUREpSig;unsignedcbSig;CORINFO_MODULE_HANDLEscope;// passed to getArgClassmdTokentoken;CorInfoCallConvgetCallConv(){returnCorInfoCallConv((callConv&CORINFO_CALLCONV_MASK));}boolhasThis(){return((callConv&CORINFO_CALLCONV_HASTHIS)!=0);}boolhasExplicitThis(){return((callConv&CORINFO_CALLCONV_EXPLICITTHIS)!=0);}unsignedtotalILArgs(){return(numArgs+hasThis());}boolisVarArg(){return((getCallConv()==CORINFO_CALLCONV_VARARG)||(getCallConv()==CORINFO_CALLCONV_NATIVEVARARG));}boolhasTypeArg(){return((callConv&CORINFO_CALLCONV_PARAMTYPE)!=0);}};
Local JIT Hook
Adımlarımız :
Clrjit.dll için DLL Import işlemi ve extern ettiği getJit fonksiyonunu almak.
getJit fonksiyonumuzun döndürüğü IntPtr değerini bir değişkene alalım ve içerisinde tuttuğu ilk pointer değerini okuyalım.
varvTable=getJit();//Döndürdüğü değeri vTable adlı değişkene tanımladık.varcompileMethodPtr=Marshal.ReadIntPtr(vTable);//vTable'ın ilk sanal fonksiyonunu okuyup compileMethodPtr adlı değişkene attık.
Burdan sonrası için önce sahte fonksiyonumuzu hazırlamamız gerekiyor. Öncelikle tamamen taklit edebilecek bir delegate hazırlamamız gerekiyor. Bu işlemleri farklı bir class üzerinde yapacağım.
(SJITHook Kullanılabilir) Bunu yaptıktan sonra kullandığımız bazı şeyler hata verecek hemen struct yapılarını da alalım corjit.h ve corinfo.h içerisinden.
[StructLayout(LayoutKind.Sequential)]publicunsafestructCorinfoSigInst{publicuintclassInstCount;uintdummy;publicIntPtr*classInst;publicuintmethInstCount;uintdummy2;publicIntPtr*methInst;}publicenumCorJitFlag{CORJIT_FLG_SPEED_OPT=0x00000001,CORJIT_FLG_SIZE_OPT=0x00000002,CORJIT_FLG_DEBUG_CODE=0x00000004,// generate "debuggable" code (no code-mangling optimizations)CORJIT_FLG_DEBUG_EnC=0x00000008,// We are in Edit-n-Continue modeCORJIT_FLG_DEBUG_INFO=0x00000010,// generate line and local-var infoCORJIT_FLG_LOOSE_EXCEPT_ORDER=0x00000020,// loose exception orderCORJIT_FLG_TARGET_PENTIUM=0x00000100,CORJIT_FLG_TARGET_PPRO=0x00000200,CORJIT_FLG_TARGET_P4=0x00000400,CORJIT_FLG_TARGET_BANIAS=0x00000800,CORJIT_FLG_USE_FCOMI=0x00001000,// Generated code may use fcomi(p) instructionCORJIT_FLG_USE_CMOV=0x00002000,// Generated code may use cmov instructionCORJIT_FLG_USE_SSE2=0x00004000,// Generated code may use SSE-2 instructionsCORJIT_FLG_PROF_CALLRET=0x00010000,// Wrap method calls with probesCORJIT_FLG_PROF_ENTERLEAVE=0x00020000,// Instrument prologues/epiloguesCORJIT_FLG_PROF_INPROC_ACTIVE_DEPRECATED=0x00040000,// Inprocess debugging active requires different instrumentationCORJIT_FLG_PROF_NO_PINVOKE_INLINE=0x00080000,// Disables PInvoke inliningCORJIT_FLG_SKIP_VERIFICATION=0x00100000,// (lazy) skip verification - determined without doing a full resolve. See comment belowCORJIT_FLG_PREJIT=0x00200000,// jit or prejit is the execution engine.CORJIT_FLG_RELOC=0x00400000,// Generate relocatable codeCORJIT_FLG_IMPORT_ONLY=0x00800000,// Only import the functionCORJIT_FLG_IL_STUB=0x01000000,// method is an IL stubCORJIT_FLG_PROCSPLIT=0x02000000,// JIT should separate code into hot and cold sectionsCORJIT_FLG_BBINSTR=0x04000000,// Collect basic block profile informationCORJIT_FLG_BBOPT=0x08000000,// Optimize method based on profile informationCORJIT_FLG_FRAMED=0x10000000,// All methods have an EBP frameCORJIT_FLG_ALIGN_LOOPS=0x20000000,// add NOPs before loops to align them at 16 byte boundariesCORJIT_FLG_PUBLISH_SECRET_PARAM=0x40000000,// JIT must place stub secret param into local 0. (used by IL stubs)};[StructLayout(LayoutKind.Sequential,Pack=1)]publicstructCorMethodInfo{publicIntPtrmethodHandle;publicIntPtrmoduleHandle;publicIntPtrilCode;publicuintilCodeSize;publicushortmaxStack;publicushortEHCount;publicuintcorInfoOptions;publicCorinfoSigInstargs;publicCorinfoSigInstlocals;}
Şimdi class ile işimiz bitti şimdi main kısmına geçip işlemlerimizi yapalım gerekli bütün açıklamalar yorum satırlarında yazıyor ama olur da bir yerde takılırsanız bana ulaşabilirsiniz
unsafestaticvoidMain(string[]args){uintold;Context.delCompileMethodhookedCompileMethod=HookedCompileMethod;varvTable=getJit();//ICorJitCompiler pointer'ı alındıvarcompileMethodPtr=Marshal.ReadIntPtr(vTable);//İçerisindeki ilk pointer okundu.OrigCompileMethod=(Context.delCompileMethod)Marshal.GetDelegateForFunctionPointer(Marshal.ReadIntPtr(compileMethodPtr),typeof(Context.delCompileMethod));//Orjinal compileMethod fonksiyonu Delegate türünde yüklendi.//Bizim iznimizde tekrardan çalıştırmak istersek orjinal fonksiyonu yerine koymak zorunda olduğumuz içinif(!VirtualProtect(compileMethodPtr,(uint)IntPtr.Size,0x40,outold))//VirtualProtect ile bölgenin izinleri execute read write izni olarak değiştirildireturn;RuntimeHelpers.PrepareDelegate(hookedCompileMethod);//Belirtilen temsilcinin kısıtlanmış bir yürütme bölgesine (CER) eklenmek üzere hazırlanması gerektiğini gösterir.RuntimeHelpers.PrepareDelegate(OrigCompileMethod);//Bunları koymadan çalıştırırsanız göreceksiniz ki program stackoverflow exception'a düşecek. Sonsuz döngüye girmemesi için koyuyoruz.Marshal.WriteIntPtr(compileMethodPtr,Marshal.GetFunctionPointerForDelegate(hookedCompileMethod));//Fake fonksiyonumuzun adresini alıp compileMethod pointer'ının yerine yazdırdık.VirtualProtect(compileMethodPtr,(uint)IntPtr.Size,old,outold);//İzinleri eski haline döndürüyoruz.Console.WriteLine(testFunc());//Bakalım çalışıyor muif(!VirtualProtect(compileMethodPtr,(uint)IntPtr.Size,0x40,outold))//VirtualProtect ile bölgenin izinleri execute read write izni olarak değiştirildireturn;//Şimdi orjinal compileMethod'u yazdıracağımız için tekrar izinleri düzenliyoruz execute read write olarak.Marshal.WriteIntPtr(compileMethodPtr,Marshal.GetFunctionPointerForDelegate(OrigCompileMethod));//Orjinal compileMethod'u yazdırdık fonksiyonumuzu normal çalıştırmak için.Console.WriteLine("Şuan çalışmıyor");Console.ReadKey();}publicstaticstringtestFunc(){return"Çalışıyorrr";}privatestaticunsafeintHookedCompileMethod(IntPtrthisPtr,[In]IntPtrcorJitInfo,[In]Context.CorMethodInfo*methodInfo,Context.CorJitFlagflags,[Out]IntPtrnativeEntry,[Out]IntPtrnativeSizeOfCode){inttoken;Console.WriteLine("Compilation:\r\n");Console.WriteLine("Token: "+(token=(0x06000000+*(ushort*)methodInfo->methodHandle)).ToString("x8"));//Token hesaplaması. dnSpy üzerinden tekrardan teyit edersek doğru olduğunu göreceğiz.Console.WriteLine("Name: "+typeof(Program).Module.ResolveMethod(token).Name);Console.WriteLine("Body size: "+methodInfo->ilCodeSize);varbodyBuffer=newbyte[methodInfo->ilCodeSize];//ilCodeSize tam da burda işimize yarıyor. ne kadar byte allocate edeceğimizi ona göre seçiyoruz.Marshal.Copy(methodInfo->ilCode,bodyBuffer,0,bodyBuffer.Length);//ilCode yapısını değişkenimize yazdırıyoruz.Console.WriteLine("Body: "+BitConverter.ToString(bodyBuffer));returnOrigCompileMethod(thisPtr,corJitInfo,methodInfo,flags,nativeEntry,nativeSizeOfCode);//Asıl fonksiyonu çalıştırıyoruz.}
Yazım gerçekten uzun olmuş olabilir. Tüm ayrıntılarıyla detaylı bir yazı ortaya çıkartmaya çalıştım. Takıldığınız bir nokta olursa bana her zaman yorumlar kısmından veya Twitter’dan ulaşabilirsiniz. Remote JIT Hooker’ı da çok yakında yayınlayacağım aralarında fazla açıklık olmayacak :D İngilizce versiyonu da yakında…