Android Tersine Mühendislik: FLAG_SECURE, LSPosed ve DisableFlagSecure

2 Haziran 2026
android androidreverse-engineeringflag-securelsposed
22 Dakika
4258 Kelime
FLAG_SECURE, LSPosed ve DisableFlagSecure konusunu anlatan siyah beyaz karikatür çizimi

Android’de ekran görüntüsü ve ekran kaydı kısıtlamaları çoğu zaman FLAG_SECURE üzerinden uygulanır. İlk bakışta bu yalnızca “ekran görüntüsü almayı engelleyen bir pencere bayrağı” gibi görünür. Fakat modern Android mimarisinde bu mekanizma; pencere yöneticisi, Surface katmanları, ekran yakalama altyapısı, sanal ekranlar, uygulama callback’leri, Android sürüm farkları ve üretici ROM’larıyla birlikte çalışan çok katmanlı bir güvenlik modelidir.

Bu yazıda FLAG_SECURE davranışını eski Smali / services.jar müdahalesinden modern LSPosed runtime hook yaklaşımına kadar inceleyeceğiz. Referans olarak DisableFlagSecure.java dosyasındaki mimari ele alınacak; özellikle modülün system server, ScreenCapture, Android 14/15 tespit API’leri, sanal ekran akışları ve OEM framework katmanlarıyla nasıl ilişkilendiği açıklanacaktır.

Kaynak notu: Bu yazıda incelenen DisableFlagSecure.java dosyası, LSPosed ekosistemindeki LSPosed/DisableFlagSecure reposunda bulunan app/src/main/java/io/github/lsposed/disableflagsecure/DisableFlagSecure.java dosyasıdır. Yani burada bağımsız bir örnekten değil, doğrudan LSPosed DisableFlagSecure reposundaki uygulamadan hareketle mimariyi inceliyoruz.

Buradaki amaç, güvenlik kontrollerini bilinçsizce devre dışı bırakmayı teşvik etmek değil; Android framework’ün bu kararı nerede, neden ve nasıl verdiğini teknik olarak anlamaktır. FLAG_SECURE çoğu zaman kullanıcı mahremiyetini ve hassas verileri korumak için kullanılır. Bu nedenle konu izinli araştırma, kendi cihazında test ve Android güvenlik modeli analizi bağlamında ele alınmalıdır.

1. FLAG_SECURE Nedir ve Android’de Nerede Çalışır?

FLAG_SECURE, Android’de bir pencerenin içeriğini güvenli içerik olarak işaretlemek için kullanılan bir pencere bayrağıdır. Uygulama geliştiricisi bu bayrağı ilgili pencereye eklediğinde sistem, o pencerenin içeriğini ekran görüntüsü, ekran kaydı, harici ekran yansıtma ve bazı sanal ekran senaryolarında korunması gereken veri olarak değerlendirir.

Bu bayrak genellikle şu ekranlarda karşımıza çıkar:

  • bankacılık ve ödeme ekranları,
  • kimlik doğrulama veya parola giriş alanları,
  • sağlık ve kişisel veri içeren ekranlar,
  • özel mesajlaşma uygulamaları,
  • lisanslı veya DRM korumalı medya oynatıcıları,
  • kurumsal veri ve yönetim panelleri.

Buradaki kritik ayrım şudur: FLAG_SECURE uygulama tarafında ayarlanır, fakat etkisi uygulama sürecinde sınırlı kalmaz. Android framework bu bilgiyi pencere yöneticisi seviyesine taşır. Uygulama, pencereye “bu içerik secure” der; sistem ise bu bilginin screenshot, recording, display capture ve Surface katmanlarında nasıl uygulanacağını belirler.

Basitleştirilmiş akış şöyledir:

flowchart TD
    A["Uygulama pencereye FLAG_SECURE ekler"]
    B["WindowManager pencereyi secure olarak kaydeder"]
    C["WindowState güvenlik durumunu framework seviyesinde temsil eder"]
    D["Surface / ScreenCapture katmanı secure layer politikasını uygular"]
    E["Screenshot, screen recording, MediaProjection ve virtual display akışları etkilenir"]

    A --> B --> C --> D --> E

Yani FLAG_SECURE, yalnızca tek bir API çağrısı değildir. Android’in pencere yönetimi ve grafik altyapısına yayılan bir güvenlik sinyalidir. Bu nedenle konuyu incelerken yalnızca “uygulama bayrağı nerede koyuyor?” sorusu yeterli olmaz. Asıl önemli soru şudur: Android sistemi bu bayrağı hangi servislerde, hangi metotlarda ve hangi capture akışlarında yorumluyor?

Bu bakış açısı, DisableFlagSecure gibi LSPosed modüllerinin neden tek bir metoda odaklanmadığını da açıklar. Modern Android’de secure content kararı tek bir noktada verilmez; pencere durumu, Surface metadata’sı, capture argümanları, callback kayıtları ve üreticiye özel screenshot servisleri birlikte rol oynar.

2. Eski Yöntem: Smali / services.jar Müdahalesi

Android tersine mühendisliğinde eski yöntemlerden biri, sistem framework dosyalarını doğrudan değiştirmekti. Bu yaklaşımda genellikle services.jar gibi sistem bileşenleri decompile edilir, Smali kodları düzenlenir ve değiştirilmiş framework dosyası tekrar cihaza yerleştirilirdi.

Bu yöntemin temel mantığı şuydu:

  • sistemde FLAG_SECURE kontrolünün yapıldığı metot bulunur,
  • ilgili koşul veya dönüş değeri Smali seviyesinde değiştirilir,
  • framework yeniden paketlenir,
  • sistem bölümü değiştirilir veya patch uygulanır,
  • cihaz yeniden başlatılıp davranış test edilir.

Bu yaklaşım öğretici olabilir. Çünkü Android framework’ün iç yapısını doğrudan görmeyi sağlar. WindowManagerService, WindowState, Surface oluşturma akışları ve permission kontrolleri gibi noktalar Smali üzerinden takip edilebilir.

