-15% na nową książkę sekuraka: Wprowadzenie do bezpieczeństwa IT. Przy zamówieniu podaj kod: 10000

Analiza komunikacji HTTP na platformie Android

07 października 2015, 14:50 | Teksty | komentarzy 11

Artykuł poświęcony jest zagadnieniu analizy ruchu sieciowego pomiędzy aplikacją na urządzeniu z systemem Android a zewnętrznymi serwerami. Znakomita większość tego typu komunikacji odbywa się z wykorzystaniem protokołu HTTP (z szyfrowaniem lub bez) i w tym artykule skoncentruję się na przechwytywaniu tej komunikacji oraz problemach jakie trzeba będzie rozwiązać, aby być w stanie analizować ten ruch za pomocą takich narzędzi jak tcpdump lub Burp Proxy

Z artykułu dowiesz się:

  • jak skompilować tcpdump dla procesorów ARM;
  • jak zainstalować certyfikat BURP na urządzeniu Android i przechwytywać ruch HTTPS;
  • jak zmodyfikować aplikację Android, żeby wyłączyć Certificate Pinning

Przedstawione w artykule przykłady będą dotyczyć zarówno fizycznego urządzania jak również emulatora architektury ARM. Kolejne przykłady dotycząc rozwiązania problemów, na które natkniemy się analizując ruch sieciowy na platformie Android stosując powszechnie stosowane narzędzia takie jak: tcpdump, Burp. Przykłady ułożone są w kolejności trudności analizy ruchu sieciowego – od nieszyfrowanego do szyfrowanego. Wymogiem do przeprowadzania poniższych przykładów jest posiadania zrootowanego urządzenia (np. instalacja oprogramowania dodatkowego i nadawanie uprawnień).

1. Tcpdump – zrzut komunikacji sieciowej

Device

Do wykonania zrzutu komunikacji sieciowej potrzebujemy powszechnie stosowanego narzędzia tcpdump. W tym celu musimy ściągnąć binarkę dla architektury procesorów ARM lub samodzielnie wykonać tzw. cross-compilation. W kolejnym kroku zainstalujemy tcpdump na urządzeniu w lokalizacji, która jest na ścieżce uruchomieniowej (PATH) – /system/bin lub /system/xbin. Źródła tcpdump oraz biblioteki libpcap pozyskać można ze strony http://www.tcpdump.org.

Do kompilacji w systemie Linux (x86_64) źrodeł programów wykonywalnych na procesorach ARM potrzebujemy kompilator ARM GNU C++. W przykładzie wykorzystamy starsze, stabilne repozytorium dla Debian Squeeze:

apt-get install g++-4.4-arm-linux-gnueabi

W katalogu, w którym umieściliśmy rozpakowane źródła, przygotowujemy skrypt kompilacji wskazując nowy kompilator i uruchamiamy kompilację libpcap – biblioteki do przechwytywania pakietów.

cd libpcap-1.7.4/
export CC=arm-linux-gnueabi-gcc
./configure --host=arm-linux --with-pcap=linux
make

Pora na narzędzie tcpdump… Ustawiamy zmienną środowiskową ac_cv_linux_vers na główną wersję jądra systemu Linux a pozostałe zmienne tak,by tcpdump był zlinkowany statycznie.

cd ../tcpdump-4.7.4
export ac_cv_linux_vers=3
export CFLAGS=-static
export CPPFLAGS=-static
export LDFLAGS=-static
./configure --host=arm-linux --disable-ipv6
make
arm-linux-gnueabi-strip tcpdump

W efekcie otrzymujemy narzędzie tcpdump dla procesorów ARM.

file tcpdump
ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, for GNU/Linux 2.6.18, BuildID[sha1]=0xf632a7a79f19fd9e622886355bfa4ef7172ec421, stripped

Do instalacji tcpdump wykorzystamy adb (Android Debug Bridge). W pierwszym kroku umieszczamy tcpdump na zewnętrznej karcie SD, a następnie należy zamontować partycję /system w trybie do zapisu (niezbędny root na urządzeniu).

adb -d push tcpdump /sdcard/tcpdump
adb -d shell
(adb)mount -o remount,rw /system
(adb)cp /sdcard/data/tcpdump system/bin
(adb)cd /system/bin
(adb)chown root:shell /system/bin/tcpdump
(adb)chmod 754 /system/bin/tcpdump
(adb)mount -o remount,ro /system

Jesteśmy gotowi do uruchomienia polcenia tcpdump w celu zebrania komunikacji sieciowej:

tcpdump -vv -s 0 -w /sdcard/dump.pcap

Plik dump.pcap możemy skopiować z urzadzenia na stację roboczą, gdzie możemy poddać go dalszej analizie za pomocą np. Wireshark.

adb -d pull /sdcard/dump.pcap /android/pcaps/

Emulator

W przypadku emulatorów urządzeń Android istnieje możliwość uruchomienia takiego emulatora, który całą komunikację sieciową będzie zapisywał do lokalnego pliku pcap.

emulator -avd Nexus_4_API_18 --tcpdump dump_avd.pcap

Na koniec zaproponuję jeszcze przechwytywanie pakietów w trybie „live” z wykorzystaniem netcat. Pakiety przekazywane są do netcata na port 8888 urządzenia/emulatora Android.

adb shell "tcpdump -s 0 -w - | nc -l -p 8888"

Na stacji roboczej włączamy przekierowanie portu 8888 urządzania/emulatora na port lokalny 8888 stacji roboczej, a następnie otrzymane pakiety przekazujemy do Wireshark

adb forward tcp:8888 tcp:8888
nc localhost 8888 | sudo wireshark -k -S -i -
wireshark_live

Przechwytywanie pakietów „live” z urządzenia/emulatora Android do Wirshark na stacji roboczej z systemem Linux

Przy okazji warto zwrócić uwagę na adresy IP jakie zostają przydzielone dla emulatora. Każdy emulator otrzymuje adresację z sieci 10.0.2.0/24, gdzie poszczególne adresy są przyporządkowane konkretnym hostom:

  • 10.0.2.1 – brama domyślna
  • 10.0.2.2 – adres odpowiadający interfejsowi loopback na maszynie deweloperskiej (pentesterskiej)
  • 10.0.2.4-10.0.2.6 – adresy serwerów DNS
  • 10.0.2.15 – adres IP interfejsu sieciowego emulatora
  • 127.0.0.1 – adres interfejsu loopback

2. Przechwytywanie ruchu HTTP(S) w Burp Suite

Znakomita większość komunikacji pomiędzy aplikacjami a serwerami zewnętrznymi odbywa się na portach 80 i 443 wykorzystując protokół http. W tym przykładzie przedstawię przechwytywanie tej komunikacji w narzędziu typu http-proxy. Najprostszym sposobem na przekazywanie tego ruchu jest wskazanie adresu IP i portu na którym nasłuchuje Burp Suite na komputerze, gdzie będziemy przeprowadzać analizę. Jak korzystać z narzędzia Burp możemy dowiedzieć się z innych artykułów na Sekuraku.

Device

Urządzenie Android i komputer (192.168.1.100) podłączamy do tej samej sieci WiFi. W czasie zestawiania połączenia należy rozwinąć opcje zaawansowane i skonfigurować serwer proxy podając komputera i numer portu, na którym nasłuchuje Burp.

proxy

Emulator

W przypadku emulatora, możemy skonfigurować adres serwera proxy (tu 192.168.1.100 lub 127.0.0.1) dla protokołu http podając go w czasie uruchamiania emulatora z linii poleceń.

emulator -avd Nexus_4_API_18 -http-proxy 192.168.1.100:8080

ale poprawna jest również poniższa składnia

emulator -avd Nexus_4_API_18 -http-proxy http://127.0.0.1:8080

Instalacja certyfikatu Burp

Do przechwytywania ruchu szyfrowanego musimy zainstalować certyfikat naszego narzędzia http-proxy. W tym celu konfigurujemy ustawienia PROXY w Burp Suite:

1. Konfiguracja Burp do nasłuchiwania na wszystkich interfejsach (nie tylko loopback)

burp2

2. Edytujemy ustawienia (przycisk Edit) i włączamy opcję invisible proxying (http://
blog.portswigger.net/2008/11/mobp-invisible-proxying.html), co pozwoli również na przechwytywanie zapytań tzw. nonproxy.

3.Pobieramy certyfikat Burp w formacie DER (przycisk CA certificate…)

burpcrt

i zapisujemy z rozszerzeniem .crt Android nie rozpoznaje domyślnego dla Burp rozszerzenia der.

burpcrt1

4. Plik certyfikatu przenosimy na kartę SD na emulator/urządzenie.

adb -e push burp.crt /sdcard/
10 KB/s (712 bytes in 0.066s)

5. Ostatnim krokiem jest instalacja tego certyfikatu:

Settings->Security->Install from SD card

installcrtnamecrt

Jeśli uda nam się podsłuchać komunikację szyfrowaną po zainstalowaniu, to oznacza nasz sukces a porażkę deweloperów. Aplikacja powinna implementować przypinanie certyfikatów SSL i instalacja certyfikatu Burp nie powinna umożliwić przechwytywania ruchu HTTPS.

3.Pentester kontra cert pinning

Zbliżony temat był już poruszany na łamach Sekuraka

Coraz więcej aplikacji stosuje mechanizm SSL certificate pinning (przypinanie certyfikatów). Co do tego kiedy i jak stosować pinning, odwołam się do zaleceń OWASP, które są już standardem dobrych praktyk budowania bezpiecznych aplikacji (https://www.owasp.org/index.php/Pinning_Cheat_Sheat). Na wymienionej stronie OWASP znajdują się również przykładowe aplikacje w tym projekt przygotowany dla Androida. W poniższym przykładzie odnajdziemy kod odpowiedzialny za przypinanie certyfikatów. W przykładowej aplikacji znajduje się klasa PubKeyManager, która implementuje interfejs X509TrustManager:

public final class PubKeyManager implements X509TrustManager {

	// DER encoded public key
	private static String PUB_KEY = "30820122300d06092a864886f70d0101"
			+ "0105000382010f003082010a0282010100b35ea8adaf4cb6db86068a836f3c85"
			+ "5a545b1f0cc8afb19e38213bac4d55c3f2f19df6dee82ead67f70a990131b6bc" (...)

Aplikacja implementuje ten interfejs, aby poprzez obiekt SSLSocketFactory ufać innym certyfikatom CA w czasie realizacji połączeń HTTPS. Warto również odwołać się do opisu realizacji połączeń HTTPS na oficjalnej stronie Androida dla deweloperów –  http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html

Należy również pamiętać, że aplikacja Android może również wykonywać połączenia SSL za pomocą obiektów innych klas np.

  • android.webkit.WebView
  • org.apache.http.impl.client.DefaultHttpClient
  • javax.net.ssl.HttpsURLConnection

Na potrzeby tego artykułu skoncentruję się na połączeniach za pomocą tej ostatniej. Jednakże zaprezentowane techniki mogą być również stosowane dla pozostałych.

TrustManager tm[] = { new PubKeyManager() };
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tm, null);

URL url = new URL("https://www.example.com/");
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();

W interfejsie X509TrustManager zadeklarowane są 3 metody:

  • void checkClientTrusted(X509Certificate[] chain, String authType)
  • void checkServerTrusted(X509Certificate[] chain, String authType)
  • X509Certificate[] getAcceptedIssuers()

Aplikacja, która stosuje przypinanie certyfikatów, w celu weryfikacji połączenia z serwerem musi implementować metodę checkServerTrusted . Poniżej przykład takiej implementacji.

public void checkServerTrusted(X509Certificate[] chain, String authType)
			throws CertificateException {

		assert (chain != null);
		if (chain == null) {
			throw new IllegalArgumentException(
					"checkServerTrusted: X509Certificate array is null");
		}

		assert (chain.length > 0);
		if (!(chain.length > 0)) {
			throw new IllegalArgumentException(
					"checkServerTrusted: X509Certificate is empty");
		}

		assert (null != authType && authType.equalsIgnoreCase("RSA"));
		if (!(null != authType && authType.equalsIgnoreCase("RSA"))) {
			throw new CertificateException(
					"checkServerTrusted: AuthType is not RSA");
		}

		// Perform customary SSL/TLS checks
		TrustManagerFactory tmf;
		try {
			tmf = TrustManagerFactory.getInstance("X509");
			tmf.init((KeyStore) null);

			for (TrustManager trustManager : tmf.getTrustManagers()) {
				((X509TrustManager) trustManager).checkServerTrusted(
						chain, authType);
			}

		} catch (Exception e) {
			throw new CertificateException(e);
		}

		// Hack ahead: BigInteger and toString(). We know a DER encoded Public
		// Key starts with 0x30 (ASN.1 SEQUENCE and CONSTRUCTED), so there is
		// no leading 0x00 to drop.
		RSAPublicKey pubkey = (RSAPublicKey) chain[0].getPublicKey();
		String encoded = new BigInteger(1 /* positive */, pubkey.getEncoded())
				.toString(16);

		// Pin it!
		final boolean expected = PUB_KEY.equalsIgnoreCase(encoded);
		assert(expected);
		if (!expected) {
			throw new CertificateException(
					"checkServerTrusted: Expected public key: " + PUB_KEY
							+ ", got public key:" + encoded);
		}
	}

W ramach tej metody sprawdzana jest poprawność certyfikatu serwera i porównanie z certyfikatem umiesczonym w aplikacji:

final boolean expected = PUB_KEY.equalsIgnoreCase(encoded);
assert(expected);

Jeśli asercje wykryją niezgodność certyfikatów rzucany jest wyjątek CertificateException. Aby ominąć weryfikację certyfikatu mamy dwa sposoby do wyboru a z każdym z tych sposobów wiążą się określone ograniczenia:

  • modyfikacja kodu aplikacji – najcześciej nie będziemy mieli dostępu do kodu źródłowego w języku JAVA, zatem konieczne będzie zastosowanie techniki reverse enginnering i ponowna kompilacja aplikacji,
  • przechwytywanie w czasie uruchomienia aplikacji wywołań jej metod,tu metody checkServerTrusted. – do tego niezbędne będzie „zrootwane” urządzenie.

Modyfikacja aplikacji

Istnieje szereg narzędzi do dekompilacji kodu aplikacji zawartej w pliku classes.dex  (kod bajtowy wirtualnej maszyny Dalvik ) do postaci kodu źródłowego napisanego w języku Java, są to m.in.:

W przypadku modyfikacji aplikacji, dekompilacja do postaci plików .java może jedynie pomóc w analizie działania aplikacji, do tego celu niezbędne będzie skorzystanie z dezasemblacji kodu dex do postaci plików smali. Klasycznym narzędziem do przeprowadzenia dezasmblacji jest oprogramowanie wiersza poleceń – apktool. W międzyczasie powstały również narzędzia zawierające GUI automatyzujące kroki, które w przypadku apktool trzeba wykonać ręcznie np. cyfrowe podpisywanie aplikacji lub instalację aplikacji):

Na potrzeby artykułu skorzystamy z apktool i przedstawię niezbędne kroki po kolei. Do analizy wykorzystam wcześniej wspomnianą aplikację PubKeyPin pobraną z serwerów OWASP, która jest przykładem poprawnej weryfikacji certyfikatów SSL. Jednym z konsultantów kodu aplikacji był specjalista od bezpieczeństwa platformy Android Nikolay Elenkov autor książki Android Security Internals.
1. Pobrany kod należy zaimportować do IDE dla Android np. Android Studio  i wygenerować APK (myapplication.apk

Build -> Generate Signed APK…

adb install pubkeypin.apk

Po zainstalowaniu aplikacji na urządzeniu z certyfikatem Burp, aplikacji generuje błąd walidacji – SSL pinning działa poprawnie.

ssl_error

2. Za pomocą apktool należy przekształcić kod wykonywalny dex do postaci plików smali, przełącznik -r w tym wypadku służy do dekompilacji wyłącznie kodu (bez zasobów resources np. plików xml).

apktool d com.example.root.pubkeypin-2.apk -o smali/ -f -r 
I: Using Apktool 2.0.1 on com.example.root.myapplication-2.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: /root/apktool/framework/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...

3. Wyszukujemy pkik smali odpowiadający za weryfikację certyfikatu

# grep -R -i "checkServerTrusted" .
./smali/com/example/root/pubkeypin/PubKeyManager.smali:.method public checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V

Poszukiwany plik  to PubKeyManager.smali Po modyfikacji i usunięciu kodu ze środka metody checkServerTrusted plik ten powinien wyglądać tak (fragment zawierający metodę):

method public checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V
    .locals 12
    .param p1, "chain"    # [Ljava/security/cert/X509Certificate;
    .param p2, "authType"    # Ljava/lang/String;
    .annotation system Ldalvik/annotation/Throws;
        value = {
            Ljava/security/cert/CertificateException;
        }
    .end annotation

    .line 81
    return-void
.end method

4. Rekompilujemy aplikację, podpisujemy ją i instalujemy ponownie

apktool b smali/ pinning_removed.apk -f
I: Using Apktool 2.0.1
I: Smaling smali folder into classes.dex...
I: Copying raw resources...
I: Building apk file...
jarsigner -verbose  -sigalg SHA1withRSA -digestalg SHA1 -keystore key.keystore pinning_removed.apk sign

Tym razem aplikacji poprawnie łączy się z serwerem docelowym,

ssl_bypass_fetch

a ruch został również przechwycony przez http proxy – Burp.

pubkey_burp

Istnieje również przydatne narzędzie napisane w Pythonie – intrinsec-android-ssl-patch , które automatyzuje powyższe kroki i generuje zmodyfikowaną i podpisaną aplikację.

Hooking

Innym sposobem ominięcia walidacji certyfikatu jest przechwytywanie wywołań metod sprawdzających. Dla platformy Android instnieje narzędzie Cydia Substrate, (aplikacja w formacie APK) które pozwala wykonywać takie modyfikacje w czasie uruchomienia programu. Do tego oprogramowania istnieje wiele rozszerzeń m.in. Android-SSL-TrustKiller , za pomocą którego również możemy osiągnąć zamierzony cel. Jedynym zastrzeżeniem jest to, że zarówno aplikacja Cydia jak i rozszerzenia muszą być uruchamiane z podwyższonymi uprawnieniami.

Ciekawy przykład przechwytywania wywołań metod za pomocą narzędzia Frida zaprezentowali @_lavalamp i @rotlogix na swoim blogu http://rotlogix.com/2015/09/13/defeating-ssl-pinning-in-coin-for-android/  W tym przypadku za pomocą przygotowanego skryptu byli w stanie przechwycić hasło przekazywane do metody otwierającej zaszyfrowany, lokalny kontener (Bouncy Castle) na zaufane certyfikaty. W ten sposób byli w stanie dopisać do tego zasobu również certyfikat Burp i analizować ruch HTTPS.

Podsumowanie

W artykule zostały przedstawione problemy (i rozwiązania) jakie spotkamy na platformie Android, jeśli chcielibyśmy zastosować narzędzia, które często na co dzień stosowane są w pracy np. dewelopera lub pentestera aplikacji webowych. W artykule pokazano jak skompilować narzędzie tcpdump dla systemów pracujących z wykorzystaniem procesorów ARM oraz przechwytywać ruch HTTP i HTTPS nawet dla aplikacji stosujących przypinanie certyfikatów SSL.

źródła i inspiracje:

  1. http://techblog.vsza.hu/tags/android/
  2. http://www.symantec.com/connect/blogs/monitoring-android-network-traffic-part-i-installing-toolchain
  3. http://www.thoughtcrime.org/blog/authenticity-is-broken-in-ssl-but-your-app-ha
  4. https://www.owasp.org/index.php/Pinning_Cheat_Sheet
  5. http://www.mcafee.com/us/resources/white-papers/wp-defeating-ssl-cert-validation.pdf

Bartek Jerzman

Spodobał Ci się wpis? Podziel się nim ze znajomymi:



Komentarze

  1. XxX

    Dajcie proszę więcej o testowaniu apek mobilnych, mega ciekawy temat.

    Odpowiedz
  2. aaaaa

    Gdzie można dostać taką kartę SB ? ;)

    Odpowiedz
  3. @ XxX Wkrótce więcej artykułów na temat testów bezpieczeństwa aplikacji mobilnych. Z mojej strony dotyczących Androida.

    @aaaaa

    Może u Służby Bezpieczeństwa? ;) poprawione

    Odpowiedz
  4. asdf

    Jak komuś się nie chce bawić z tcpdumpem to w play storze jest appka packet capture, którą można podglądać i zapisywać ruch TCP i UDP.
    Dodatkowo deszyfruje HTTPS, jeśli zgodzimy się na dodanie jej certyfikatu.

    Odpowiedz
  5. darek

    Konkretnie na temat. Mam jednak pytanie, dlaczego w pkt 2 jest mowa o porażce deweloperów? Jeżeli dobrze rozumiem w tym przypadku aplikacja ufa certyfikatom zainstalowanym w globalnym repozytorium, jakie to stwarza zagrożenie? Jedyne co przychodzi mi do głowy, to że inna aplikacja zainstaluje swój certyfikat i tym samym nasza aplikacja zaczyna ufać certyfikatowi dostarczonemu przez innego dewelopera

    Odpowiedz
  6. me_and_me

    Co to tcdump? :)

    Odpowiedz
  7. Grzegorz

    a może takie artykuły też się pojawią jako mobi/epub? :)

    Odpowiedz
  8. @Darek Z jednej strony „porażka” jest być może słowem przesadzonym, ponieważ masz rację, że nie oznacza to wcale, że mamy do czynienia z załamaniem się zabezpieczeń SSL ponieważ bazujemy na certyfikatach CA osadzonych w systemie Android. Z drugiej strony, czasem warto przesadzić, żeby zwrócić uwagę na pewne zagadnienie i podejście. W sieci rzeczywiście trwa dyskusja pod hasłem „to pin or not to pin”. Jednym z ciekawych przykładów jest artykuł z tegorocznego symposium USENIX: https://www.usenix.org/system/files/conference/usenixsecurity15/sec15-paper-oltrogge.pdf

    Na pewno przypinanie certyfikatów znacząco zwiększa bezpieczeństwo w porównaniu do klasycznego modelu bazującego na kilkudziesięciu do kilkuset wbudowanych certyfikatów CA systemu. Zresztą ten model sprawdza się w przypadku przeglądarek internetowych, gdzie użytkownicy muszą wykonywać połączenia do różnych serwerów www. W przypadku aplikacji mobilnych połączenie do serwerów aplikacyjnych jest znane i łączą się one do zaufanych hostów, stąd wręcz naturalnym jest zastosowanie pinningu. Pozostaje oczywiście problem funkcjonalności (odwieczny zresztą konflikt z bezpieczeństwem) w przypadku zmiany certyfikatów po stronie serwera i akltualizacji aplikacji na urządzeniach. Tu zresztą również mamy wybór pomiędzy Leaf pinning, gdzie przypinamy certyfikaty serwerów aplikacyjnych (opcja restrykcyjna) lub można zastsować bardziej elastyczny CA pinning, gdzie przypinamy certyfikat CA, który podpisuje certyfikaty pozostałych serwerów.

    Scenariusz, w którym dodamy podstawiony certyfikat i wykonamy skuteczny MiTM (szybko i prosto) jest bardziej prawdopdobne niż rekompilacja aplikacji i podpisanie jej (kluczem deweloperskim). Także jeśli porówanamy te dwa podejścia poprzez położenie na szalę, to pinning rzeczywiście przyczynia się do wzrostu bezpieczeństwa w stosunku do klasycznego modelu znanego z przeglądarek. Co ciekawe Google Chrome na Androida też stosuje pinning dla połączeń z serwerami Google.

    @Adam
    Dzięki za fajne uzupełnienie! Idealnie wposowuje się w kontekst artykułu, czyli jak wykorzystać znane narzędzia w świecie mobilnym.

    Odpowiedz
  9. Marcin

    W moim przypadku ruch z przeglądarki na androidzie rzeczywiście przechodzi przez proxy na PC, natomiast ruch z aplikacji idzie „inną drogą” jak z tym sobie radzić?
    Rootowanie telefonu to jedyny sposób?

    Odpowiedz

Odpowiedz na Marcin