Reparación, pirateo, excavación. Resolviendo la misión en línea Droid Mission



El año pasado, realizamos una búsqueda en línea para desarrolladores móviles: Droid Mission. En un mes, los participantes tuvieron que resolver tantos problemas como sea posible en tres direcciones: ¡arréglenlo! (búsqueda de errores e investigación de código), ¡córtalo! (ingeniería inversa) y cavarlo! (aprendiendo las características de Android). En total, la búsqueda tuvo 23 tareas: son muy similares a las que los especialistas de Android encuentran en el trabajo real. En la publicación mostraremos todas las condiciones y las soluciones correctas.


Buscar errores e investigar códigos


arreglarlo! # 1

Autor: Anastasia Laushkina

En un tranquilo viernes por la noche, dos agentes se quedan despiertos hasta tarde y lideran una disputa insoluble. Se les indicó que almacenaran una gran cantidad de datos secretos en una base de datos Sqlite con una clave de clave entera.

El Agente A crea una tabla con una consulta de la forma: CREATE TABLE t (key INT PRIMARY KEY, secret_value_1, secret_value_2), y el agente B con una consulta de la forma CREATE TABLE t (key INTEGER PRIMARY KEY ASC, secret_value_1, secret_value_2).

El Agente A está absolutamente seguro de que no hay error en su solicitud, sin embargo, el Agente B tiene un argumento en contra de esto: afirma que la integridad de los datos durante el acercamiento del Agente A puede ser violada.

Enumere con una coma: la columna o columnas que son responsables de la integridad del Agente B, pero no son responsables del Agente A; así como la estructura de datos, en ruso, por lo que la solicitud del agente B será más rápida.

Notas
Formato de respuesta: [nombre de columna 1], [nombre de columna N], [nombre de estructura] (sin corchetes, con una letra minúscula)

Ejemplo de respuesta: x, y, matriz

Solución

Se trata de rowId y su relación con la clave primaria.

En el caso de "CREATE TABLE t (key INTEGER PRIMARY KEY ASC, secret_value_1, secret_value_2)" la clave es un alias para rowId. En el caso de "CREATE TABLE t (key INT PRIMARY KEY, secret_value_1, secret_value_2)" - no. El motivo se puede encontrar en la documentación .

Por lo tanto, en ausencia de una relación clave con rowId, se ejecutará con éxito una solicitud del formulario "INSERTAR EN LOS VALORES (" algunos "," y "," z ")", lo que podría violar la integridad de los datos. Si existe tal conexión, se producirá un error de discordancia de tipo de datos.

La respuesta se tomó como un par de clave, rowId y key / rowId por separado (porque si hay una conexión, se refieren a la misma columna).

La velocidad se logra mediante el almacenamiento en forma de un árbol de búsqueda.

arreglarlo! # 2

Autor: Anastasia Laushkina
Archivo binario

Para detectar enemigos rápidamente, el Agente Kew está desarrollando una aplicación con una lista de criminales conocidos. Se las arregló para mostrar fácilmente esta lista en la aplicación, pero sobre esto decide que esa información importante debería estar en la pantalla principal.

Para hacer esto, necesita un widget. Un problema: por alguna razón, el widget no muestra una lista. Un instinto le dice al agente que el problema está solo en una línea.

Nómbrelo con una coma, así como la línea por la que necesita reemplazarlo, para obtener la solución más óptima.

Notas
Formato de respuesta: [línea 1], [línea 2] (sin corchetes)

Ejemplo de respuesta: FrameLayout, ScrollView

Solución