Fakat pratikte bu yöntem ciddi riskler taşır:

  • sistem dosyalarına kalıcı müdahale gerekir,
  • hatalı düzenleme bootloop’a neden olabilir,
  • OTA güncellemeleri değişikliği bozabilir,
  • Android sürümüne göre sınıf ve metot yapıları değişebilir,
  • üretici ROM’larında AOSP sınıf yapısı birebir bulunmayabilir,
  • geri dönüş her zaman kolay olmayabilir,
  • ART optimizasyonları ve hidden API değişimleri analizi zorlaştırabilir.

Özellikle Android 12 ve sonrası sürümlerde framework parçaları daha fazla ayrışmıştır. Screenshot, Surface, virtual display ve screen recording davranışları farklı sınıflara taşınmış; Android 14 ve Android 15 ile birlikte uygulamaların ekran yakalama olaylarını tespit edebilmesini sağlayan yeni callback mekanizmaları da eklenmiştir.

Bu nedenle Smali tabanlı tek nokta müdahalesi artık çoğu durumda kırılgan bir yaklaşımdır. Bir cihazda çalışan değişiklik, başka bir Android sürümünde veya başka bir üretici ROM’unda çalışmayabilir. Modern yaklaşımın doğduğu yer de burasıdır: sistemi fiziksel olarak değiştirmek yerine, çalışma zamanında framework metotlarının davranışını analiz etmek ve gerektiğinde hook zinciri üzerinden değiştirmek.

3. Modern Yöntem: LSPosed ve Runtime Hook Mantığı

LSPosed, Android runtime üzerinde çalışan ve belirli metotlara çalışma zamanında hook uygulanmasını sağlayan modern Xposed altyapılarından biridir. Smali yaklaşımından farklı olarak sistem dosyalarını kalıcı biçimde değiştirmez. Bunun yerine hedef metot bellekte yüklendiğinde, o metodun çağrı akışına müdahale eder.

Modern libxposed mimarisinde modül, klasik Xposed yapısından daha düzenli bir yaşam döngüsüyle çalışır. İncelenen dosyada ana sınıf şu yapıdadır:

1
public class DisableFlagSecure extends XposedModule

Bu sınıf, LSPosed tarafından sunulan yaşam döngüsü metotlarını kullanır. En önemli iki aşama şunlardır:

  • onSystemServerStarting: system server başlarken framework servislerine yönelik hook’lar hazırlanır.
  • onPackageReady: belirli paketler yüklendiğinde veya hazır hale geldiğinde paket bazlı hook’lar uygulanır.

Runtime hook modelini basitleştirirsek akış şöyledir:

flowchart TD
    A["Hedef sınıf ClassLoader üzerinden bulunur"]
    B["Hedef metot reflection ile seçilir"]
    C["Metot çağrısı hook zincirine alınır"]
    D{"Müdahale gerekiyor mu?"}
    E["Argümanlar veya dönüş değeri değiştirilir"]
    F["Orijinal metoda devam edilir"]
    G["Sonuç framework'e döner"]

    A --> B --> C --> D
    D -- "Evet" --> E --> G
    D -- "Hayır" --> F --> G

Bu model, Smali yaklaşımına göre daha esnektir. Çünkü Android sürümüne göre farklı sınıf adları seçilebilir, üretici ROM’larına özel hook’lar koşullu uygulanabilir, bazı çağrılarda orijinal davranış korunabilir ve modül kapsamı belirli sistem paketleriyle sınırlandırılabilir.

Ancak bu esneklik, otomatik olarak risksiz olduğu anlamına gelmez. LSPosed sistemin hassas noktalarında çalışır. Yanlış hook noktası, yanlış paket kapsamı veya hatalı dönüş değeri framework davranışını bozabilir. Bu nedenle modern runtime hook yaklaşımında en önemli beceri, “hangi metodu değiştirebilirim?” sorusundan önce “hangi çağrı bağlamında değiştirmeliyim?” sorusunu sormaktır.

4. DisableFlagSecure Kodunun Genel Mimarisi

DisableFlagSecure.java dosyası, tek bir metoda hook atan basit bir modül değildir. Kod, Android’in ekran yakalama güvenlik modelini birden fazla katmanda ele alır. Dosyanın mimarisi incelendiğinde dört ana blok öne çıkar:

  • modül yaşam döngüsü,
  • system server hook’ları,
  • paket bazlı hook’lar,
  • yardımcı hook/deoptimization fonksiyonları.

Bu bolumu yalnizca kavramsal anlatmak yeterli degil. Dosyanin nasil dusundugunu anlamak icin kodu parcalara ayirarak okumak daha saglikli olur. Aşağıdaki bloklar, dosyadaki ana akışları sadeleştirilmiş haliyle gösterir; amaç birebir kopya vermekten çok hangi kod parçasının hangi framework kararına dokunduğunu okumaktır.

4.1. Modül Sınıfı ve Yaşam Döngüsü

İlk olarak modül sınıfı XposedModule sınıfını genişletir. Bu, modern libxposed API’sine uygun bir yapıdır. Klasik Xposed modüllerinde görülen eski assets/xposed_init yaklaşımı yerine, modern LSPosed/libxposed tarafında modül daha düzenli bir yaşam döngüsüne sahiptir.

Basitleştirilmiş sınıf iskeleti şöyle düşünülebilir:

1
public class DisableFlagSecure extends XposedModule {
2
private static DisableFlagSecure module;
3
4
@Override
5
public void onModuleLoaded(ModuleLoadedParam param) {
6
module = this;
7
}
8
9
@Override
10
public void onSystemServerStarting(SystemServerStartingParam param) {
11
ClassLoader classLoader = param.getClassLoader();
12
// Framework ve system_server hook'lari burada kurulur.
13
}
14
15
@Override
4 collapsed lines
16
public void onPackageReady(PackageLoadedParam param) {
17
// SystemUI ve OEM screenshot paketleri burada ele alinir.
18
}
19
}

Burada module = this gibi küçük görünen bir atama bile önemlidir. Çünkü hook fonksiyonları farklı yardımcı metotlara dağıldığında loglama, API erişimi veya ortak yardımcı fonksiyonlar için modül örneğine ihtiyaç duyulabilir. Bu tarz modüllerde ana sınıf yalnızca giriş noktası değil, aynı zamanda bütün hook ağının koordinasyon merkezidir.

Kodun merkezi ise onSystemServerStarting metodudur. Bu metot çağrıldığında param.getClassLoader() ile system server class loader alınır. Ardından hook fonksiyonları sırayla çağrılır. Bu sıralama, modülün tek bir güvenlik kontrolüne değil, ekran yakalama zincirinin farklı halkalarına dokunduğunu gösterir.

Kodun mantığı sadeleştirilirse onSystemServerStarting içinde ana akış kabaca şu hale gelir:

1
@Override
2
public void onSystemServerStarting(SystemServerStartingParam param) {
3
ClassLoader classLoader = param.getClassLoader();
4
5
deoptimizeCriticalMethods(classLoader);
6
7
hookScreenRecordingCallback(classLoader); // Android 15+
8
hookScreenCaptureObserver(classLoader); // Android 14+
9
hookScreenCapture(classLoader); // Surface / ScreenCapture
10
hookActivityManagerPermission(classLoader); // Eski surum uyumlulugu
11
hookDisplayControl(classLoader); // Display olusturma
12
hookVirtualDisplayAdapter(classLoader); // MediaProjection / virtual display
13
hookOemFrameworks(classLoader); // OneUI, HyperOS, OPlus vb.
14
hookWindowState(classLoader); // WindowState.isSecureLocked
15
}

Bu kod iskeletindeki sıralama çok şey anlatır. WindowState.isSecureLocked() önemli olsa da en sona yakın kurulması tesadüf değildir; modül önce capture, display, callback ve OEM yüzeylerini hazırlar. Çünkü modern Android’de güvenlik kararı yalnızca “pencere secure mı?” sorusundan ibaret değildir.

Akış diyagramı olarak bakarsak:

flowchart TD
    A["System server class loader alınır"]
    B["Kritik framework metotları deoptimize edilir"]
    C["Android 15+ screen recording callback noktaları ele alınır"]
    D["Android 14+ screenshot observer noktaları ele alınır"]
    E["ScreenCapture / Surface capture akışı hazırlanır"]
    F["Eski sürümler için permission kontrolü değerlendirilir"]
    G["DisplayControl ve VirtualDisplayAdapter akışları ele alınır"]
    H["OneUI, HyperOS ve OPlus gibi OEM katmanları hedeflenir"]
    I["WindowState secure kontrolü hook zincirine alınır"]

    A --> B --> C --> D --> E --> F --> G --> H --> I

Bu yapı bize önemli bir şey söyler: FLAG_SECURE davranışı yalnızca WindowState.isSecureLocked() ile açıklanamaz. Evet, bu metot çok kritik bir noktadır; fakat tek başına yeterli değildir. Capture argümanları, display oluşturma akışları, screen recording callback’leri ve OEM screenshot sınıfları da davranışı etkileyebilir.

4.2. Hook Yardımcısı: Tekrarlanan Pattern

Dosyada farklı sınıflara hook uygulanırken genellikle aynı düşünce kalıbı tekrarlanır:

1
private void hookWindowState(ClassLoader classLoader) throws Exception {
2
Class<?> windowStateClass =
3
classLoader.loadClass("com.android.server.wm.WindowState");
4
5
hookMethods(
6
windowStateClass,
7
chain -> false,
8
"isSecureLocked"
9
);
10
}

Bu küçük blokta üç ayrı karar vardır:

  • classLoader.loadClass(...): Hedef sınıf system server class loader içinde aranır.
  • "isSecureLocked": Hook uygulanacak metot adı seçilir.
  • chain -> false: Orijinal metot yerine döndürülecek değer belirlenir.

Kodun okuması şudur: Android framework “Bu pencere secure mı?” diye sorduğunda, bu hook belirli bağlamlarda “hayır” cevabını üretmeye çalışır. Ancak dosyanın tamamı bundan ibaret değildir. Daha yeni Android sürümlerinde ve bazı ROM’larda capture akışı başka metotlardan da geçtiği için modül aynı pattern’i farklı sınıflara tekrar uygular.

Modern libxposed tarafında bu yapı interceptor zinciri gibi düşünülebilir:

1
hookMethods(targetClass, chain -> {
2
// Gerekirse argumanlar okunur.
3
// Gerekirse orijinal metoda devam edilir.
4
// Gerekirse farkli sonuc dondurulur.
5
return false;
6
}, "targetMethodName");

Buradaki kritik nokta şudur: Her hook “orijinal metodu tamamen iptal et” anlamına gelmez. Bazı yerlerde sabit değer dönmek mantıklıdır; bazı yerlerde argümanı değiştirmek, bazı yerlerde ise yalnızca belirli çağrı bağlamlarında müdahale etmek gerekir.

4.3. WindowState: En Bilinen Ama Tek Başına Yeterli Olmayan Nokta

WindowState, Android pencere yöneticisinin her pencere için tuttuğu framework seviyesindeki temsildir. FLAG_SECURE bilgisi uygulama tarafından pencereye eklense de sistem bunu WindowState üzerinden yorumlar.

Basit bir hook örneği şöyle görünür:

1
Class<?> windowStateClass =
2
classLoader.loadClass("com.android.server.wm.WindowState");
3
4
hookMethods(windowStateClass, chain -> false, "isSecureLocked");

Bu blok tek başına okunduğunda çok güçlü görünür: isSecureLocked() çağrısı false döndürürse pencere secure kabul edilmez. Fakat gerçek framework davranışı daha hassastır. Bazı çağrılar Surface oluşturma veya pencere metadata’sını hazırlama aşamasından gelebilir. Bu bağlamlarda orijinal davranışı tamamen bozmak, yalnızca screenshot davranışını değil, pencere oluşturma akışını da etkileyebilir.

Bu yüzden daha dikkatli bir okuma şu mantığı arar:

1
hookMethods(windowStateClass, chain -> {
2
if (isSurfaceInitializationCall()) {
3
return chain.proceed();
4
}
5
6
return false;
7
}, "isSecureLocked");

