Bu yazımızda android apk dosyalarının smali kodlarını düzenleyerek kod akışını istediğimiz şekilde nasıl nasıl yönlendirebileceğimizi yada ek kod parçalarını nasıl ekleyebileceğimize değineceğiz.
Bu yazıyı okumadan önce smali kodları hakkında daha fazla bilgi edinmek için daha önce yazdığım smali kodlarını okumak yazıma bakabilirsiniz.
Burada yapacağımız örnekte mobil bir zararlı yazılımın decode fonksiyonunu düzenleyerek bu fonksiyonun çıktılarını alacağız ve zararlının hedeflediği uygulamaları kendi içerisinde sakladığı komuta kontrol sunucusunun domain adresini ve kurban cihazda ne yapmak istediği ile ilgili bilgileri elde edeceğiz.
Öncelikle burada kullanacağım zararlı yazılım bilgileri aşağıdaki gibidir.
Paket adı | p0da2e7fa.pfabcaa97.p531a72f4 |
SHA256 | 2da55aa65107c4514a877c4f9b7b951c17cc2b96e54a2520989d98114c0c3408 |
MD5 | 2d648c6cfbbab944165b7d20cdce9f7a |
Link | Download |
Uygulama hakkında kısa bir bilgi vermek gerekirse, uygulama 21 haziran 2020 tarihinde babalar gününe özel 20GB internet hediyesi vermek vaadi ile kurbanlarına yüklemeye çalışan yüklendikten sonra da bir çok zararlı işlem gerçekleştiren bir zararlı yazılımdır.
Jadx-gui ile apk dosyasına bakacak olursak aşağıdaki gibi bir decode fonksiyonu görebiliriz.
package com.decryptstringmanager;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class DecryptString {
public static String decipher(String str) throws Exception {
SecretKeySpec secretKeySpec = new SecretKeySpec(SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(new PBEKeySpec("IJucsmcEqkNX0EYdIVB58lRX9Hugwa9E".toCharArray(), "IJucsmcEqkNX0EYdIVB58lRX9Hugwa9E".getBytes(), 128, 256)).getEncoded(), "AES");
Cipher instance = Cipher.getInstance("AES/ECB/PKCS5Padding");
instance.init(2, secretKeySpec);
return new String(instance.doFinal(toByte(str)));
}
public static String decryptString(String str) {
try {
return decipher(str);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String[] decryptStringArray(String[] strArr) {
String[] strArr2 = new String[strArr.length];
for (int i = 0; i < strArr.length; i++) {
strArr2[i] = decryptString(strArr[i]);
}
return strArr2;
}
private static byte[] toByte(String str) {
int length = str.length() / 2;
byte[] bArr = new byte[length];
for (int i = 0; i < length; i++) {
int i2 = 2 * i;
bArr[i] = Integer.valueOf(str.substring(i2, i2 + 2), 16).byteValue();
}
return bArr;
}
}
Bu kodda bizim için önemli olan kısım decryptString fonksiyonu bu fonksiyon gelen şifreli verileri decode ederek geri döndürüyor.
Uygulamayı düzenlemek için ilk ihtiyacımız olan smali kodlarını elde etmek bunun için apktool aracını kullanacağız. Aşağıdaki komut ile smali kodlarını elde edebiliriz.
apktool d -r sample.apk
Komut çalıştıktan sonra sample/smali/com/decryptstringmanager dizininde DecryptString.smali isminde bizim jadx-gui de bulduğumuz fonksiyonu görebiliriz. Bu dosyayı açtığımızda smali kodlarını göreceğiz. Bizim düzenlememiz gereken fonksiyonun smali kodları aşağıdaki gibidir.
.method public static decryptString(Ljava/lang/String;)Ljava/lang/String;
.locals 0
:try_start_0
invoke-static {p0}, Lcom/decryptstringmanager/DecryptString;->decipher(Ljava/lang/String;)Ljava/lang/String;
move-result-object p0
:try_end_0
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
return-object p0
:catch_0
move-exception p0
invoke-virtual {p0}, Ljava/lang/Exception;->printStackTrace()V
const/4 p0, 0x0
return-object p0
.end method
Burada fonksiyona zarar vermeden ekleme yapmamız gerekmekte. Yukarıdaki koda bakacak olursak try catch bloklarının nerede başlayıp bittiğini görebiliyoruz. Bizim yapmamız gereken try içerisinde decipher fonksiyonu çağrıldıktan sonra gelen plain texti geri döndürmeden önce loga yazdırmak.
const-string v0, "muraterdem_org"
nvoke-static {v0, p0}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
Yukarıdaki kod parçacığı bize bunun için yardımcı olacaktır burada v0 log başlığı p0 ise şifresi çözülmüş olan stringler. bu kod parçasını fonksiyonumuza eklediğimizde fonksiyonumuzun son hali aşağıdaki gibi olacaktır. Dikkat etmemiz gereken fonksiyonda local değişken kullanılmamış olması ve biz log yazarken bir tane kullandık bunun için .locals değerini 1 olarak değiştireceğiz.
method public static decryptString(Ljava/lang/String;)Ljava/lang/String;
.locals 1
:try_start_0
invoke-static {p0}, Lcom/decryptstringmanager/DecryptString;->decipher(Ljava/lang/String;)Ljava/lang/String;
move-result-object p0
:try_end_0
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
const-string v0, "muraterdem_org"
invoke-static {v0, p0}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
return-object p0
:catch_0
move-exception p0
invoke-virtual {p0}, Ljava/lang/Exception;->printStackTrace()V
const/4 p0, 0x0
return-object p0
.end method
Artık yapmamız gereken uygulamayı tekrar build edip imzalamak. Build işlemi için aşağıdaki komutu çalıştırabilirsiniz.
apktool b sample
Burada sample bizim az önce apkyı decode ettiğimizdeki klasör. bu işlem bittikten sonra hata alıyorsanız decode sırasında “-r” parametresini kullanmadığınız için olabilir. Eğer hata almadan buraya kadar geldiyseniz bundan sorasında apk imzalamaya geçebiliriz. APK imzalamak için keytool aracı ile bir sertifika oluşturuyoruz aşağıdaki komutu çalıştırdıktan sonra sizden bazı girdiler isteyecektir. burada belirlediğiniz şifre bir sonraki adımda kullanılacak. İstediği diğer bilgilerden birini girdikten sonra diğerlerini boş geçebilirsiniz.
keytool -genkey -v -keystore key.jks -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
İşlem bittikten sonra key.jks dosyası oluştuğunu göreceksiniz. Bu dosyayı sample/dist dizinine taşıyalım burada bulunan sample apk editledikten sonra build ettiğimiz apk dosyasıdır. key.jks dosyası ile aynı dizine attıktan sonra aşağıdaki komut ile imzalama işlemini yapabiliriz.
apksigner sign --ks key.jks sample.apk
Burada farklı platformlarda çalışıyorsanız farklı araçlar ile imzalama yapabilirsiniz. ben ubuntu üzerinde çalıştığım için apksinger aracını kullanıyorum.
İmzalama işlemi bittikten sonra düzenlemeyi doğru yapıp yapmadığımızı test etmek için jadx-gui ile baktığımızda fonksiyona bizim eklediğimiz noktaların geldiğini görebiliriz.
public static String decryptString(String str) {
try {
String decipher = decipher(str);
Log.i("muraterdem_org", decipher);
return decipher;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
Şimdi aşağıdaki komutu terminalde çalıştıralım daha sonra ise emülatörde uygulamamızı çalıştıralım.
adb logcat | grep "muraterdem_org" >> log.txt
Bir süre bekledikten sonra ctrl+c ile durduralım ve log.txt dosyamıza bakalım. Bir çok gereksiz çıktı göreceğiz ancak bunları incelediğimizde aşağıdaki gibi önemli bilgileri de elde edebiliriz.
Smali kodları ile uygulamaların kaynak kodunu düzenleyebildiğimiz için bu yöntemi zararlı yazılım analizlerinde, bir uygulamaya pentest sırasında yada anti vm veya root kontrol gibi analizi zorlaştırabilecek teknikleri bypass ederken kullanabilirisiniz.