1. Estamos buscando el archivo de diseño para el widget, vemos un elemento ConstraintLayout no válido en él (consulte developer.android.com/guide/topics/appwidgets#CreatingLayout ).
2. Cámbielo a lo aceptable y óptimo: LinearLayout / FrameLayout.

A pesar de que el uso de FrameLayout rompió el diseño de los elementos en el widget, todavía se aceptó como respuesta.

arreglarlo! # 3

Autor: Artyom Viter
Archivo binario

Los probadores notaron un aumento anormal en el consumo de RAM. Existe la sospecha de que hay una fuga en alguna parte.

Tienes que investigar y localizar el problema. En la respuesta, indique el nombre del método de la clase de aplicación y el lugar de su llamada, lo que conduce a una pérdida de memoria. Si encuentra varios problemas, enumere todos.

Formato de respuesta: [Nombre de la clase en la que se llama el método]: [número de línea en este archivo]: [nombre del método] (sin corchetes)

Indique varias respuestas en orden lexicográfico, separadas por un #.

Un ejemplo:

Si cree que hay una pérdida de memoria en el archivo ExampleFileName.java,

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

Ejemplo de respuesta: ExampleFileName: 8: problemMethodName

Solución

La primera forma es el análisis de código.

  1. Estudiamos el código de la aplicación.
  2. La primera fuga es obvia. Este es un registro de BroadCastReceiver en la clase SecondActivity. Se puede ver un mensaje sobre este problema en logcat si ejecuta la aplicación.
  3. Si presta atención al método startTimer (), notará que la clase CountDownTimer agrega un cierre al controlador principal de hilos con un enlace a textView. Y antes de que se destruya la actividad, la clase instancia (CountDownTimer) no llama al método cancel (). Esta es la segunda pérdida de memoria.

La segunda forma son las herramientas. Puede implementar la biblioteca de fugas en la aplicación, obtener un volcado de pila a través de adb o Android Profiler y analizar los informes recibidos. Eclipse Memory Analyzer (MAT) puede ayudar con el análisis. Para abrir heapdump en MAT, debe convertirlo a un formato comprensible para MAT utilizando la utilidad hprof-convd.

arreglarlo! # 4

Publicado por: Vali Ibragimov
Archivo binario

Ivan tiene su primer día en una sala de cine en línea. Como es un desarrollador genial, se le pidió de inmediato que arreglara el bloqueo cuando veía una película. Decidió al mismo tiempo refactorizar. El código se ha vuelto más conciso y limpio, el error ha dejado de reproducirse.

Sin embargo, más tarde resultó que la posición actual de la película no se envió al backend. Ayuda a Ivan a entender qué clase mezcló.

La respuesta es el nombre de la clase que debe usar.

Solución

Para enviar una posición de visualización cada cinco segundos e ir a la red, se utiliza ExecutorService. Ivan pensó en crear un ScheduledThreadPoolExecutor para una tarea periódica: ¿por qué no usarlo para ir a la red? Después de todo, ScheduledThreadPoolExecutor es el descendiente de ThreadPoolExecutor. Ivan ni siquiera sospechaba que ScheduledThreadPoolExecutor usa solo un hilo, y cuando vaya a la red, deberá esperar a que se complete la tarea periódica.

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

La solución al problema se complicó aún más por el hecho de que no estábamos accediendo a ExoPlayer en el hilo de la interfaz de usuario, y en la nueva versión 2.9. * ExoPlayer comenzó a escribir advertencias sobre esto en los registros. Pero ahora esto es solo una advertencia, y no un error cometido por Ivan.

arreglarlo! # 5

Autor: Alexander Tsybin
Archivo binario

Una compañía estaba desarrollando un juego de Farmer Simulator.

En este juego, solo un jugador podría moverse libremente. Todos los demás objetos estaban inmóviles o tenían una trayectoria de movimiento claramente definida. En algún momento, decidieron agregar enemigos al juego que se convertirían en un obstáculo para el jugador.

El desarrollador Vasya recibió instrucciones de darse cuenta de los enemigos. Durante la implementación, Vasya encontró un problema de rendimiento. Decidió arreglarlo debido a subprocesos múltiples. Como Vasya no estaba familiarizado con el subprocesamiento múltiple, aprovechó las soluciones de stackoverflow. Entonces se deshizo de los problemas de rendimiento.

Sin embargo, en la etapa de prueba, se descubrió que a veces el juego se congela. Ayuda a Vasya a encontrar la razón.

Como respuesta, ingrese los números de las líneas de código problemáticas, separadas por comas, en orden ascendente y sin espacios. Así: 1,17,42

Solución

Los tipos primitivos de boxeo (por ejemplo, int en Integer) son operaciones costosas. Para fines de optimización, las máquinas Java a veces reutilizan dichos objetos. Las líneas no son la excepción.

Las líneas ENEMY_LOCK y DECISIONS_LOCK se usan en el código para la sincronización. Entonces, si hay lugares en el proyecto con sincronización en las mismas líneas, puede ocurrir un punto muerto.

En general, no puede usar String / Integer, etc., para la sincronización.

Respuesta: 7.8

arreglarlo! # 6

Autor: Ivan Pukhov
Archivo binario

El equipo de procesamiento de imágenes ha lanzado una nueva biblioteca nativa, debe probarla y señalar los problemas, si los hay.

Solución

Esta es una tarea fácil de calentar. En los recursos se encuentra la biblioteca en sí y el archivo de encabezado correspondiente. En el encabezado, la única función que devuelve un objeto de trabajo.

¡No queda nada por hacer sino crear un proyecto de prueba (instalando NDK) y llamarlo! Una llamada a la función en el hilo principal arrojará una excepción pidiendo no llamarlo de esta manera. Transferimos la ejecución a otro hilo de cualquier manera, y todo funciona.

Ahora vale la pena verificar el objeto devuelto. ¿Es realmente un mapa de bits? La forma más fácil es mostrarlo inmediatamente en un ImageView. Veremos una imagen con una cadena de texto, y la tarea requiere una respuesta también en forma de cadena. Esto probablemente no sea solo una coincidencia. :)