Buradaki fikir şudur: Hook, her çağrıyı aynı kabul etmez. Çağrının nereden geldiğini anlamaya çalışır. Eğer çağrı Surface başlatma veya framework içi tutarlılık için gerekliyse orijinal metoda devam edilebilir. Eğer çağrı ekran yakalama kararına yaklaşıyorsa sonuç değiştirilebilir.

Bu ayrım, iyi bir LSPosed modülü ile kırılgan bir “her şeyi false yap” denemesi arasındaki farkı gösterir.

4.4. Android 14/15 Callback Noktaları

Android 14 ve 15 ile birlikte konu yalnızca secure layer yakalama meselesi olmaktan çıktı. Sistem, uygulamaların screenshot veya screen recording olaylarını gözlemleyebilmesi için callback mekanizmaları da sunuyor.

Bu nedenle dosyada şu tarz hook noktaları bulunur:

1
Class<?> atmsClass =
2
classLoader.loadClass("com.android.server.wm.ActivityTaskManagerService");
3
4
hookMethods(
5
atmsClass,
6
chain -> null,
7
"registerScreenCaptureObserver"
8
);

Bu blok Android 14 tarafındaki screenshot observer kaydına odaklanır. Okuma şekli şudur: Uygulama sisteme “ekran görüntüsü alınırsa bana haber ver” demeye çalıştığında, kayıt akışı etkisiz hale getirilebilir.

Android 15 tarafında benzer fikir screen recording callback için geçerlidir:

1
Class<?> wmsClass =
2
classLoader.loadClass("com.android.server.wm.WindowManagerService");
3
4
hookMethods(
5
wmsClass,
6
chain -> null,
7
"registerScreenRecordingCallback"
8
);

Buradaki chain -> null, “orijinal kayıt davranışını çalıştırma, geri bildirim üretme” anlamına gelen bir pattern olarak okunabilir. Bu iki hook, modülün yalnızca görüntünün yakalanıp yakalanmamasıyla değil, yakalama olayının uygulamaya bildirilip bildirilmemesiyle de ilgilendiğini gösterir.

4.5. ScreenCapture ve Capture Argümanları

WindowState pencerenin secure bilgisini tutar; fakat screenshot veya screen recording sırasında nihai davranış capture katmanına da bağlıdır. Bu yüzden dosyada SurfaceControl, ScreenCapture veya daha yeni sürümlerde ScreenCaptureInternal gibi sınıflara bakılır.

Bu bölümdeki kodların mantığı kabaca şu şekildedir:

1
Class<?> argsClass =
2
classLoader.loadClass("android.window.ScreenCapture$CaptureArgs");
3
4
Field captureSecureLayers =
5
argsClass.getDeclaredField("mCaptureSecureLayers");
6
7
captureSecureLayers.setAccessible(true);

Ardından capture isteği oluşturulurken ilgili argüman değiştirilebilir:

1
hookMethods(screenCaptureClass, chain -> {
2
Object args = chain.args()[0];
3
captureSecureLayers.setBoolean(args, true);
4
5
return chain.proceed();
6
}, "captureDisplay");

Bu kod okumasında önemli olan şey şudur: Modül yalnızca “pencere secure değil” demekle yetinmez; capture işlemine giden argümanların secure layer politikasını da inceler. Çünkü bazı Android sürümlerinde secure layer kararları capture isteğinin kendi parametreleri içinde tekrar değerlendirilir.

Daha yeni sürümlerde basit boolean yerine policy temelli alanlar görülebilir. Bu yüzden iyi bir kod incelemesi yalnızca metot adlarına değil, alan adlarının Android sürümüne göre nasıl değiştiğine de bakmalıdır:

flowchart LR
    A["Eski model"]
    B["mCaptureSecureLayers"]
    C["boolean karar"]
    D["Yeni model"]
    E["mSecureContentPolicy"]
    F["policy / enum benzeri karar"]

    A --> B --> C
    D --> E --> F

Bu ayrım, Android’in güvenlik modelinin zaman içinde daha ayrıntılı hale geldiğini gösterir.

4.6. Paket Bazlı Hook’lar: SystemUI ve OEM Screenshot Süreçleri

Kodun ikinci önemli yaşam döngüsü metodu onPackageReady metodudur. Burada modül, belirli paketler hazır olduğunda farklı hook’ları uygular. Özellikle şu paketler dikkat çeker:

  • com.android.systemui,
  • com.miui.screenshot,
  • com.oplus.screenshot,
  • com.oplus.appplatform,
  • com.flyme.systemuiex.

Bu paketlerin seçilmesi tesadüf değildir. Android’de screenshot davranışı her zaman yalnızca framework içinde kalmaz. SystemUI, üretici screenshot servisi veya uzun screenshot altyapısı bazı cihazlarda ayrı paketler üzerinden çalışabilir. Bu nedenle modül, yalnızca system server’a değil, paket bazlı screenshot bileşenlerine de bakar.

Basitleştirilmiş onPackageReady okuması şöyle yapılabilir:

1
@Override
2
public void onPackageReady(PackageLoadedParam param) {
3
String packageName = param.getPackageName();
4
ClassLoader classLoader = param.getClassLoader();
5
6
switch (packageName) {
7
case "com.android.systemui":
8
hookSystemUI(classLoader);
9
break;
10
11
case "com.miui.screenshot":
12
hookHyperOSScreenshot(classLoader);
13
break;
14
15
case "com.oplus.screenshot":
13 collapsed lines
16
case "com.oplus.appplatform":
17
hookOplusScreenshot(classLoader);
18
break;
19
20
case "com.flyme.systemuiex":
21
hookFlymeScreenshot(classLoader);
22
break;
23
24
default:
25
hookScopeWarning(classLoader);
26
break;
27
}
28
}

Bu blok, modülün kapsam yönetimini gösterir. system_server tarafında kurulan hook’lar framework kararlarına dokunurken, onPackageReady tarafı üretici screenshot uygulamalarının kendi ek kontrollerini ele alır. Yani hook mimarisi süreç bazlıdır: her sınıf her class loader içinde aranmaz; doğru paket geldiğinde doğru hook devreye girer.

