Bloga geri dön
İlk yazımızda, Android uygulamalarında dinamik analiz yöntemlerini ve çeşitli araçlar kullanarak güvenlik açıklarını nasıl tespit edebileceğimizi incelemiştik. Bu yazıda ise, UnCrackable2 uygulamasının incelemesini yapacağız.
Uygulamayı analiz etmeye, her zamanki gibi, AndroidManifest.xml dosyasını inceleyerek başlıyoruz. Bu dosya, uygulamanın temel yapı taşlarını ve bileşenlerini tanımlayan önemli bir dosyadır.
MainActivity sınıfı, uygulamanın başladığında ilk görülen ekranını temsil eder. Bu aktivite, android.intent.action.MAIN filtresi ile uygulama başlatıldığında tetiklenir. Manifest dosyasını inceledikten sonra, bir sonraki adımımız MainActivity sınıfını incelemek olacak. MainActivity, uygulamanın ana ekranını kontrol eder ve kullanıcının girdiği şifreyi doğrulamak için kullanılan mekanizmanın temelini oluşturur.
Uygulamayı açtığımızda, root kontrolü nedeniyle kapanır. İlk yazımda bunun nasıl atlatılabileceğini detaylı bir şekilde anlatmıştım. Aşağıdaki Frida scriptini çalıştırarak bu adımı geçebilirsiniz.
Frida Scripti:
Öncelikle, MainActivity.java dosyasındaki verify() fonksiyonu ve this.m.a(obj) fonksiyonu arasındaki ilişkiyi inceleyelim:
Burada dikkat çeken nokta, bar() fonksiyonunun native (yerel) bir fonksiyon olmasıdır. Yani, bu fonksiyonun bir kısmı, Java yerine yerel kütüphanede yazılmıştır.
this.m.a(obj) fonksiyonunun çalışması sırasında, CodeCheck sınıfındaki bar(byte[] bArr) fonksiyonu devreye girer. Bu fonksiyon, yerel bir kütüphaneden çağrılarak, kullanıcının girdiği şifreyi, kütüphanede saklı olan gizli şifreyle karşılaştırır.
bar() fonksiyonu, strncmp gibi karşılaştırma fonksiyonları kullanarak şifrenin doğruluğunu kontrol eder. Yani, esas doğrulama işlemi bar() fonksiyonu içinde gerçekleşir.
Doğru şifreyi bulabilmek için yerel kütüphane olan libfoo.so dosyasını analiz etmek gereklidir. Bu modülün detaylı incelenmesi, içinde kullanılan algoritmaların ve gizli şifrenin belirlenmesine yardımcı olabilir.
APK’dan libfoo.so’yu çıkartabilmek için, aşağıdaki adımları takip edebilirsiniz:
APK dosyasını analiz etmeye başlamadan önce, APK’yı apktool gibi bir araçla decompile etmemiz gerekiyor. APK’yı decompile etmek, içinde yer alan dosyalara ve kütüphanelere kolayca erişmemizi sağlar.
APK’yı decompile etme komutu:
Bu komut, APK dosyasını açar ve içerisindeki libfoo.so gibi yerel kütüphaneleri bulmamıza yardımcı olur. Çıkarılan dosyaların bulunduğu klasörde, libfoo.so dosyasını arayarak çıkartabilirsiniz.
libfoo.so dosyasını elde ettikten sonra, binary ninja gibi bir tersine mühendislik aracını kullanarak bu dosyanın içeriğini analiz edebiliriz.
binary ninja ile analiz süreci:
bar() fonksiyonu, kullanıcıdan alınan şifreyi gizli bir şifreyle karşılaştırmak için kullanılan yerel bir fonksiyondur. Şifre doğrulama sürecinde bar() fonksiyonu, kullanıcının girdiği şifreyi önceden belirlenmiş bir gizli şifreyle karşılaştırarak doğrulama yapar. Bu gizli şifre bellek içinde saklanır ve var_30 veya eax_2 gibi bellek alanlarında bulunur.
Şifre doğrulama, strncmp fonksiyonu kullanılarak yapılır. Bu fonksiyon, girilen şifrenin ilk 23 baytını gizli şifreyle karşılaştırır ve eşleşip eşleşmediğini kontrol eder. Bu karşılaştırma işlemi sayesinde, girilen şifrenin doğru olup olmadığını kolayca tespit edebiliriz.
Eğer kullanıcı tarafından girilen şifre, gizli şifreyle tam olarak eşleşirse, fonksiyon başarılı bir doğrulama sonucu döndürür ve result.b değeri 1 olarak ayarlanır. Eğer şifre yanlışsa, result sıfır olarak döndürülür.
Frida ile, bar() fonksiyonunun nasıl çalıştığını dinleyebiliriz ve şifre doğrulama sürecinde hangi parametrelerin kullanıldığını takip edebiliriz. Aşağıda, şifre doğrulama sürecini izleyen bir Frida scripti örneğini bulacaksınız. Bu script, bar() fonksiyonu çağrıldığında kullanıcıdan alınan şifreyi yazdırır ve şifrenin doğru veya yanlış olduğunu konsola yazar.
Frida Scripti:
Java.perform(function() {
send("Hooked exit()");
var system = Java.use("java.lang.System");
system.exit.implementation = function() {
send("Root Bypass");
send("Hooked function");
Interceptor.attach(Module.getExportByName('libfoo.so', 'strncmp'), {
onEnter(args) {
if (Memory.readUtf8String(args[0]).startsWith("AAAAAAAAAAAAAAAAAAAAAAA")) {
send("Secret String: " + Memory.readUtf8String(args[1]));
}
}
});
};
});Script:
strncmp fonksiyonu, genellikle iki diziyi belirli bir uzunlukta karşılaştırmak için kullanılır. Özellikle şifre doğrulama işlemlerinde bu fonksiyon sıkça kullanılır. Frida ile bu fonksiyonu dinleyerek, şifre doğrulama sürecindeki parametreleri izleyebiliriz.
onEnter, strncmp fonksiyonu çağrılmadan önce çalıştırılır. Burada, args[0] kullanılarak kullanıcı tarafından girilen şifre alınır. Bu şifre bellekteki diziden okunur ve bir string olarak alınır.
Kullanıcı tarafından girilen şifrenin ilk kısmı kontrol edilir. Eğer şifre, "AAAAAAAAAAAAAAAAAAAAAAA" dizisiyle başlıyorsa, bu doğru şifreyi tespit etme aşamasıdır. Bu kontrol, disassembled kodda belirtilen 23 karakterlik gizli diziyi temsil etmektedir.
Eğer şifre doğruysa ve karşılaştırma başarılıysa, ikinci parametre olan gizli şifre args[1] konsola yazdırılır. Bu, şifre doğrulama sırasında karşılaştırılan gizli şifredir ve Frida scripti sayesinde bu değeri izleyebiliriz.
Scriptin içinde bahsedilen strncmp fonksiyonu, eax_2 ve &var_30 dizilerini karşılaştırıyor. Bu karşılaştırmanın uzunluğu ise 0x17, yani 23 karakter. Bu durumda, strncmp fonksiyonu, dizilerin ilk 23 karakterini karşılaştırır.
else if (strncmp(eax_2, &var_30, 0x17) == 0)
Bu yazıda, UnCrackable2 uygulamasının dinamik analizini adım adım gerçekleştirdik. Frida ve Binary Ninja gibi güçlü araçlar kullanarak, uygulamanın yerel kütüphanesi olan libfoo.so dosyasını detaylı şekilde inceledik.
Şifre doğrulama mekanizmasını analiz ederek, strncmp fonksiyonunun nasıl çalıştığını ve hangi parametrelerle işlem yaptığını tespit ettik. Frida scripti sayesinde, uygulamanın sakladığı gizli ifadeyi ele geçirdik ve doğrulama sürecini başarıyla manipüle ettik.