İnternette bu konuda bir rehber göremediğim için bu rehberi yazma kararı aldım. Umarım kendi extended kernelini yazmak isteyenlere iyi bir rehber olmuştur. Hazırsanız başlayalım.
Başlamadan önce
Eğer bu rehbere geldiyseniz ve kendi uyumluluk katmanızı/extended kernelinizi yazmayı düşünüyorsanız sabırlı olmanız gerekiyor. Zira burada anlatacağım fonksiyonlarda hatalı bir implementasyon yaptığınız zaman hedeflediğiniz yazılım çökebilir, düzgün çalışmayabilir ya da anlamlı/anlamsız hatalar alabilirsiniz. Aynı şekilde hedeflediğiniz yazılımın neden çöktüğünü anlamanız da kolay olmayabilir. Bir hatalı, eksik veya beğenmediğiniz nokta varsa lütfen belirtin. Ayrıca bu rehberde anlatılanların anti hile yazılımları gibi sistemi kontrol eden yazılımlarda kullanılmasında olacaklardan ben sorumlu değilim, bunu göze alarak rehbere devam edin.
Windows API'lerinin buradaki rolü
Rehber bir uyumluluk katmanı rehberi olduğu için burayı eklemesem rehber karışık bir hal alırdı, sonuçta herkes Windows API'lerinin nasıl çalıştığını/ne işe yaradığını bilmek zorunda değil.
Öncelikle bu API'ler basitçe şöyle çalışıyor:
Teknik tarafı da bu şekilde:
Extended Kernel'e tam olarak burada ihtiyaç duyuyoruz. "Bizim sistemimiz 6.3 (Build 9600) şeklinde değer veriyor, uygulamada 10.0 (Build 19045) şeklinde bir değer bekliyor. Bunu nasıl aşacağız?" sorusunun cevabı diyebilirim. Aşağıda buna daha detaylı olarak değineceğim zaten.
Tersine Mühendislik için kullandığımız yazılımlar
Yukarıda Windows API'lerini anlattığım için burası biraz gereksiz gelebilir, isterseniz diğer adımlara geçebilirsiniz fakat burayıda anlatsam daha iyi olur dedim.
Bu rehberde CFF Explorer ve Dependency Walker olacak şekilde iki farklı araç kullanacağız. Bu işi yapan başka araçlar da mevcut, bu rehberdeki adımları diğer araçlarda da uygulayabilirsiniz.
Genel olarak arayüzleri bu şekilde(görseller internetten alındı):
Bu araçları hedef yazılımın Windows API bağımlılıklarını analiz etmek için kullanacağız.
CFF Explorer import/export tablosunu görüntülememizi/değiştirmemizi sağlıyor.
Dependency Walker isminden de anlaşılacağı üzere bağımlıkları analiz etmeyi sağlıyor, aynı bölümden CFF Explorer'da mevcut olduğunu biliyorum.
Tanımları hallettiğimize göre rehbere devam edebiliriz.
Ne yapacağız?
Aşağıda buna detaylı olarak değineceğim dediğim yer burasıydı. Bizim sistemimiz 6.3 (Build 9600) şeklinde değer veriyor, uygulamada 10.0 (Build 19045) şeklinde bir değer bekliyorsa bunu basitçe uygulamanın istediği gibi sahte versiyon bilgisi vererek şöyle aşabiliriz:
Fakat burada önemli bir sorun var. Biz sahte versiyon bilgisi döndürmek için buna ayrı bir DLL yazabiliriz(aşağıda anlatacağım) fakat bu uygulama yukarıdaki örnekteki gibi GetProcAddress kullanıyor olabilir. Eğer bu şekilde kullanılıyorsa ayrı DLL yazıp İmport tablosunu değiştirmek bir işe yaramıyor, fonksiyonu hooklamamız gerekiyor. Gerek Assembly bilgimin az olmasından gerekle bir şekilde anlatsam bile uzayıp gideceğinden bu işlemi elle değil bir kütüphane yardımı ile yapacağız. Kullanacağımız kütüphanenin ismi MinHook, kullanımını aşağıda göstereceğim fakat kabaca fonksiyonu bizim fonksiyonumuzla değiştiriyoruz diyebilirim çalışma mantığı olarak.
İmport tablosu değiştirme yöntemi daha zahmetsiz bir yöntem, eğer yazılımınız GetProcAddress kullanmıyorsa hook kullanmayın, Import yöntemi daha basit.(Bunu import tablosundaki W10+ fonksiyonları İmport yöntemi ile değiştirip deneyebilirsiniz, eğer olmuyorsa GetProcAddress kullanılıyordur. Eğer açık kaynak ise tabii ki böyle bir şey yapıp zaman kaybetmeyin direkt koda bakın -ya da zaman kaybedin.
)
Import tablosu yöntemi
Bu yöntem başlıktaki gibi import tablosuna dayanıyor. Yani buraya:
Burada görüldüğü gibi yazılım tarafından içe aktarılan Microsoft'un kendi çevirisine göre "Dinamik Bağlantı Kitaplıkları" mevcut. Bu yöntemde bunları değiştirmeyi içeriyor. Burada bir tool kullanabilir ya da kendiniz elle yazabilirsiniz, yazılım kernel32.dll'nin diğer exportlarını da kullanmak zorunda olduğu için(büyük ihtimalle) diğer exportları kernel32.dll'nin kendisine yönlendirmeniz lazım.
Örnek olarak bunu koda şu şekilde ekleme yaparak halledebilirsiniz(kullanmadığınız her export için ayrı şekilde eklemeniz lazım, ayrıca aşağıda verdiğim yöntem 64 bit için geçerli - 32bit için daha farklı):
Sahte fonksiyon(burada RtlGetVersion "undocumented" olduğu için GetProcAddress ile çağırmanın dışında bir yolu yok, import tablosunu değiştirmek bir işe yaramayacak olduğundan yerine GetVersionExA kullanıldı):
Bu kodu derlediğimizde aşağıdaki gibi bir görüntüye ulaşıyoruz:
Tabii ki kernel32.dll'nin diğer fonksiyonlarını da yönlendirmemiz gerekiyordu, ben şimdilik forwardları eklemedim rehberi daha hızlı hazır etmek amacıyla.
Burada kendi yazdığımız DLL ile yerine geçmesini istediğimiz DLL'yi değiştiriyoruz.(GetVersionExA kernel32.dll'de ben onu değiştireceğim, yani kernel32.dll -> kendi yazdığım DLL şeklinde değişecek benim senaryomda. Yine söylüyorum, forwardları yazmazsanız yazdığınız fonksiyon hiçbir işe yaramaz, ben sadece rehber için geçici olarak forwardları yazmadım.) Burada mantık aslında istediğimiz fonksiyonu içermeyen/yazılımın versiyon kontrolünden geçmeyen kernel32.dll'yi kendi yazdığımız "çakma" kernel32.dll ile değiştirmek.
Hook Yöntemi
Yukarıda da belirttiğim gibi MinHook kullanacağız. Buradaki yöntem bir nevi DLL'yi komple değiştirmek yerine Import edilen fonksiyonu yazılıma kod enjekte ederek değiştirmek. Burada yazılıma DLL'yi enjekte etmeniz gerekiyor, ben CompatibilityAPI'de iki yöntemi hibrit şekilde kullandığım için bir enjektör kullanma ihtiyacım olmadı. Burada 3 farklı yol var. 1.yöntem import tablosu ve hook yöntemini hibrit şekilde kullanmak. 2.yöntem yazılıma DLL'yi enjekte etmek. 3.yöntem ise "AppInit_DLLs" kullanmak fakat imzalama/güvenli önyüklemeyi kapatmayı gerektirdiğini belirteyim. Ayrıca bu yöntemi sistemde olmayan fonksiyonlar da kullanılamıyor diye biliyorum, yanlışsam aşağıda belirtin.
MinHook'u Visual Studio'da kullanmak için Additional Include Directories/Additional Library Directories'den MinHook kütüphanesinin(en son release'nin bin versiyonu) olduğu dizini(bin ve include klasörlerinin içeriklerini tek klasöre kopyalayıp o klasörün yolunu yazacaksınız) ekledikten sonra Linker -> Input -> Additional Dependecies'den de Mimarinize göre MinHook'un olduğu dizinden lib dosyasını eklemeniz gerekiyor. Daha sonra:
bu şekilde kullanılabiliyor. Burada RtlGetVersion örneğini kullanmamın sebebi kullanılabiliyor olmasıydı. Hook yöntemi ile import tablosu yöntemindeki gösterdiğim örnek fonksiyonu da burada gösterdiğime benzer şekilde değiştirmek mümkün. Bir nevi
Dependency Walker'ın buradaki rolü
Elbette hangi API'nin değiştirileceğini bir yerden bilmemiz lazım, burada Dependency Walker devreye giriyor. Kırmızı renkli dll'ler eksik çağrı içeren DLL, sarı soru işareti eksik DLL ve kırmızı C işareti eksik çağrı anlamına geliyor. Sarı soru işareti bazen doğru olmayabiliyor, bu işaret olan DLL'leri sistemde aramanız daha kesin sonuç verecektir. Buradaki amaç zaten eksik çağrıları bulmak. Eğer sistem versiyonunu veren fonksiyonları değiştirecekseniz kendiniz sistem versiyonunu veren fonksiyonları internette araştırmanız gerekiyor.
Son olarak
Rehberi zaman ayırıp okuduğunuz için teşekkürler.
Başlamadan önce
Eğer bu rehbere geldiyseniz ve kendi uyumluluk katmanızı/extended kernelinizi yazmayı düşünüyorsanız sabırlı olmanız gerekiyor. Zira burada anlatacağım fonksiyonlarda hatalı bir implementasyon yaptığınız zaman hedeflediğiniz yazılım çökebilir, düzgün çalışmayabilir ya da anlamlı/anlamsız hatalar alabilirsiniz. Aynı şekilde hedeflediğiniz yazılımın neden çöktüğünü anlamanız da kolay olmayabilir. Bir hatalı, eksik veya beğenmediğiniz nokta varsa lütfen belirtin. Ayrıca bu rehberde anlatılanların anti hile yazılımları gibi sistemi kontrol eden yazılımlarda kullanılmasında olacaklardan ben sorumlu değilim, bunu göze alarak rehbere devam edin.
Windows API'lerinin buradaki rolü
Rehber bir uyumluluk katmanı rehberi olduğu için burayı eklemesem rehber karışık bir hal alırdı, sonuçta herkes Windows API'lerinin nasıl çalıştığını/ne işe yaradığını bilmek zorunda değil.
Öncelikle bu API'ler basitçe şöyle çalışıyor:
Kod:
-- "Senin İşletim Sistemi sürümün ne?"
-- "10.0 (Build X)"
C:
#include <windows.h>
#include <stdio.h>
int main(void) {
OSVERSIONINFOW ver;
ver.dwOSVersionInfoSize = sizeof(ver);
typedef LONG (WINAPI* pRtlGetVersion)(PRTL_OSVERSIONINFOW); // okunabilirlik için eklendi
pRtlGetVersion RtlGetVersion = (pRtlGetVersion)GetProcAddress(GetModuleHandleW(L"ntdll"), "RtlGetVersion"); // ntdll!RtlGetVersion
RtlGetVersion(&ver);
wprintf(L"Windows Version: %lu.%lu (Build %lu)\n", ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber); // unsigned long
}
Extended Kernel'e tam olarak burada ihtiyaç duyuyoruz. "Bizim sistemimiz 6.3 (Build 9600) şeklinde değer veriyor, uygulamada 10.0 (Build 19045) şeklinde bir değer bekliyor. Bunu nasıl aşacağız?" sorusunun cevabı diyebilirim. Aşağıda buna daha detaylı olarak değineceğim zaten.
Tersine Mühendislik için kullandığımız yazılımlar
Yukarıda Windows API'lerini anlattığım için burası biraz gereksiz gelebilir, isterseniz diğer adımlara geçebilirsiniz fakat burayıda anlatsam daha iyi olur dedim.
Bu rehberde CFF Explorer ve Dependency Walker olacak şekilde iki farklı araç kullanacağız. Bu işi yapan başka araçlar da mevcut, bu rehberdeki adımları diğer araçlarda da uygulayabilirsiniz.
Genel olarak arayüzleri bu şekilde(görseller internetten alındı):
Bu araçları hedef yazılımın Windows API bağımlılıklarını analiz etmek için kullanacağız.
CFF Explorer import/export tablosunu görüntülememizi/değiştirmemizi sağlıyor.
Dependency Walker isminden de anlaşılacağı üzere bağımlıkları analiz etmeyi sağlıyor, aynı bölümden CFF Explorer'da mevcut olduğunu biliyorum.
Tanımları hallettiğimize göre rehbere devam edebiliriz.
Ne yapacağız?
Aşağıda buna detaylı olarak değineceğim dediğim yer burasıydı. Bizim sistemimiz 6.3 (Build 9600) şeklinde değer veriyor, uygulamada 10.0 (Build 19045) şeklinde bir değer bekliyorsa bunu basitçe uygulamanın istediği gibi sahte versiyon bilgisi vererek şöyle aşabiliriz:
Kod:
-- Bu bilgisayar hangi Windows sürümünü kullanıyor?
-- (sahteversiyonbilgisi)
Fakat burada önemli bir sorun var. Biz sahte versiyon bilgisi döndürmek için buna ayrı bir DLL yazabiliriz(aşağıda anlatacağım) fakat bu uygulama yukarıdaki örnekteki gibi GetProcAddress kullanıyor olabilir. Eğer bu şekilde kullanılıyorsa ayrı DLL yazıp İmport tablosunu değiştirmek bir işe yaramıyor, fonksiyonu hooklamamız gerekiyor. Gerek Assembly bilgimin az olmasından gerekle bir şekilde anlatsam bile uzayıp gideceğinden bu işlemi elle değil bir kütüphane yardımı ile yapacağız. Kullanacağımız kütüphanenin ismi MinHook, kullanımını aşağıda göstereceğim fakat kabaca fonksiyonu bizim fonksiyonumuzla değiştiriyoruz diyebilirim çalışma mantığı olarak.
İmport tablosu değiştirme yöntemi daha zahmetsiz bir yöntem, eğer yazılımınız GetProcAddress kullanmıyorsa hook kullanmayın, Import yöntemi daha basit.(Bunu import tablosundaki W10+ fonksiyonları İmport yöntemi ile değiştirip deneyebilirsiniz, eğer olmuyorsa GetProcAddress kullanılıyordur. Eğer açık kaynak ise tabii ki böyle bir şey yapıp zaman kaybetmeyin direkt koda bakın -ya da zaman kaybedin.
Import tablosu yöntemi
Bu yöntem başlıktaki gibi import tablosuna dayanıyor. Yani buraya:
Burada görüldüğü gibi yazılım tarafından içe aktarılan Microsoft'un kendi çevirisine göre "Dinamik Bağlantı Kitaplıkları" mevcut. Bu yöntemde bunları değiştirmeyi içeriyor. Burada bir tool kullanabilir ya da kendiniz elle yazabilirsiniz, yazılım kernel32.dll'nin diğer exportlarını da kullanmak zorunda olduğu için(büyük ihtimalle) diğer exportları kernel32.dll'nin kendisine yönlendirmeniz lazım.
Örnek olarak bunu koda şu şekilde ekleme yaparak halledebilirsiniz(kullanmadığınız her export için ayrı şekilde eklemeniz lazım, ayrıca aşağıda verdiğim yöntem 64 bit için geçerli - 32bit için daha farklı):
C:
#pragma comment(linker, "/export:ActivateActCtx=kernel32.ActivateActCtx,@3")
#pragma comment (linker, "/export:OlduğuGibiBıraktığınızAPI=buapininbulunduğumodül.OlduğuGibiBıraktığınızAPI,@OrdinalNumarası")(hedef yazılımın import yöntemine bağlı olarak genelde rastgele olsa da olur, siz yine de kernel32.dll'yi CFF Explorer ile açıp exportlarından ordinal numarasını alıp ona göre girin)
Sahte fonksiyon(burada RtlGetVersion "undocumented" olduğu için GetProcAddress ile çağırmanın dışında bir yolu yok, import tablosunu değiştirmek bir işe yaramayacak olduğundan yerine GetVersionExA kullanıldı):
C:
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD dwReason, PVOID pvReserved)
{
UNREFERENCED_PARAMETER(pvReserved);
return TRUE;
}
__declspec(dllexport)
BOOL WINAPI GetVersionExA(OSVERSIONINFOA* vi)
{
if (!vi || vi->dwOSVersionInfoSize != sizeof(OSVERSIONINFOA))
return FALSE;
vi->dwMajorVersion = 10;
vi->dwMinorVersion = 0;
vi->dwBuildNumber = 19045;
vi->dwPlatformId = VER_PLATFORM_WIN32_NT;
vi->szCSDVersion[0] = 0;
return TRUE;
}
Bu kodu derlediğimizde aşağıdaki gibi bir görüntüye ulaşıyoruz:
Tabii ki kernel32.dll'nin diğer fonksiyonlarını da yönlendirmemiz gerekiyordu, ben şimdilik forwardları eklemedim rehberi daha hızlı hazır etmek amacıyla.
Burada kendi yazdığımız DLL ile yerine geçmesini istediğimiz DLL'yi değiştiriyoruz.(GetVersionExA kernel32.dll'de ben onu değiştireceğim, yani kernel32.dll -> kendi yazdığım DLL şeklinde değişecek benim senaryomda. Yine söylüyorum, forwardları yazmazsanız yazdığınız fonksiyon hiçbir işe yaramaz, ben sadece rehber için geçici olarak forwardları yazmadım.) Burada mantık aslında istediğimiz fonksiyonu içermeyen/yazılımın versiyon kontrolünden geçmeyen kernel32.dll'yi kendi yazdığımız "çakma" kernel32.dll ile değiştirmek.
Hook Yöntemi
Yukarıda da belirttiğim gibi MinHook kullanacağız. Buradaki yöntem bir nevi DLL'yi komple değiştirmek yerine Import edilen fonksiyonu yazılıma kod enjekte ederek değiştirmek. Burada yazılıma DLL'yi enjekte etmeniz gerekiyor, ben CompatibilityAPI'de iki yöntemi hibrit şekilde kullandığım için bir enjektör kullanma ihtiyacım olmadı. Burada 3 farklı yol var. 1.yöntem import tablosu ve hook yöntemini hibrit şekilde kullanmak. 2.yöntem yazılıma DLL'yi enjekte etmek. 3.yöntem ise "AppInit_DLLs" kullanmak fakat imzalama/güvenli önyüklemeyi kapatmayı gerektirdiğini belirteyim. Ayrıca bu yöntemi sistemde olmayan fonksiyonlar da kullanılamıyor diye biliyorum, yanlışsam aşağıda belirtin.
MinHook'u Visual Studio'da kullanmak için Additional Include Directories/Additional Library Directories'den MinHook kütüphanesinin(en son release'nin bin versiyonu) olduğu dizini(bin ve include klasörlerinin içeriklerini tek klasöre kopyalayıp o klasörün yolunu yazacaksınız) ekledikten sonra Linker -> Input -> Additional Dependecies'den de Mimarinize göre MinHook'un olduğu dizinden lib dosyasını eklemeniz gerekiyor. Daha sonra:
C:
#include <windows.h>
#include <stdio.h>
#include "MinHook.h"
typedef LONG(WINAPI* RtlGetVersion_t)(PRTL_OSVERSIONINFOEXW lpVersionInformation);
RtlGetVersion_t originalRtlGetVersion = NULL;
LONG WINAPI HookedRtlGetVersion(PRTL_OSVERSIONINFOEXW lpVersionInformation)
{
// Sabit Windows 10 bilgilerini döndür
lpVersionInformation->dwMajorVersion = 10;
lpVersionInformation->dwMinorVersion = 0;
lpVersionInformation->dwBuildNumber = 19045;
lpVersionInformation->dwPlatformId = 2; // VER_PLATFORM_WIN32_NT
wcscpy(lpVersionInformation->szCSDVersion, L"");
return 0; // STATUS_SUCCESS
}
int main()
{
MH_Initialize();
HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
FARPROC target = GetProcAddress(hNtdll, "RtlGetVersion");
MH_CreateHook(target, &HookedRtlGetVersion, (LPVOID*)&originalRtlGetVersion);
MH_EnableHook(target);
RTL_OSVERSIONINFOEXW verInfo = {0};
verInfo.dwOSVersionInfoSize = sizeof(verInfo);
RtlGetVersion(&verInfo);
wprintf(L"Windows Version: %lu.%lu Build %lu\n",
verInfo.dwMajorVersion,
verInfo.dwMinorVersion,
verInfo.dwBuildNumber);
MH_DisableHook(target);
MH_Uninitialize();
return 0;
}
RtlGetVersion -> HookedRtlGetVersion şeklinde bir değişim yapmış olduk hedef yazılımda.Dependency Walker'ın buradaki rolü
Elbette hangi API'nin değiştirileceğini bir yerden bilmemiz lazım, burada Dependency Walker devreye giriyor. Kırmızı renkli dll'ler eksik çağrı içeren DLL, sarı soru işareti eksik DLL ve kırmızı C işareti eksik çağrı anlamına geliyor. Sarı soru işareti bazen doğru olmayabiliyor, bu işaret olan DLL'leri sistemde aramanız daha kesin sonuç verecektir. Buradaki amaç zaten eksik çağrıları bulmak. Eğer sistem versiyonunu veren fonksiyonları değiştirecekseniz kendiniz sistem versiyonunu veren fonksiyonları internette araştırmanız gerekiyor.
Son olarak
Rehberi zaman ayırıp okuduğunuz için teşekkürler.
Son düzenleyen: Moderatör: