RĂ©parer, pirater, creuser. RĂ©soudre la quĂȘte en ligne Droid Mission



L'annĂ©e derniĂšre, nous avons menĂ© une quĂȘte en ligne pour les dĂ©veloppeurs mobiles - Droid Mission. En un mois, les participants ont dĂ» rĂ©soudre autant de problĂšmes que possible dans trois directions: rĂ©soudre le problĂšme! (recherche de bogues et recherche de code), piratez-le! (reverse engineering) et creusez-le! (apprentissage des fonctionnalitĂ©s d'Android). Au total, la quĂȘte avait 23 tĂąches - elles sont trĂšs similaires Ă  celles que les spĂ©cialistes Android rencontrent dans le travail rĂ©el. Dans le post, nous montrerons toutes les conditions et les bonnes solutions.


Recherche d'erreurs et recherche de code


le réparer! # 1

Auteur: Anastasia Laushkina

Par un calme vendredi soir, deux agents restent debout tard et mÚnent un différend insoluble. On leur a demandé de stocker une grande quantité de données secrÚtes dans une base de données Sqlite avec une clé de clé entiÚre.

L'agent A crĂ©e une table avec une requĂȘte du formulaire: CREATE TABLE t (clĂ© INT PRIMARY KEY, secret_value_1, secret_value_2), et l'agent B avec une requĂȘte du formulaire CREATE TABLE t (key INTEGER PRIMARY KEY ASC, secret_value_1, secret_value_2).

L'agent A est absolument sĂ»r qu'il n'y a pas d'erreur dans sa demande, cependant, l'agent B a un argument contre cela: il prĂ©tend que l'intĂ©gritĂ© des donnĂ©es lors de l'approche de l'agent A peut ĂȘtre violĂ©e.

Liste avec une virgule: la ou les colonnes responsables de l'intégrité de l'agent B, mais non responsables de l'agent A; ainsi que la structure des données, en russe, grùce à laquelle la demande de l'agent B sera plus rapide.

Remarques
Format de réponse: [nom de colonne 1], [nom de colonne N], [nom de structure] (sans crochets, avec une petite lettre)

Exemple de réponse: x, y, tableau

Solution

Il s'agit de rowId et de sa relation avec la clé primaire.

Dans le cas de "CREATE TABLE t (key INTEGER PRIMARY KEY ASC, secret_value_1, secret_value_2)", la clĂ© est un alias de rowId. Dans le cas de "CREATE TABLE t (key INT PRIMARY KEY, secret_value_1, secret_value_2)" - non. La raison peut ĂȘtre trouvĂ©e dans la documentation .

Par consĂ©quent, en l'absence d'une relation clĂ© avec rowId, une requĂȘte de la forme «INSERT INTO t VALUES (" certains "," y "," z ")" sera exĂ©cutĂ©e avec succĂšs, ce qui pourrait potentiellement violer l'intĂ©gritĂ© des donnĂ©es. S'il existe une telle connexion, une erreur d'incompatibilitĂ© de type de donnĂ©es se produit.