arreglarlo! # 7

Autor: Dmitry Zaitsev
Archivo binario

Hay una aplicación con códigos donde se enmarca el código deseado. Desafortunadamente, el diseño se rompió y necesita ser restaurado para encontrar el código.

Notas
Después de corregir el código, el marco pasará por exactamente 22 caracteres que deben enumerarse en sentido antihorario, comenzando desde la esquina superior izquierda del marco.

Ejemplo de respuesta: ScQG1jxbazmjbcARefbRMo

Solución

Ahora Dagger se ha convertido en el estándar de facto en el desarrollo de Android. Cuando la DI ya está configurada, es muy simple escribir Inyectar delante del constructor y entregarle el resto del trabajo a Dagger. Es tan fácil que luego puede dejar de pensar en cómo se proporcionan las dependencias. También en Android, a menudo tiene que trabajar con la clase Context y sus descendientes: Aplicación y Actividad. La aplicación está vinculada al ciclo de vida de la aplicación, y la Actividad está vinculada al ciclo de vida de la pantalla. Por lo tanto, al trabajar con estas clases, es importante observar muchas características. La tarea se basa precisamente en el uso del contexto incorrecto, y todo esto se disfraza con DI.

Para resolver, puede realizar los siguientes pasos:

  1. Examine los archivos del proyecto y observe que ItemStyle se aplica a todas las vistas de texto.
  2. Ver que el estilo no se aplica realmente. Esto debería llevar a la idea de que el problema está relacionado con el tema y, por lo tanto, con el contexto de la Actividad.
  3. Comprenda el gráfico de dependencia y comprenda que el contexto de la aplicación se proporciona en el adaptador.
  4. En el ContestAdapter, cambie el tipo de contexto a Actividad.

arreglarlo! # 8

Publicado por: Vali Ibragimov
Archivo binario

Ivan recibió la tarea de refactorizar la aplicación para obtener una lista de tareas a través de ContentProvider usando DI. Refactorizó la aplicación usando Dagger, y cuando comenzó, la aplicación comenzó a fallar.

Ayuda a Ivan a entender la razón:

  1. Especifique el número de línea con el nombre de la clase que desea transferir a otra clase.
  2. Proporcione un enlace a la documentación con un ancla a un lugar que lo ayudará a comprender por qué la aplicación falla.

Notas
Formato de respuesta: [Nombre de clase]: [Número de línea] -> [Nombre de clase]: [Número de línea], [Enlace]

Ejemplo de respuesta:

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

Solución

La tarea constaba de dos partes:

1. Comprenda y proporcione una solución de bloqueo en ContentProvider al acceder al componente DI. La solución correcta era transferir la inicialización del componente DI al ContentProvider.



El método onCreate en ContentProvider se llama antes que en la Aplicación. Esto es fácil de verificar si examina el código de inicio de la aplicación en la clase ActivityThread y el método handleBindApplication.



2. Proporcione un enlace que describa que onCreate para ContentProvider se llama antes que para la Aplicación. Google no menciona esto en la página de documentación de ContentProvider. Pero aquí hay un extracto de la descripción del método de aplicación onCreate:

Se llama cuando se inicia la aplicación, antes de que se haya creado cualquier actividad, servicio u objeto receptor (excluyendo proveedores de contenido).

arreglarlo! # 9

Autor: Alexander Tsybin
Archivo binario

El desarrollador recibió la tarea de implementar la Vista para alguna lista de elementos. En TK había un requisito importante: desplazar la lista al primer y último elemento para que estos elementos aparezcan en el centro de la pantalla. El desarrollador implementó la funcionalidad, pero al probarlo resultó que a veces el centrado no funciona. Ayude al desarrollador a encontrar las líneas problemáticas en el código.

Notas
Formato de respuesta:

<Nombre de archivo sin extensión>: <Números de línea separados por comas>

<Nombre de archivo sin extensión>: <Números de línea separados por comas>

Las líneas deben clasificarse en orden lexicográfico. Los números de línea deben clasificarse en orden ascendente.

Ejemplo de respuesta:

BarFoo: 1

FooBar: 12,13,14,99

Solución

Hay dos problemas en el código:

1. A juzgar por el código, se supone que la llamada a onViewDetachedFromWindow siempre ocurrirá después de onBindViewHolder, pero esto no es así. El método gemelo para onViewDetachedFromWindow es onViewAttachedToWindow. Como resultado, se llama a holder.view.reset () y el centrado desaparece si el desplazamiento es lento.

2. En Android anterior a la versión 7, addView (rootView, params) no funciona como se esperaba.

Cuando agrega una Vista, se genera un LayoutParam, que se ve así:

Marshmallow - github.com/aosp-mirror/platform_frameworks_base/blob/marshmallow-release/core/java/android/widget/FrameLayout.java#L403
Turrón - github.com/aosp-mirror/platform_frameworks_base/blob/nougat-release/core/java/android/widget/FrameLayout.java#L384

FrameLayout.LayoutParams tiene dos constructores, uno de los cuales acepta ViewGroup.MarginLayoutParams y copia los márgenes en sí mismo: github.com/aosp-mirror/platform_frameworks_base/blob/nougat-release/core/java/android/view/ViewGroup.java#L14

El segundo constructor acepta ViewGroup.LayoutParams e ignora los márgenes: github.com/aosp-mirror/platform_frameworks_base/blob/nougat-release/core/java/android/view/ViewGroup.java#L7328

Como resultado, el centrado no funciona en Android hasta la versión 7.

La respuesta es:
Vista de artículo: 35
Actividad principal: 53

Ingeniería inversa


hackearlo! # 1

Autor: Valentin Baryshev
Archivo binario

Un desarrollador se apresuró y ni siquiera probó el .apk final antes de enviarlo al concurso.

Resultó que mezcló un grupo de vistas en activity_main.xml. Además, colocó cada letra de la llave en una vista de texto separada. Como resultado, todas las vistas en el diseño se separaron y hay una indignación total en la pantalla.

Intente determinar qué grupo de vista debe estar en lugar de los actuales.

La respuesta al problema es el código que aparece en la pantalla después de sustituir el grupo de visualización correcto.

Solución

Es necesario descompilar .apk de cualquier forma disponible (por ejemplo, mediante descompiladores en línea) y recrear el proyecto por código.

Si analizamos main_layout, entonces podemos adivinar a partir de los atributos que el grupo de vista raíz debería ser ConstraintLayout.

Respuesta: tniartsnoc

hackearlo! # 2

Autor: Alexander Tsybin
Archivo binario

Un usuario experimentado bloqueó la aplicación. No se sorprendió, abrió los registros, encontró la línea que causó el error: N72XbphDx5NnFl6CKMNl8w == y lo envió al soporte técnico.

Resultó que estaba usando una versión desactualizada de la aplicación, que había sido desarrollada antes de usar repositorios de código, por lo que se perdió el código de descifrado. Sin embargo, fue posible encontrar .apk de la versión anterior de la aplicación.

Ayuda a descifrar el mensaje.

Solución

Es necesario descompilar .apk de cualquier forma disponible (por ejemplo, mediante descompiladores en línea) y recrear el proyecto por código.

El código en sí contiene tanto la función de cifrado como la función de descifrado, a las que puede dar la cadena cifrada y obtener una respuesta.

La respuesta es:
TF: ¡Buen trabajo!

hackearlo! # 3

Autor: Valentin Baryshev
Archivo binario

¿Qué tan bien conoce el ciclo de vida de una aplicación de Android?

¿Sabía que en algunos casos el sistema puede matar el proceso de solicitud?

Simule esta situación en la aplicación.

Verá la respuesta después de restaurar la aplicación, en forma de código.

Solución

Fue suficiente para instalar .apk en el teléfono, ejecutar y obtener el caso cuando el sistema descarga la aplicación, pero al mismo tiempo permanece en la cortina.

Una opción es instalar la aplicación, iniciarla y minimizarla (o simplemente cambiar a otra aplicación). Luego nos conectamos vía adb a la computadora. En Android Studio, seleccione la pestaña Logcat, seleccione el proceso de esta aplicación y haga clic en el icono de detención rojo. Luego abra la aplicación previamente minimizada desde la cortina. La pantalla mostrará la respuesta: sss2384gxcxxX.

hackearlo! # 4

Autor: Ivan Pukhov
Archivo binario

Un colega de front-end publicó una demostración del nuevo módulo, pero olvidó enviar especificaciones. Ahora duerme en una zona horaria diferente, y una demostración debe hacerse en unos minutos. De una conversación con un colega, recuerdas su frase "Sí, es fácil allí, solo necesitas escribir talones y funcionará".

Descargue el archivo e intente hacer frente sin un colega.

Solución

Esta es una tarea simple, pero requiere cierta curiosidad y perseverancia.

Descargue el archivo, cargue su contenido en el servidor de prueba local, cree un proyecto con WebView y ejecútelo. Después de un breve tiempo de espera, vemos el mensaje "Calibrar orientación". Y eso es todo. ¿Pero el mensaje sugiere que tal vez deberías girar el teléfono en tus manos? Después de eso, se mostrará un mensaje sospechoso sobre la excepción lanzada, con una llamada para verificar la salida de la consola.

Abrimos la consola y vemos una llamada a un método inexistente en el objeto API. Agregamos un objeto a WebView para implementar la API JS y nuevamente intentamos probar la aplicación.

Aparentemente, vale la pena agregar un valor de retorno. Seleccionamos, agregamos, funciona. Pero no hay una línea con la respuesta en ninguna parte. Vuelva a encender el teléfono: se llama a un nuevo método. Añádelo también. Después de varias iteraciones, recibimos la llamada finalizeCalibrating y luego storeResult, en el que llegará nuestra línea.

Al descargar la fuente, es posible que no desee resolver nada, pero ábralos y extraiga la respuesta. Este método funciona, pero un par de pequeños rastrillos te esperan en el camino.

hackearlo! # 5

Autor: Ivan Pukhov

El ensamblaje de desarrollo de la nueva versión de Android se filtró a la red, en la cual el nuevo mecanismo de firma de aplicaciones se depuró directamente en el dispositivo. En este ensamblaje, la clave privada para firmar se cose en el código fuente del sistema, y ​​los desarrolladores dejaron partes de la interfaz de usuario para recibirla. Encuentra la llave.


Solución

1. Cuando ejecute el emulador, notará la aplicación SecretActivity. Si lo abres, aparece un brindis que ofrece ver los registros. Como se trata de un ensamblaje de depuración, hay muchos registros; deben filtrarse por el nombre de la aplicación.

 adb logcat | grep 'SecretActivity' 