Kodda ayrıca yanlış kapsam kullanımını yakalamaya çalışan bir davranış da vardır. onPackageReady içinde beklenmeyen paketler için hookOnResume çağrılır. Bu hook, kullanıcıya modülün yanlış scope ile kullanıldığını belirten bir uyarı göstermeye yöneliktir. Bu ayrıntı önemlidir; çünkü LSPosed modüllerinde scope seçimi doğrudan kararlılığı etkiler. Bir modülün rastgele uygulama süreçlerinde çalıştırılması beklenmedik sonuçlar doğurabilir.

4.7. OEM Hook’ları: Aynı Amaç, Farklı Dönüş Değerleri

Üretici ROM’larında sınıf ve metot adları değişebilir. Bu yüzden kod, OEM hook’larını genellikle ayrı try/catch bloklarında kurar. Örneğin Xiaomi/HyperOS tarafında mantık şu şekilde okunabilir:

1
try {
2
Class<?> implClass =
3
classLoader.loadClass("com.android.server.wm.WindowManagerServiceImpl");
4
5
hookMethods(
6
implClass,
7
chain -> false,
8
"notAllowCaptureDisplay"
9
);
10
} catch (ClassNotFoundException ignored) {
11
// Bu ROM'da Xiaomi/HyperOS sinifi yoksa normal kabul edilir.
12
}

Burada metot adı zaten niyeti gösterir: notAllowCaptureDisplay. Yani sistem “display capture’a izin verme” sonucunu üretiyorsa, hook bunun false dönmesini hedefler.

Samsung/OneUI tarafında mantık tersine kurulabilir:

1
try {
2
Class<?> controllerClass =
3
classLoader.loadClass("com.samsung.android.server.wm.WmScreenshotController");
4
5
hookMethods(
6
controllerClass,
7
chain -> true,
8
"canBeScreenshotTarget"
9
);
10
} catch (ClassNotFoundException ignored) {
11
// Samsung sinifi yoksa bu hook atlanir.
12
}

Burada dönüş değerinin true olması gerekir; çünkü soru ters kurulmuştur: “Bu pencere screenshot hedefi olabilir mi?” Bu örnek, kod incelemesinde yalnızca “true mu false mu dönüyor?” diye bakmanın yetmeyeceğini gösterir. Metot adının semantiği okunmalıdır.

OPlus/ColorOS tarafında ise uzun ekran görüntüsü veya üretici screenshot altyapısı ayrı sınıflarda kontrol yapabilir:

1
try {
2
Class<?> longshotClass =
3
classLoader.loadClass("com.android.server.wm.OplusLongshotMainWindow");
4
5
hookMethods(
6
longshotClass,
7
chain -> false,
8
"hasSecure"
9
);
10
} catch (ClassNotFoundException ignored) {
11
// OPlus/ColorOS sinifi yoksa normaldir.
12
}

Bu üç örnek aynı mimari dersi verir:

  • Xiaomi tarafında “izin verme” anlamındaki metot false yapılır.
  • Samsung tarafında “hedef olabilir mi?” anlamındaki metot true yapılır.
  • OPlus tarafında “secure var mı?” anlamındaki metot false yapılır.

Yani başarılı hook yazımı, her ROM’da aynı dönüş değerini zorlamak değil, hedef metodun sorduğu soruyu doğru okumaktır.

Kod incelemesinde dikkat edilmesi gereken bir diğer nokta, hook fonksiyonlarının çoğunun try/catch bloklarıyla çağrılmasıdır. Özellikle OEM sınıflarında ClassNotFoundException çoğu zaman normal kabul edilir. Çünkü Samsung’a özel bir sınıf Xiaomi cihazda bulunmayabilir; OPlus sınıfı AOSP cihazda yüklenmeyebilir. Kod bu durumu hata gibi değil, koşullu uyumluluk gereği gibi ele alır.

1
try {
2
hookOneUI(classLoader);
3
} catch (ClassNotFoundException ignored) {
4
// OneUI olmayan cihazlarda beklenen durum.
5
}
6
7
try {
8
hookHyperOS(classLoader);
9
} catch (ClassNotFoundException ignored) {
10
// HyperOS/MIUI olmayan cihazlarda beklenen durum.
11
}
12
13
try {
14
hookOplus(classLoader);
15
} catch (ClassNotFoundException ignored) {
2 collapsed lines
16
// OPlus olmayan cihazlarda beklenen durum.
17
}

Bu try/catch yapıları hata saklamak için değil, ROM çeşitliliğini yönetmek için kullanılır. Bir sınıfın bulunmaması çoğu zaman “hook başarısız oldu” değil, “bu cihazda o üretici katmanı yok” anlamına gelir.

Yani DisableFlagSecure.java mimarisi üç ilkeye dayanır:

  • Android sürümüne göre farklı framework noktalarını seçmek,
  • üretici ROM’larında sınıf bulunmamasını normal kabul etmek,
  • tek bir bypass noktası yerine çok katmanlı capture kararlarını analiz etmek.

Bu nedenle dosya, basit bir “metodu false yap” örneğinden çok daha zengindir. System server yaşam döngüsünü, class loader kullanımını, reflection tabanlı metot seçimini, callback kayıtlarını, capture argümanlarını, sanal ekran davranışını ve OEM framework farklarını aynı mimari içinde birleştirir.

5. System Server Hook’ları

Android’de system_server, sistemin en kritik süreçlerinden biridir. WindowManager, ActivityTaskManager, ActivityManager, DisplayManager ve benzeri servisler burada çalışır. FLAG_SECURE gibi pencere ve ekran yakalama davranışları da büyük ölçüde bu servisler tarafından yorumlanır.

DisableFlagSecure kodundaki system server hook’ları bu nedenle modülün ana omurgasını oluşturur. Uygulama pencereye FLAG_SECURE ekler, fakat bu bayrağın screenshot veya recording sırasında nasıl uygulanacağına system server tarafındaki framework sınıfları karar verir.

Bu bölümde en bilinen hedef WindowState sınıfıdır. WindowState, Android pencere yöneticisinin her pencere için tuttuğu framework seviyesindeki temsildir. Pencerenin bayrakları, görünürlüğü, Surface ilişkisi ve güvenlik durumu bu yapı üzerinden okunur.

WindowState.isSecureLocked() metodu, pencerenin secure kabul edilip edilmeyeceğini belirleyen kritik kontrol noktalarından biridir. Ancak incelenen kod bu metoda kör biçimde sabit bir değer döndürmez. Özellikle Android 14 ve sonrası için StackWalker, daha eski sürümler için stack trace kontrolü kullanılır. Kod, çağrının createSurfaceLocked veya setInitialSurfaceControlProperties gibi framework içi yüzey oluşturma akışlarından gelip gelmediğini kontrol eder. Bu çağrılarda orijinal metoda devam edilir.

Bu incelik önemlidir. Çünkü Android’in Surface oluşturma aşamasında bazı metadata’ların doğru kurulması gerekebilir. Her bağlamda aynı dönüş değerini zorlamak, framework kararlılığını bozabilir. Modülün yaptığı şey, sistemin tüm secure bilgisini yok saymaktan çok, capture ile ilgili karar bağlamlarını hedeflemeye çalışmaktır.

System server tarafındaki diğer önemli noktalar şunlardır:

  • ActivityTaskManagerService: Android 14 ve sonrası screenshot observer kayıtlarını ele alır.
  • WindowManagerService: Android 15 ve sonrası screen recording callback mekanizmasıyla ilişkilidir.
  • ActivityManagerService: Android 12-13 gibi eski sürümlerde belirli permission kontrolleriyle ilişkilidir.
  • DisplayControl: display ve virtual display oluşturma davranışlarında rol oynar.
  • VirtualDisplayAdapter: MediaProjection ve virtual display akışlarında devreye girer.

Bu yapı, FLAG_SECURE analizinin neden tek katmanlı olmadığını açıkça gösterir. Bir pencerenin secure olup olmadığı, capture isteğinin secure layer’ları nasıl ele aldığı, uygulamanın screenshot olayını gözlemleyip gözlemleyemediği ve görüntünün sanal ekrana aktarılıp aktarılmadığı farklı framework noktalarında değerlendirilir.

6. ScreenCapture ve Secure Layer Mantığı

Android’de ekran görüntüsü almak, ekrandaki piksellerin düz biçimde kopyalanması değildir. Grafik katmanları, Surface yapıları ve capture argümanları devreye girer. Secure olarak işaretlenmiş layer’lar normal şartlarda capture sonucuna dahil edilmez, karartılır veya yakalama işleminden dışlanır.

Bu nedenle ScreenCapture ve SurfaceControl sınıfları FLAG_SECURE davranışının merkezindedir. İncelenen kod Android sürümüne göre farklı sınıfları hedefler:

  • Android 12-13 tarafında android.view.SurfaceControl,
  • Android 14 ve sonrası için android.window.ScreenCapture,
  • daha yeni sürümlerde android.window.ScreenCaptureInternal.

Bu sürüm ayrımı önemlidir. Çünkü Android framework geliştikçe capture altyapısı farklı sınıflara taşınmıştır. Eski sürümde çalışan bir hook noktası yeni sürümde hiç çağrılmayabilir. Bu nedenle kod, Build.VERSION.SDK_INT ve daha yeni sürümler için SDK_INT_FULL kontrolleriyle sınıf seçimini koşullandırır.

Kodda capture argümanları içinde secure layer davranışını etkileyen alanlara erişilir. Bazı sürümlerde bu alan mCaptureSecureLayers olarak karşımıza çıkar. Daha yeni sürümlerde ise mSecureContentPolicy gibi policy temelli bir alan kullanılır. Bu fark, Android’in basit boolean kararından daha ayrıntılı policy modeline doğru evrildiğini gösterir.

Bu bölümün ana fikri şudur:

flowchart LR
    A["WindowState"]
    B["Pencerenin secure olup olmadığını söyler"]
    C["ScreenCapture"]
    D["Capture isteğinin secure layer'ları nasıl ele alacağını belirler"]
    E["Nihai yakalama davranışı"]

    A --> B --> E
    C --> D --> E

Bu iki nokta birbirini tamamlar. Yalnızca pencere güvenlik bilgisini değiştirmek bazı cihazlarda yeterli olmayabilir; çünkü capture altyapısı secure layer’ları ayrıca filtreleyebilir. Benzer şekilde yalnızca capture argümanına bakmak da yeterli değildir; çünkü pencere metadata’sı farklı framework kararlarında tekrar kullanılabilir.

Kodun nativeCaptureDisplay ve nativeCaptureLayers gibi yerel capture girişlerine yakın metotları hedeflemesi de bu yüzden anlamlıdır. Bu noktalar, screenshot veya display capture işleminin framework’ten native grafik altyapısına doğru indiği kritik eşiklerdir.

7. Android 14/15 ile Gelen Tespit API’leri

Modern Android sürümlerinde güvenlik modeli yalnızca “secure içeriği yakalama” meselesi değildir. Sistem, uygulamaların screenshot veya screen recording olaylarından haberdar olmasını da sağlayabilir.

Android 14 tarafında screenshot tespiti için observer tabanlı yapı öne çıkar. ActivityTaskManagerService içindeki registerScreenCaptureObserver, uygulamaların ekran görüntüsü olaylarını gözlemlemesine imkan tanıyan mekanizmanın framework tarafındaki önemli noktalarından biridir.

Android 15 tarafında screen recording tespiti daha da belirgin hale gelir. WindowManagerService içindeki registerScreenRecordingCallback, ekran kaydıyla ilgili callback kayıtlarının yönetildiği önemli bir noktadır.

Bu değişim Android güvenlik modelinin evrimini gösterir:

flowchart LR
    A["Eski yaklaşım"]
    B["Secure içerik yakalanmasın"]
    C["Yeni yaklaşım"]
    D["Secure içerik yakalanmasın"]
    E["Yakalama olayı uygulamaya bildirilebilsin"]

    A --> B
    C --> D
    C --> E

Bu nedenle Android 14/15 sonrası analizlerde yalnızca screenshot çıktısına bakmak yeterli değildir. Uygulamanın ekran görüntüsü veya ekran kaydı olayını algılayıp algılamadığı da ayrı bir araştırma konusudur.

Bu tespit API’leri hassas uygulamalar için önemlidir. Uygulama, ekran görüntüsü alındığını fark ettiğinde kullanıcıya uyarı gösterebilir, oturumu sonlandırabilir, içeriği geçici olarak gizleyebilir veya güvenlik log’u oluşturabilir.

DisableFlagSecure mimarisinin bu callback noktalarını da dikkate alması, modülün yalnızca capture sonucuyla değil, capture olayının uygulamaya bildirilmesiyle de ilgilendiğini gösterir. Bu ayrım, modern Android tersine mühendisliğinde giderek daha önemli hale gelmektedir.

8. Sanal Ekran, MediaProjection ve Display Hook’ları

Ekran yakalama yalnızca klasik screenshot mekanizmasından ibaret değildir. Android’de görüntü farklı yollarla başka bir hedefe aktarılabilir:

  • MediaProjection ile ekran kaydı,
  • virtual display oluşturma,
  • Wi-Fi Display,
  • overlay display,
  • harici ekran yansıtma,
  • üreticiye özel uzun screenshot sistemleri.

Bu nedenle FLAG_SECURE analizinde display altyapısı da dikkate alınmalıdır. İncelenen kodda DisplayControl ve VirtualDisplayAdapter sınıflarına yönelik hook’lar bulunur.

DisplayControl, display oluşturma ve yönetme süreçlerinde rol oynar. Android sürümüne göre createDisplay veya createVirtualDisplay gibi metotlar hedeflenir. Bu metotların argümanları içinde display’in güvenli kabul edilip edilmeyeceğini etkileyen boolean değerler bulunabilir.

VirtualDisplayAdapter ise sanal ekran oluşturma akışının önemli parçalarından biridir. MediaProjection tabanlı ekran kaydı veya ekran paylaşımı senaryolarında virtual display mekanizması devreye girebilir. Kod, bu akışta çağıranın kim olduğunu ve MediaProjection bağlamı olup olmadığını değerlendirerek davranışı koşullu ele alır.

Bu bölümün önemli mesajı şudur: Android açısından “ekran görüntüsü” ile “ekranı başka bir hedefe aktarma” teknik olarak aynı şey değildir, fakat secure content politikası ikisini de etkileyebilir.

Geniş güvenlik yüzeyi şöyle düşünülebilir:

flowchart TD
    A["Ekran yakalama yüzeyi"]
    A --> B["Screenshot"]
    A --> C["Screen recording"]
    A --> D["MediaProjection"]
    A --> E["Virtual display"]
    A --> F["External display"]
    A --> G["OEM longshot / screenshot services"]

Bir modül veya güvenlik analizi yalnızca klasik screenshot düğmesine odaklanırsa, MediaProjection veya virtual display gibi farklı akışları gözden kaçırabilir. DisableFlagSecure.java dosyasının display katmanına da bakması bu nedenle mimari olarak önemlidir.

9. OEM ROM’larda Ek Güvenlik Katmanları

AOSP, Android framework’ü anlamak için temel referanstır. Fakat gerçek cihazlarda üretici ROM’ları AOSP üzerine kendi servislerini ve kontrollerini ekler. Bu yüzden AOSP’de çalışan bir analiz, Samsung, Xiaomi, OPlus, Realme veya Flyme tabanlı cihazlarda eksik kalabilir.

İncelenen kodda üreticiye özel davranışlar açıkça görülür.

Xiaomi / HyperOS tarafında WindowManagerServiceImpl gibi üreticiye özel sınıflar dikkate alınır. Bu sınıflarda screenshot veya display capture izni üzerinde etkili olabilecek ek metotlar bulunabilir. Kodda HyperOS için ayrı bir hook fonksiyonu olması, AOSP WindowState kontrolünün bazı Xiaomi cihazlarda tek başına yeterli olmayabileceğini gösterir.

Samsung / OneUI tarafında WmScreenshotController sınıfı hedeflenir. Bu sınıf, bir pencerenin screenshot hedefi olup olamayacağını değerlendiren üreticiye özel karar noktalarından biridir. Ayrıca ScreenshotHardwareBuffer.containsSecureLayers gibi metotlar üzerinden secure layer bilgisi ayrıca kontrol edilebilir.

OPlus / Realme tarafında OplusLongshotMainWindow, OplusScreenCapture ve OPlus screenshot paketleri önem kazanır. Özellikle uzun screenshot veya üreticiye ait screenshot servisleri AOSP capture akışından farklı davranabilir.

Kod ayrıca şu paketleri özel olarak dikkate alır:

  • com.android.systemui,
  • com.miui.screenshot,
  • com.oplus.screenshot,
  • com.oplus.appplatform,
  • com.flyme.systemuiex.

Bu paketlerin varlığı, screenshot davranışının çoğu cihazda yalnızca framework içinde kalmadığını gösterir. SystemUI veya üretici screenshot uygulaması capture işleminin bir parçası olabilir.

OEM katmanları şu nedenlerle önemlidir:

  • üretici kendi screenshot servisini kullanabilir,
  • uzun screenshot sistemi AOSP’den bağımsız çalışabilir,
  • SystemUI davranışı değiştirilmiş olabilir,
  • secure content kontrolü ek framework sınıflarına taşınmış olabilir,
  • aynı Android sürümündeki iki cihaz farklı sonuç verebilir.

Bu nedenle sağlam bir analiz daima şu üç bilgiyi birlikte ister:

flowchart LR
    A["Android sürümü"]
    B["Cihaz üreticisi / ROM türü"]
    C["Screenshot veya recording akışını yöneten paketler"]
    D["Sağlıklı analiz"]

    A --> D
    B --> D
    C --> D

DisableFlagSecure.java dosyasının OEM hook’larını ayrı try/catch bloklarıyla uygulaması da bu esnekliği sağlar. Sınıf yoksa modül bunu çoğu zaman normal kabul eder; çünkü her cihazda her üretici sınıfının bulunması beklenmez.

10. Deoptimization Neden Gerekli?

Kodda ilk bakışta gözden kaçabilecek ama teknik olarak çok önemli bir bölüm deoptimizeSystemServer çağrısıdır.

Android Runtime, yani ART, performans için sık kullanılan metotları optimize eder. Bu optimizasyonlar normal uygulama çalışması için yararlıdır; ancak runtime hook mantığında bazı metotların beklenen şekilde yakalanmasını zorlaştırabilir. Bir metot inline edilmiş, optimize edilmiş veya farklı bir çağrı yoluna taşınmışsa hook beklenen etkiyi göstermeyebilir.

Bu nedenle bazı LSPosed modülleri kritik framework metotlarında deoptimization uygular. İncelenen kodda özellikle şu noktalar hedeflenir:

  • WindowStateAnimator.createSurfaceLocked,
  • WindowManagerService.relayoutWindow,
  • RootWindowContainer ile ilişkili synthetic lambda sınıfları,
  • DisplayContent ile ilişkili predicate/lambda yapıları.

Bu detay, modülün yalnızca “metodu bul ve dönüş değerini değiştir” seviyesinde olmadığını gösterir. Framework seviyesinde tutarlı davranış elde etmek için ART optimizasyonları, synthetic sınıflar ve çağrı zincirleri de hesaba katılır.

Deoptimization özellikle şu durumlarda önem kazanır:

  • metot çok sık çağrılıyorsa,
  • ART metodu optimize etmişse,
  • çağrı zinciri inline edilmişse,
  • framework sınıfları synthetic lambda yapıları kullanıyorsa,
  • hook uygulanmasına rağmen davranış değişmiyorsa.

Bu bölüm, Android tersine mühendisliğinde önemli bir ders verir: doğru metodu bulmak tek başına yeterli değildir. O metodun runtime’da nasıl derlendiğini, optimize edildiğini ve hangi çağrı bağlamlarında çalıştığını da anlamak gerekir

11. Sınırlamalar, Riskler ve Etik Kullanım

LSPosed tabanlı runtime hook yaklaşımı, Smali / services.jar müdahalesine göre daha esnek ve geri döndürülebilir bir model sunar. Ancak bu, yöntemin risksiz olduğu anlamına gelmez.

Teknik sınırlamalar şunlardır:

  • Android sürüm değişiklikleri sınıf ve metot adlarını değiştirebilir.
  • Üretici ROM’ları AOSP’den farklı davranabilir.
  • Bazı hook noktaları yalnızca belirli API seviyelerinde çalışır.
  • ART optimizasyonları hook etkisini değiştirebilir.
  • Yanlış scope seçimi uygulama çökmesine veya sistem kararsızlığına neden olabilir.
  • Hidden API ve private framework sınıfları sürümler arasında kararsızdır.
  • OEM screenshot servisleri cihazdan cihaza farklılık gösterebilir.

Etik açıdan konu daha da hassastır. FLAG_SECURE çoğu zaman kullanıcı mahremiyetini, finansal verileri, sağlık bilgilerini, kişisel mesajları veya lisanslı içerikleri korumak için kullanılır. Bu mekanizmayı anlamak meşru bir araştırma konusu olabilir; fakat izinsiz kullanım ciddi etik ve hukuki sorunlar doğurabilir.

Bu tür çalışmalar şu bağlamlarda anlamlıdır:

  • kendi cihazında güvenlik araştırması,
  • Android framework davranışını öğrenme,
  • izinli laboratuvar ortamı,
  • uygulama güvenlik modeli analizi,
  • tersine mühendislik eğitimi,
  • güvenlik kontrollerinin nasıl çalıştığını belgeleme.

Şu bağlamlarda ise sorunludur:

  • başkasının özel verisini kaydetmek,
  • bankacılık veya sağlık uygulamalarındaki hassas ekranları izinsiz yakalamak,
  • DRM veya ücretli medya korumasını aşmak,
  • kurumsal güvenlik politikalarını ihlal etmek,
  • kullanıcı rızası olmadan ekran içeriği toplamak.

Bu nedenle yazının ana çerçevesi “korumayı nasıl aşarım?” değil, “Android bu korumayı hangi katmanlarda uygular ve bu katmanlar nasıl analiz edilir?” olmalıdır.

12. Sonuç

FLAG_SECURE, Android’de basit bir pencere bayrağı gibi görünse de arkasında çok katmanlı bir framework davranışı vardır. Uygulama tarafında başlayan bu işaret; WindowState, WindowManagerService, ScreenCapture, SurfaceControl, DisplayControl, VirtualDisplayAdapter, Android 14/15 callback API’leri, SystemUI ve üretici screenshot servislerine kadar uzanan geniş bir teknik alana yayılır.

Eski Smali / services.jar müdahalesi yöntemi Android framework’ü öğrenmek için öğretici olabilir, fakat modern cihazlarda kırılgan ve risklidir. LSPosed ve libxposed tabanlı runtime hook yaklaşımı ise sistem dosyalarını kalıcı olarak değiştirmeden framework davranışını analiz etmeye imkan verir.

DisableFlagSecure.java dosyasının mimarisi, başarılı bir analizin tek bir metoda indirgenemeyeceğini gösterir. WindowState.isSecureLocked() önemli bir noktadır; ancak ScreenCapture argümanları, secure layer politikası, screenshot observer kayıtları, screen recording callback’leri, MediaProjection/virtual display akışları, OEM framework sınıfları ve ART deoptimization birlikte düşünülmelidir.

Bu yüzden FLAG_SECURE konusu Android tersine mühendislik için güçlü bir örnektir. Bir yandan pencere ve grafik altyapısını anlamayı sağlar; diğer yandan modern Android güvenlik modelinin ne kadar katmanlı hale geldiğini gösterir.

En sağlıklı yaklaşım, bu konuyu sorumlu araştırma çerçevesinde ele almaktır: amaç güvenlik mekanizmasını kötüye kullanmak değil, Android’in bu kararı nerede, neden ve nasıl verdiğini anlamaktır.

Yazı başlığı: Android Tersine Mühendislik: FLAG_SECURE, LSPosed ve DisableFlagSecure
Yazar: Enes Salman
Yayın tarihi: 2 Haziran 2026
Copyright 2026
RSS