La rĂ©ponse a Ă©tĂ© considĂ©rĂ©e comme une paire de clĂ©s, rowId et key / rowId sĂ©parĂ©ment (car s'il y a une connexion, elles se rĂ©fĂšrent Ă  la mĂȘme colonne).

La vitesse est obtenue grĂące au stockage sous la forme d'un arbre de recherche.

le réparer! # 2

Auteur: Anastasia Laushkina
Fichier binaire

Pour dĂ©tecter rapidement les ennemis, l'agent Kew dĂ©veloppe une application avec une liste de criminels connus. Il a facilement rĂ©ussi Ă  afficher cette liste dans l'application, mais sur ce point, il dĂ©cide que ces informations importantes devraient ĂȘtre sur l'Ă©cran principal.

Pour ce faire, il a besoin d'un widget. Un problĂšme: pour une raison quelconque, le widget n'affiche pas de liste. Un instinct indique Ă  l'agent que le problĂšme n'est que sur une seule ligne.

Nommez-le avec une virgule, ainsi que la ligne par laquelle vous devez le remplacer, afin d'obtenir la solution la plus optimale.

Remarques
Format de réponse: [ligne 1], [ligne 2] (sans crochets)

Exemple de réponse: FrameLayout, ScrollView

Solution

1. Nous recherchons le fichier de mise en page pour le widget, nous y voyons un élément ConstraintLayout non valide (voir developer.android.com/guide/topics/appwidgets#CreatingLayout ).
2. Changez-le en acceptable et optimal - LinearLayout / FrameLayout.

Malgré le fait que l'utilisation de FrameLayout a brisé la disposition des éléments dans le widget, cela a toujours été accepté comme réponse.

le réparer! # 3

Auteur: Artyom Viter
Fichier binaire

Les testeurs ont remarqué une augmentation anormale de la consommation de RAM. On soupçonne qu'il y a une fuite quelque part.

Vous devez enquĂȘter et localiser le problĂšme. Dans la rĂ©ponse, indiquez le nom de la mĂ©thode de la classe d'application et le lieu de son appel, ce qui entraĂźne une fuite de mĂ©moire. Si vous rencontrez plusieurs problĂšmes, rĂ©pertoriez-les tous.

Format de la réponse: [Nom de la classe dans laquelle la méthode est appelée]: [numéro de ligne dans ce fichier]: [nom de la méthode] (sans crochets)

Indiquez plusieurs réponses dans l'ordre lexicographique, séparées par un #.

Un exemple:

Si vous pensez qu'il y a une fuite de mémoire dans le fichier ExampleFileName.java,

class ExampleFileName {   public problemMethodName(Sting arg) {}   public test(Sting arg) {     problemMethodName(arg); //  8   ExampleFileName.java   } } 

Exemple de réponse: ExampleFileName: 8: problemMethodName

Solution

La premiÚre façon est l'analyse de code.

  1. Nous Ă©tudions le code d'application.
  2. La premiĂšre fuite est Ă©vidente. Il s'agit d'un enregistrement BroadCastReceiver dans la classe SecondActivity. Un message sur ce problĂšme peut ĂȘtre vu dans logcat si vous exĂ©cutez l'application.
  3. Si vous faites attention à la méthode startTimer (), vous remarquerez que la classe CountDownTimer ajoute une fermeture au gestionnaire de thread principal avec un lien vers textView. Et avant que l'activité ne soit détruite, la classe d'instance (CountDownTimer) n'appelle pas la méthode cancel (). Il s'agit de la deuxiÚme fuite de mémoire.

La deuxiÚme façon, ce sont les outils. Vous pouvez implémenter la bibliothÚque leakcanary dans l'application, obtenir un vidage de tas via adb ou android Profiler et analyser les rapports reçus. Eclipse Memory Analyzer (MAT) peut vous aider avec l'analyse. Pour ouvrir le heapdump dans MAT, vous devez le convertir dans un format compréhensible pour MAT à l'aide de l'utilitaire hprof-convd.

le réparer! # 4

Publié par: Vali Ibragimov
Fichier binaire

Ivan a son premier jour dans un cinĂ©ma en ligne. Puisqu'il est un dĂ©veloppeur sympa, on lui a immĂ©diatement demandĂ© de corriger le crash en regardant un film. Il a dĂ©cidĂ© en mĂȘme temps de refactoriser. Le code est devenu plus concis et plus propre, l'erreur a cessĂ© de se reproduire.

Cependant, il s'est avéré plus tard que la position actuelle du film n'a pas été envoyée au backend. Aidez Ivan à comprendre quelle classe il a mélangée.

La réponse est le nom de la classe à utiliser.

Solution

Pour envoyer une position de visualisation toutes les cinq secondes et aller sur le rĂ©seau, ExecutorService est utilisĂ©. CrĂ©er un ScheduledThreadPoolExecutor pour une tĂąche pĂ©riodique, pensa Ivan - pourquoi ne pas l'utiliser pour aller sur le rĂ©seau? AprĂšs tout, ScheduledThreadPoolExecutor est le descendant de ThreadPoolExecutor. Ivan ne soupçonnait mĂȘme pas que ScheduledThreadPoolExecutor n'utilise qu'un seul thread, et lorsque vous vous connectez au rĂ©seau, vous devrez attendre la fin de la tĂąche pĂ©riodique.

 scheduleFuture = scheduledExecutorService.scheduleAtFixedRate(() -> {                    try {                        timingsApi.sendTiming(filmId, player.getContentPosition()).get();                    } catch (Exception e) {                        e.printStackTrace();                    }                },                0, PERIOD_SECONDS, TimeUnit.SECONDS); 

La solution au problÚme a été encore compliquée par le fait que nous n'accédions pas à ExoPlayer dans le thread d'interface utilisateur - et dans la nouvelle version 2.9. * ExoPlayer a commencé à écrire des avertissements à ce sujet dans les journaux. Mais maintenant, ce n'est qu'un avertissement, et non une erreur commise par Ivan.

le réparer! # 5

Auteur: Alexander Tsybin
Fichier binaire

Une entreprise développait un jeu Farmer Simulator.

Dans ce jeu, seul un joueur pouvait se dĂ©placer librement. Tous les autres objets Ă©taient immobiles ou avaient une trajectoire de mouvement clairement dĂ©finie. À un moment donnĂ©, ils ont dĂ©cidĂ© d'ajouter des ennemis au jeu qui deviendraient un obstacle pour le joueur.

Le développeur Vasya a été chargé de réaliser les ennemis. Pendant l'implémentation, Vasya a rencontré un problÚme de performances. Il a décidé de le réparer en raison du multithreading. Comme Vasya n'était pas familier avec le multithreading, il a profité des solutions de stackoverflow. Il s'est donc débarrassé des problÚmes de performances.

Cependant, au stade des tests, il a été constaté que parfois le jeu se bloquait. Aidez Vasya à trouver la raison.

En guise de réponse, entrez les numéros des lignes de code problématiques - séparés par des virgules, dans l'ordre croissant et sans espaces. Comme ceci: 1,17,42

Solution

Les types primitifs de boxe (par exemple int dans Integer) sont des opĂ©rations coĂ»teuses. À des fins d'optimisation, les machines Java rĂ©utilisent parfois ces objets. Les lignes ne font pas exception.

Les lignes ENEMY_LOCK et DECISIONS_LOCK sont utilisĂ©es dans le code pour la synchronisation. Par consĂ©quent, s'il existe des emplacements dans le projet avec synchronisation sur les mĂȘmes lignes, un blocage peut se produire.

En général, vous ne pouvez pas utiliser String / Integer, etc., pour la synchronisation.

RĂ©ponse: 7.8

le réparer! # 6

Auteur: Ivan Pukhov
Fichier binaire

L'équipe de traitement d'image a publié une nouvelle bibliothÚque native, vous devez l'essayer et signaler les problÚmes, le cas échéant.

Solution

C'est une tĂąche facile Ă  rĂ©chauffer. Dans les ressources se trouve la bibliothĂšque elle-mĂȘme et le fichier d'en-tĂȘte correspondant. Dans l'en-tĂȘte, seule fonction renvoyant un jobject.

Il ne reste plus qu'à créer un projet de test (en installant NDK) et à l'appeler! Un appel de fonction sur le thread principal lÚvera une exception demandant de ne pas l'appeler de cette façon. Nous transférons l'exécution à un autre thread de quelque maniÚre que ce soit - et tout fonctionne.

Maintenant, cela vaut la peine de vérifier l'objet retourné - est-ce vraiment une image bitmap? Le moyen le plus simple consiste à l'afficher immédiatement sur une ImageView. Nous verrons une image avec une chaßne de texte, et la tùche nécessite également une réponse sous la forme d'une chaßne. Ce n'est probablement pas une simple coïncidence. :)

le réparer! # 7

Auteur: Dmitry Zaitsev
Fichier binaire

Il existe une application avec des codes oĂč le code souhaitĂ© est encadrĂ©. Malheureusement, la mise en page a Ă©tĂ© brisĂ©e et doit ĂȘtre restaurĂ©e pour trouver le code.

Remarques
AprĂšs avoir corrigĂ© le code, le cadre passera par exactement 22 caractĂšres qui doivent ĂȘtre rĂ©pertoriĂ©s dans le sens antihoraire, en commençant par le coin supĂ©rieur gauche du cadre.

Exemple de réponse: ScQG1jxbazmjbcARefbRMo

Solution

Maintenant, Dagger est devenu la norme de facto dans le développement Android. Lorsque la DI est déjà configurée, il est trÚs simple d'écrire Inject devant le constructeur et de donner tout le reste du travail à Dagger. C'est si facile que vous pouvez plus tard cesser de penser à la façon dont les dépendances sont fournies. Toujours dans Android, vous devez souvent travailler avec la classe Context et ses descendants: Application et Activité. L'application est liée au cycle de vie de l'application et l'activité est liée au cycle de vie de l'écran. Par conséquent, en travaillant avec ces classes, il est important d'observer de nombreuses fonctionnalités. La tùche est précisément basée sur l'utilisation du mauvais contexte, et tout cela est déguisé en utilisant DI.

Pour résoudre, vous pouvez effectuer les étapes suivantes:

  1. Examinez les fichiers du projet et notez que ItemStyle est appliqué à tous les TextViews.
  2. Voyez que le style n'est pas vraiment appliqué. Cela devrait conduire à l'idée que le problÚme est lié au sujet, et donc au contexte de l'activité.
  3. Comprenez le graphique des dépendances et comprenez que le contexte d'application est fourni dans l'adaptateur.
  4. Dans ContestAdapter, changez le type de contexte en Activity.

le réparer! # 8

Publié par: Vali Ibragimov
Fichier binaire

Ivan a été chargé de refactoriser l'application pour obtenir une liste de tùches via ContentProvider à l'aide de DI. Il a refactorisé l'application à l'aide de Dagger, et lorsqu'elle a démarré, l'application a commencé à planter.

Aidez Ivan Ă  comprendre la raison:

  1. Spécifiez le numéro de ligne avec le nom de classe que vous souhaitez transférer vers une autre classe.
  2. Fournissez un lien vers la documentation avec une ancre vers un emplacement qui vous aidera Ă  comprendre pourquoi l'application se bloque.

Remarques
Format de réponse: [Nom de classe]: [Numéro de ligne] -> [Nom de classe]: [Numéro de ligne], [Lien]

Exemple de réponse:

FirstCLass: 12-> SecondClass: 45, https: //developer.android.com/reference/android/app/Activity#activity-lifecycle

Solution

La tĂąche se composait de deux parties:

1. Comprendre et fournir une solution de plantage dans ContentProvider lors de l'accÚs au composant DI. La bonne solution était de transférer l'initialisation du composant DI au ContentProvider.



La méthode onCreate sur ContentProvider est appelée plus tÎt que sur Application. Ceci est facile à vérifier si vous examinez le code de démarrage de l'application dans la classe ActivityThread et la méthode handleBindApplication.



2. Fournissez un lien décrivant que onCreate pour ContentProvider est appelé plus tÎt que pour Application. Google ne le mentionne pas sur la page de documentation ContentProvider. Mais voici un extrait de la description de la méthode onCreate Aplication:

Appelé au démarrage de l'application, avant la création de toute activité, service ou objet récepteur (à l'exception des fournisseurs de contenu).

le réparer! # 9

Auteur: Alexander Tsybin
Fichier binaire

Le développeur a été chargé d'implémenter la vue pour une liste d'éléments. Dans les savoirs traditionnels, il y avait une exigence importante - faire défiler la liste jusqu'au premier et au dernier élément afin que ces éléments apparaissent au centre de l'écran. Le développeur a implémenté la fonctionnalité, mais lors des tests, il s'est avéré que parfois le centrage ne fonctionne pas. Aidez le développeur à trouver les lignes problématiques dans le code.

Remarques
Format de réponse:

<Nom de fichier sans extension>: <Numéros de ligne séparés par des virgules>

<Nom de fichier sans extension>: <Numéros de ligne séparés par des virgules>

Les lignes doivent ĂȘtre triĂ©es par ordre lexicographique. Les numĂ©ros de ligne doivent ĂȘtre triĂ©s par ordre croissant.

Exemple de réponse:

BarFoo: 1

FooBar: 12,13,14,99

Solution

Il y a deux problĂšmes dans le code:

1. À en juger par le code, il est supposĂ© que l'appel Ă  onViewDetachedFromWindow se produira toujours aprĂšs onBindViewHolder, mais ce n'est pas le cas. La mĂ©thode double pour onViewDetachedFromWindow est onViewAttachedToWindow. Par consĂ©quent, holder.view.reset () est appelĂ© et le centrage disparaĂźt si le dĂ©filement est lent.

2. Sur Android avant la version 7, addView (rootView, params) ne fonctionne pas comme prévu.

Lorsque vous ajoutez une vue, un LayoutParam est généré, qui ressemble à ceci:

Guimauve - github.com/aosp-mirror/platform_frameworks_base/blob/marshmallow-release/core/java/android/widget/FrameLayout.java#L403
Nougat - github.com/aosp-mirror/platform_frameworks_base/blob/nougat-release/core/java/android/widget/FrameLayout.java#L384

FrameLayout.LayoutParams a deux constructeurs, dont l'un accepte ViewGroup.MarginLayoutParams et copie les marges en lui-mĂȘme: github.com/aosp-mirror/platform_frameworks_base/blob/nougat-release/core/java/android/view/ViewGroup.java#L14

Le deuxiĂšme constructeur accepte ViewGroup.LayoutParams et ignore les marges: github.com/aosp-mirror/platform_frameworks_base/blob/nougat-release/core/java/android/view/ViewGroup.java#L7328

Par conséquent, le centrage ne fonctionne pas sur Android jusqu'à la version 7.

La réponse est:
ItemView: 35
Activité principale: 53

Rétro-ingénierie


piratez-le! # 1

Auteur: Valentin Baryshev
Fichier binaire

Un dĂ©veloppeur s'est prĂ©cipitĂ© et n'a mĂȘme pas testĂ© le .apk final avant de se soumettre au concours.

Il s'est avéré qu'il a mélangé un groupe de vues dans activity_main.xml. De plus, il a placé chaque lettre de la clé dans une vue texte distincte. En conséquence, toutes les vues sur la mise en page se sont séparées et il y a une indignation totale à l'écran.

Essayez de dĂ©terminer quel groupe de vues doit ĂȘtre Ă  la place des groupes actuels.

La réponse au problÚme est le code qui apparaßt à l'écran aprÚs avoir remplacé le groupe de vues correct.

Solution

Il est nécessaire de décompiler .apk de n'importe quelle maniÚre disponible (par exemple, par des décompilateurs en ligne) et de recréer le projet par code.

Si nous analysons main_layout, nous pouvons deviner Ă  partir des attributs que le groupe de vues racine doit ĂȘtre ConstraintLayout.

RĂ©ponse: tniartsnoc

piratez-le! # 2

Auteur: Alexander Tsybin
Fichier binaire

Un utilisateur expérimenté a bloqué l'application. Il n'a pas été surpris, a ouvert les journaux, a trouvé la ligne à l'origine de l'erreur: N72XbphDx5NnFl6CKMNl8w == et l'a envoyée au support technique.

Il s'est avéré qu'il utilisait une version obsolÚte de l'application, qui avait été développée avant d'utiliser des référentiels de code, de sorte que le code de déchiffrement a été perdu. Cependant, il était possible de trouver le .apk de l'ancienne version de l'application.

Aidez à déchiffrer le message.

Solution

Il est nécessaire de décompiler .apk de n'importe quelle maniÚre disponible (par exemple, par des décompilateurs en ligne) et de recréer le projet par code.

Le code lui-mĂȘme contient Ă  la fois la fonction de cryptage et la fonction de dĂ©cryptage, Ă  laquelle vous pouvez donner la chaĂźne cryptĂ©e et obtenir une rĂ©ponse.

La réponse est:
TF: Bon travail!

piratez-le! # 3

Auteur: Valentin Baryshev
Fichier binaire

Connaissez-vous bien le cycle de vie d'une application Android?

Saviez-vous que dans certains cas, le systĂšme peut tuer le processus de demande?

Simulez cette situation sur l'application.

Vous verrez la réponse aprÚs avoir restauré l'application - sous forme de code.

Solution

Il suffisait d'installer .apk sur le tĂ©lĂ©phone, d'exĂ©cuter et d'obtenir le boĂźtier lorsque le systĂšme dĂ©charge l'application, mais en mĂȘme temps, il reste dans le rideau.

Une option consiste Ă  installer l'application, Ă  la lancer et Ă  la rĂ©duire (ou simplement Ă  passer Ă  une autre application). Ensuite, nous nous connectons via adb Ă  l'ordinateur. Dans Android Studio, sĂ©lectionnez l'onglet Logcat, sĂ©lectionnez le processus de cette application et cliquez sur l'icĂŽne d'arrĂȘt rouge. Ouvrez ensuite l'application prĂ©cĂ©demment rĂ©duite du rideau. L'Ă©cran affichera la rĂ©ponse: sss2384gxcxxX.

piratez-le! # 4

Auteur: Ivan Pukhov
Fichier binaire

Un collĂšgue front-end a postĂ© une dĂ©mo du nouveau module, mais a oubliĂ© d'envoyer des spĂ©cifications. Maintenant, il dort dans un fuseau horaire diffĂ©rent, et une dĂ©mo doit ĂȘtre faite en quelques minutes. Lors d'une conversation avec un collĂšgue, vous vous souvenez de sa phrase "Oui, c'est facile lĂ -bas, il vous suffit d'Ă©crire des talons et cela fonctionnera."

Téléchargez l'archive et essayez de vous en sortir sans collÚgue.

Solution

C'est une tùche simple, mais elle nécessite de la curiosité et de la persévérance.

TĂ©lĂ©chargez l'archive, tĂ©lĂ©chargez son contenu sur le serveur de test local, crĂ©ez un projet avec WebView et exĂ©cutez-le. AprĂšs un court dĂ©lai, nous voyons le message «Calibrer l'orientation». Et c'est tout. Mais le message suggĂšre que vous devriez peut-ĂȘtre tourner le tĂ©lĂ©phone entre vos mains? AprĂšs cela, un message suspect sur l'exception levĂ©e sera affichĂ© - avec un appel pour vĂ©rifier la sortie de la console.

Nous ouvrons la console et voyons un appel à une méthode inexistante sur l'objet API. Nous ajoutons un objet à WebView pour implémenter l'API JS et essayons à nouveau de tester l'application.

Apparemment, cela vaut la peine d'ajouter une valeur de retour. Nous sélectionnons, ajoutons - cela fonctionne. Mais il n'y a de ligne avec la réponse nulle part. Tournez à nouveau le téléphone - une nouvelle méthode est appelée. Ajoutez-le également. AprÚs plusieurs itérations, nous obtenons l'appel finalizeCalibrating puis storeResult, dans lequel notre ligne arrivera.

Lors du téléchargement de la source, vous pouvez ne rien vouloir résoudre, mais ouvrez-les et retirez la réponse. Cette méthode fonctionne, mais quelques petits rùteaux vous attendent en cours de route.

piratez-le! # 5

Auteur: Ivan Pukhov

L'assemblage de développement de la nouvelle version d'Android a fuité sur le réseau, sur lequel le nouveau mécanisme de signature d'application a été débogué directement sur l'appareil. Dans cet assemblage, la clé privée de signature est cousue dans le code source du systÚme et les développeurs ont laissé des parties de l'interface utilisateur pour la recevoir. Trouvez la clé.


Solution

1. Lorsque vous exĂ©cutez l'Ă©mulateur, vous remarquerez l'application SecretActivity. Si vous l'ouvrez, un toast apparaĂźt, vous proposant de consulter les journaux. Puisqu'il s'agit d'un assembly de dĂ©bogage, il existe de nombreux journaux - ils doivent ĂȘtre filtrĂ©s par le nom de l'application.

 adb logcat | grep 'SecretActivity' 

1898 1898 E SecretActivity: Try broadcast ZggwxXsaQ9SapPKyHpnwnYmALHazVFWX

2. Nous essayons de lancer une diffusion avec action Ă  partir du journal:

 adb shell am broadcast -a ZggwxXsaQ9SapPKyHpnwnYmALHazVFWX 

3. Un toast apparaĂźtra Ă  l'Ă©cran avec une proposition pour voir les journaux par la balise SecretReceiver.

 adb logcat | grep 'SecretReceiver' 

1898 1898 E SecretReceiver: ISecretService.aidl
1898 1898 E SecretReceiver: package android.app;
1898 1898 E SecretReceiver: interface ISecretService {
1898 1898 E SecretReceiver: String getSecret();
1898 1898 E SecretReceiver: }
1898 1898 E SecretReceiver: See global settings to bind SecretService


4. Nous examinons les paramĂštres globaux pour trouver une action pour se connecter Ă  SecretService.

 adb shell settings list global 

...
bind_secret_service_action=g8mNyGQZR8aHLTXNWcjdwJJYZ85Ewx83
...


5. Nous Ă©crivons un client pour le service ISecretService en utilisant cette interface aidl.

 public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = new Intent(); intent.setAction("g8mNyGQZR8aHLTXNWcjdwJJYZ85Ewx83"); PackageManager pm = getPackageManager(); List<ResolveInfo> resolveInfoList = pm.queryIntentServices(intent, 0); if (resolveInfoList == null || resolveInfoList.size() != 1) { return; } ResolveInfo serviceInfo = resolveInfoList.get(0); ComponentName component = new ComponentName(serviceInfo.serviceInfo.packageName, serviceInfo.serviceInfo.name); Intent explicitIntent = new Intent(intent); explicitIntent.setComponent(component); bindService(explicitIntent, new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { ISecretService secretService = ISecretService.Stub.asInterface(service); try { Log.d("MainActivity", secretService.getSecret()); } catch (Exception e) { Log.e("MainActivity", "RemoteException", e); } } @Override public void onServiceDisconnected(ComponentName name) { } }, Context.BIND_AUTO_CREATE); } } 

6. Exécutez, dans les journaux, nous obtenons la réponse:

 adb logcat | grep 'MainActivity' 

1898 1898 E MainActivity: VENHz=qWr7y!t3ZhP!8Skw!!kcTkt7V%

piratez-le! # 6

Auteur: Pavel Vorobkalov
Fichier binaire

M. Tupper a Ă©crit une application Android qui envoie des messages.

DĂ©chiffrez le message et continuez.

Format de réponse: chaßne sous forme de lien.

Solution

Installez et exĂ©cutez l'application sur l'appareil. Nous voyons deux boutons: Envoyer un message et Envoyer une formule. Lorsque vous cliquez dessus, rien de visible ne se produit. Vous devez dĂ©terminer comment et oĂč l'application envoie quelque chose.



Nous ouvrons les applications .apk en utilisant certains moyens de rétro-ingénierie et examinons l'activité principale. Vous pouvez y voir les méthodes onSendMessageClick et onSendFormulaClick. Ils créent un Intent avec l'action "com.yandex.tupper.action.NEW_MESSAGE" et en plus avec la clé "com.yandex.tupper.message" et l'envoient aux applications à partir de la liste (en utilisant la méthode générale). Ils obtiennent la liste en appelant PackageManager.queryBroadcastReceivers.

Vous pouvez continuer à utiliser des outils d'ingénierie inverse pour étudier le message en l'extrayant du code de l'application. Mais ce chemin est intentionnellement (quoique légÚrement) compliqué par un cryptage RSA supplémentaire.