1898 1898 E SecretActivity: Try broadcast ZggwxXsaQ9SapPKyHpnwnYmALHazVFWX

2. Intentamos lanzar difusión con acción desde el registro:

 adb shell am broadcast -a ZggwxXsaQ9SapPKyHpnwnYmALHazVFWX 

3. Aparecerá un brindis en la pantalla con una propuesta para ver los registros con la etiqueta 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. Observamos la configuración global para encontrar una acción para conectarnos a SecretService.

 adb shell settings list global 

...
bind_secret_service_action=g8mNyGQZR8aHLTXNWcjdwJJYZ85Ewx83
...


5. Estamos escribiendo un cliente para el servicio ISecretService utilizando esta interfaz de ayuda.

 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. Ejecutar, en los registros obtenemos la respuesta:

 adb logcat | grep 'MainActivity' 

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

hackearlo! # 6

Autor: Pavel Vorobkalov
Archivo binario

El Sr. Tupper escribió una aplicación de Android que envía mensajes.

Descifra el mensaje y sigue adelante.

Formato de respuesta: cadena como enlace.

Solución

Instala y ejecuta la aplicación en el dispositivo. Vemos dos botones: Enviar mensaje y Enviar fórmula. Cuando haces clic en ellos, no sucede nada visible. Debe averiguar cómo y dónde la aplicación envía algo.



Abrimos aplicaciones .apk usando algunos medios para la ingeniería inversa y miramos la Actividad principal. Allí puede ver los métodos onSendMessageClick y onSendFormulaClick. Crean un Intent con la acción "com.yandex.tupper.action.NEW_MESSAGE" y extra con la clave "com.yandex.tupper.message" y lo envían a las aplicaciones desde la lista (utilizando el método general). Obtienen la lista llamando a PackageManager.queryBroadcastReceivers.

Puede continuar utilizando herramientas de ingeniería inversa para estudiar el mensaje extrayéndolo del código de la aplicación. Pero este camino es intencionalmente (aunque ligeramente) complicado por el cifrado RSA adicional.

Hay otra forma que coincide mejor con la programación de Android. Debe escribir una aplicación que maneje esta intención de transmisión. Creamos nuestra aplicación, en el receptor de registro manifiesto:

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

En el controlador, recibimos el mensaje de extra y ejecutamos nuestra propia Actividad, en la que mostraremos el resultado del procesamiento:

 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); } } 

Ahora tratemos con el descifrado del mensaje en sí. Si busca en Yandex el "mensaje de Tupper", puede encontrar un artículo sobre la fórmula de Tupper de Wupper. Tiene un ejemplo de una fórmula codificada en forma de número:

48584 ...
4858450636189713423582095962494202044581400587983244549483093085061934704708809928450644769865524364849997247024915119110411605739177407856919754326571855442057210445735883681829823754139634338225199452191651284348332905131193199953502413758765239264874613394906870130562295813219481113685339535565290850023875092856892694555974281546386510730049106723058933586052544096664351265349363643957125565695936815184334857605266940161251266951421550539554519153785457525756590740540157929001765967965480064427829131488548259914721248506352686630476300

Este número se envía como mensaje haciendo clic en el botón Enviar fórmula. El resultado de decodificar el número también se encuentra en el artículo de Wikipedia. Se puede usar para depurar su implementación del algoritmo de decodificación.

Implementamos el algoritmo de decodificación en Java y, por razones de belleza, convertimos el número binario resultante en un mapa de bits monocromo:

  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); } } 

En Actividad, llame a la decodificación y muestre el resultado como un mapa de bits en 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(); } } } } 




clck.ru/FevR5 .

hack it! #7

:


.

. , , .

: - .



.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. ,

, .

: . .

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



: , 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. .



, 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 .



C stack trace, .

Ejemplo

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.reflect.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) 



, , « ».

/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

.

?



, 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

:
— - .

?

: — .



, . 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 , ?



, , 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



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/484876/


All Articles