Il existe un autre moyen qui correspond mieux à la programmation Android. Vous devez écrire une application qui gÚre cette intention de diffusion. Nous créons notre application, dans le récepteur du registre des manifestes:

  <receiver android:name=".TupperMessageReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.yandex.tupper.action.NEW_MESSAGE" /> </intent-filter> </receiver> 

Dans le gestionnaire, nous recevons le message de extra et exécutons notre propre activité, dans laquelle nous afficherons le résultat du traitement:

 public class TupperMessageReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE); if (message == null) { return; } Intent activityIntent = new Intent(context, MainActivity.class); activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); activityIntent.putExtra(MainActivity.EXTRA_MESSAGE, message); context.startActivity(activityIntent); } } 

Voyons maintenant le dĂ©chiffrement du message lui-mĂȘme. Si vous recherchez dans Yandex le «message de Tupper», vous pouvez trouver un article sur la formule de Wupper pour la formule de Tupper. Il prĂ©sente un exemple de formule codĂ©e sous la forme d'un nombre:

48584 ...
4858450636189713423582095962494202044581400587983244549483093085061934704708809928450644769865524364849997247024915119110411605739177407856919754326571855442057210445735883681829823754139634338225199452191651284348332905131193199953502413758765239264874613394906870130562295813219481113685339535565290850023875092856892694555974281546386510730049106723058933586052544096664351265349363643957125565695936815184334857605266940161251266951421550539554519153785457525756590740540157929001765967965480064427829131488548259914721248506352686630476300

Ce numĂ©ro est envoyĂ© sous forme de message en cliquant sur le bouton Envoyer la formule. Le rĂ©sultat du dĂ©codage du numĂ©ro est Ă©galement dans l'article Wikipedia. Il peut ĂȘtre utilisĂ© pour dĂ©boguer sa mise en Ɠuvre de l'algorithme de dĂ©codage.

Nous implémentons l'algorithme de décodage en Java et, pour des raisons de beauté, convertissons le nombre binaire résultant en un bitmap monochrome:

  public class TupperCodec { static final int TUPPER_R = 17; private TupperCodec() {} public static BigInteger decodeFromDecString(String decString) throws NumberFormatException { BigInteger data = new BigInteger(decString, 10); return data.divide(BigInteger.valueOf(TUPPER_R)); } public static String getNumberAsBinImage(BigInteger number) { String binString = number.toString(2); StringBuffer result = new StringBuffer(binString.length()); int i; for (i = 0; i < binString.length(); i++) { if ((i % TUPPER_R) == 0 && i > 0) { result.append('\n'); } result.append(binString.charAt(binString.length() - 1 - i)); } int numberOfRows = (binString.length() + TUPPER_R - 1) / TUPPER_R; for (; i < numberOfRows * TUPPER_R; i++) { result.append(~_~quot&#0;quot~_~); } return result.toString(); } public static Bitmap getBinImageAsBitmap(String binImage) { String[] lines = binImage.split("\n"); int[] colors = new int[lines.length * TUPPER_R]; for (int i = 0; i < lines.length; i++) { String line = lines[i]; assert line.length() == TUPPER_R; for (int j = 0; j < line.length(); j++) { colors[i * TUPPER_R + j] = line.charAt(j) == '0' ? Color.WHITE : Color.BLACK; } } return Bitmap.createBitmap(colors, TUPPER_R, lines.length, Bitmap.Config.ARGB_8888); } } 

Dans Activity, appelez le décodage et affichez le résultat sous forme de bitmap dans ImageView:

  public class MainActivity extends AppCompatActivity { static final String EXTRA_MESSAGE = "com.yandex.tupper.message"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = getIntent(); if (intent != null && intent.hasExtra(MainActivity.EXTRA_MESSAGE)) { String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE); try { BigInteger data = TupperCodec.decodeFromDecString(message); String binImage = TupperCodec.getNumberAsBinImage(data); Bitmap bitmap = TupperCodec.getBinImageAsBitmap(binImage); ImageView imageView = findViewById(R.id.message_image); imageView.setImageBitmap(bitmap); CharSequence toastText = TextUtils.ellipsize(message, new TextPaint(), 400, TextUtils.TruncateAt.END); Toast.makeText(this, toastText, Toast.LENGTH_SHORT).show(); } catch (NumberFormatException ex) { Toast.makeText(this, getString(R.string.invalid_message), Toast.LENGTH_LONG).show(); } } } } 


Capture d'écran du message décodé

La réponse est clck.ru/FevR5 .

piratez-le! # 7

Auteur: Igor Eremeev
Fichier binaire

Il est nécessaire de casser l'algorithme de vérification des clés dans l'application.

. , , .

: - .

Solution

.apk zip- classes.dex — Java- . , , dex2jar Android Studio. dex2jar Java-, Android Studio class-. , .

, proguard ( android.support.* androidx.*, ), — com.yandex.contest.keygenme.MainActivity, Activity . , : MainActivity.a#doInBackground bbaab b , .



c d. N- π ( --), . , π hexed [c[i] + key[i]] = d[i], i = 0..11. , . 50 hex- π. : 537306089144. key_2056BE33E064.

, , — . doInBackground:



, , if (var12[0] == var8) {
 }. var7 — i- . , var7 0 9 . . , .apk ( JVM) .

hack it! #8

:


N , . : Android 4.4, 1200 × 600, FM-. , , FM- . FM- , .

, FM-. .

, :

1. 106.00
2. (seek)
3. ,

, .

Remarques
: . .

: , a(106.00) 106.00 , b(true) , c() , a(106.00);b(true);c()

Solution

: , FM- . .apk dex-. Java. , , — jadx.

.apk, . MainActivity. : fragment_holder RadioFullFragment . . RadioFullPresenter, ChangeRadioFrequencyUsecase, RadioRepo. .

«implements RadioRepo». : RadioRepoImpl. , b. ServiceConnection — . , b lambda$new$0$RadioRepoImpl. , $$Lambda$RadioRepoImpl$FYTR9gAgZEGvrLjkbkpAIup7OKw, serviceConnection IRadioService radioService. , RadioService.

: onServiceConnected «b radioManager = Stub.asInterface(service)». , . «bindService». bindService «com.some_company.help_service». , com.some_company.carradio.FmUtils. , , . , : j , : 8880 88.00 . 8800. a. , .

. : k z. RadioRepoImpl, enableRadio() disableRadio() k. z(boolean isForwardDirection), « (seek) ».

: a(10600);z(true);j().

hack it! #9

:


flatbuffers . GPX . . , .

FlatBuffers :

 namespace ru.yandex.android.task; struct GeoPoint { latitude:double; longitude:double; } table Points { items:[GeoPoint]; } root_type Points; 

: , sha256. .

Solution

, FlatBuffers.

1. fbs- .
2. Java-. JVM- , Gradle flatbuffers.
3. fbs-.
4. fbs-, - — . , FlatBuffers.
5. , . structs , struct table. .
6. Kotlin. FlatBuffers.

fbs , . FlatBufferBuilder input. input :

 val pointsList = Points.getRootAsPoints(buffer) val restoredPoints = (0 until pointsList.itemsLength()) .mapNotNull(pointsList::items) 

c , (lat, long). as-is GPX-.

GPX- ( ) . GPX XML. , XML- . XML- GPX-track-. XML- MAP- . «yandroid».

, . CLI-:

 echo "yandroid" | shasum -a 256 

.

Android


dig it! #1

:


QA- .

:

— ( 'Screen 1')
— GO NEXT ( 'Screen 2')
— GO BACK
— GO NEXT,
— 5–10

: 'Screen 2'.

: , « ».

.apk .

Remarques


C stack trace, .

Exemple

stack trace 


 at android.os.MessageQueue.nativePollOnce(Native method) at android.os.MessageQueue.next(MessageQueue.java:326) at android.os.Looper.loop(Looper.java:160) at com.android.server.SystemServer.run(SystemServer.java:454) at com.android.server.SystemServer.main(SystemServer.java:294) at java.lang.reïŹ‚ect.Method.invoke(Native method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:838) 


 , RuntimeInit.java, 493, :

 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 

Solution

, , « ».

/data/anr/traces.txt /data/anr/anr_* (https://developer.android.com/topic/performance/vitals/anr#pull_a_traces_file). adb-:

 adb root adb shell ls /data/anr adb pull /data/anr/<filename> 

( adb root , Google API.)

stack trace . — stack trace, :

 com.droid.mission.anrapk.MainActivity$2.onClick(MainActivity.java:43) 



dig it! #2

:

Android Studio, , MultiDex. -, apk- .dex- (65536). / apk Andorid Studio .dex-.

— dex- , dex- ( , ) . dex- .

: multidex , android gradle plugin 3.0.0 – 3.1.4

.

?

Solution

, dex-. Gradle — , , .apk.

Java- dex- ART/Dalvik: transformClassesWithDexBuilderForDebug. dex-, . Android Gradle plugin (AGP) Gradle . , — , Gradle . , . AGP, — DSL- Gradle build.gradle.

3.1.* minimal-main-dex. , .

dig it! #3

:
— - .

?

: — .

Solution

, . Android WindowManager. . , WindowManager.LayoutParams . , , TYPE_APPLICATION .

, , . , , - .

, Android SDK, , . , WindowManager AOSP. , , , .

, , TYPE_BOOT_PROGRESS . getWindowLayerFromTypeLw WindowManagerPolicy. Javadoc :

Returns the layer assignment for the window type. Allows you to control how different kinds of windows are ordered on-screen

, , . switch-case , layer — TYPE_POINTER, 2018. .

dig it! #4

:

, - , .

. , , , .

Android , ?

Solution

, , Android SDK, .

startActivity Context . Context Activity — , startActivity . startActivityForResult. , Activity mInstrumentation.execStartActivity .

? startActivity ActivityManagerService, ServiceManager.getService(Context.ACTIVITY_SERVICE).

ActivityManagerService . startActivityAsUser, startActivity, ActivityStarter execute. , .

C startActivity . , abort , true START_ABORTED Activity.

, , mService.mIntentFirewall.checkStartActivity. , . :

This is called from ActivityManager to check if a start activity intent should be allowed.

, IntentFirewall . RULES_DIR . : ifw.

dig it! #5

:

. , (, ) — 8800. , , (+8800). , 8800 +8800 , / , .

. . , +8800 8800 .

, (java/c/c++). , .

: source.android.com
— : androidxref.com

Solution

Dialer , , URI authorities = "content://com.android.contacts" .

ContentProvider. URI ContactsPoviider. : , SQL- . :

 sb.append("PHONE_NUMBERS_EQUAL(" + Tables.DATA + "." + Phone.NUMBER + ", "); 

PHONE_NUMBERS_EQUAL — SQL-. sqlite .

sqlite, , . ContactsProvider , config_use_strict_phone_number_comparation, . :

 <bool name="config_use_strict_phone_number_comparation">true</bool> 

Source: https://habr.com/ru/post/fr484876/


All Articles