Lista de ejercicios para practicar
A.1. Tipos de Datos y Variables
Calculadora de Almacenamiento de Datos Primitivos:
- Declara variables para cada uno de los tipos de datos primitivos enteros (
byte,short,int,long) e inicialízalas con valores que se acerquen a sus límites (tanto positivos como negativos). Imprime cada valor. - Haz lo mismo para los tipos de punto flotante (
float,double). - Declara una variable
chare inicialízala con un carácter Unicode (p.ej., un símbolo o una letra no inglesa). Imprímela. - Declara una variable
booleane imprímela. - Reflexión: ¿Cómo se comportaría un
intde C++ que intente almacenar un valor mayor al de unintde Java? ¿Qué pasa si intentas asignar un valor muy grande a unbyteen Java?
- Declara variables para cada uno de los tipos de datos primitivos enteros (
Simulador de Pila y Heap (Conceptual):
- Escribe un pequeño programa donde declares algunas variables locales primitivas dentro de un método.
- Luego, crea un objeto simple (p.ej.,
Object obj = new Object();) y un array de enteros (int[] numeros = new int[3];). - Dibuja en un papel cómo crees que se verían la pila (stack) y el montículo (heap) en ese momento, indicando dónde residirían las variables primitivas, las referencias y los objetos/arrays en sí.
- Reflexión: Si asignas una variable de array a otra (
int[] copiaNumeros = numeros;), ¿qué sucede en la memoria? ¿Estás copiando el array o la referencia?
Manejo de Posibles "unsigned int":
- Imagina que recibes un valor de 32 bits de una fuente externa que conceptualmente es un "unsigned int" (p.ej.,
0xFFFFFFFF). En Java, esto se interpretaría como-1si se almacena en unint. - Lee este valor como un
int. Luego, usa unlongpara almacenar su representación "sin signo" correcta (realiza un casting y una máscara con0xFFFFFFFFLsi es necesario). Imprime ambos valores. - Reflexión: ¿Por qué es importante
0xFFFFFFFFL(con laL) en la máscara?
- Imagina que recibes un valor de 32 bits de una fuente externa que conceptualmente es un "unsigned int" (p.ej.,
A.2. Operadores
Calculadora Básica y Asignaciones:
- Declara dos variables enteras y dos
double. - Realiza operaciones de suma, resta, multiplicación, división y módulo con ellas. Presta atención al resultado de la división entera vs. la división con
double. - Utiliza los operadores de asignación compuesta (+=, -=, *=, /=, %=) para modificar los valores de estas variables. Imprime los resultados en cada paso.
- Declara dos variables enteras y dos
Comparador de Referencias vs. Contenido:
- Crea dos objetos
Stringcon el mismo contenido literal (p.ej.,String s1 = "test"; String s2 = "test";). Compara usando==y.equals(). - Crea otros dos objetos
Stringusandonew String("test")(p.ej.,String s3 = new String("test"); String s4 = new String("test");). Compara usando==y.equals(). - Crea una tercera referencia que apunte a uno de los objetos anteriores (p.ej.,
String s5 = s3;). Comparas3 == s5. - Imprime todos los resultados y explica por qué son diferentes.
- Reflexión: ¿Cómo podría un malware usar incorrectamente
==conStringspara evadir una detección o tomar una decisión incorrecta?
- Crea dos objetos
Lógica de Cortocircuito:
- Escribe una expresión
ifque use&&donde la segunda condición podría causar unNullPointerExceptionsi se evaluara, pero no lo hace debido al cortocircuito. (P.ej.,String str = null; if (str != null && str.length() > 0) { ... }). - Haz lo mismo con
||donde la segunda condición no se evalúa porque la primera estrue.
- Escribe una expresión
Manipulación de Bits para Ofuscación Simple:
- Toma un
char(p.ej., 'A'). Conviértelo aint. - Realiza una operación XOR con una "clave"
int(p.ej.,0xAA). Imprime el resultado. - Realiza la operación XOR nuevamente con la misma clave sobre el resultado anterior. ¿Qué obtienes?
- Usa operadores de desplazamiento (
<<,>>,>>>) sobre un número entero (positivo y negativo) e imprime los resultados. Observa la diferencia entre>>y>>>con números negativos. - Reflexión: ¿Cómo podría usarse XOR para ocultar cadenas o datos en un payload?
- Toma un
Verificación de Tipo con
instanceof:- Crea un
Objectque sea en realidad unString(p.ej.,Object obj = "Soy una cadena";). - Usa
instanceofpara verificar siobjes una instancia deString, deInteger, y deObject. Imprime los resultados. - Si es un
String, haz un casting seguro aStringy llama a algún método deString(p.ej.,length()).
- Crea un
A.3. Declaraciones de Flujo de Control
Clasificador de Números:
- Pide al usuario (o define una variable) un número entero.
- Usa una estructura
if-else if-elsepara determinar si el número es positivo, negativo o cero. - Usa una instrucción
switchpara realizar una acción diferente basada en si un número (del 1 al 3) es 1 ("Opción Uno"), 2 ("Opción Dos"), o 3 ("Opción Tres"), con un casodefaultpara otros valores. Intenta usarStringen elswitch(si tu versión de Java lo permite y es relevante para el texto).
Iteraciones y Factorial:
- Calcula el factorial de un número usando un bucle
for. - Calcula el mismo factorial usando un bucle
while. - Calcula el mismo factorial usando un bucle
do-while. - Imprime los resultados.
- Calcula el factorial de un número usando un bucle
Procesamiento de Comandos (Simulado):
- Crea un array de
Stringcon algunos "comandos" (p.ej.,"START","PROCESS_DATA","PAUSE","STOP","UNKNOWN_CMD"). - Itera sobre el array usando un bucle
for-each. - Dentro del bucle, usa
ifoswitchpara simular la ejecución de cada comando. - Si encuentras el comando
"STOP", usabreakpara salir del bucle. - Si encuentras un comando
"SKIP_NEXT_IF_PAUSE"y el siguiente es"PAUSE", usacontinuepara saltar el procesamiento de"PAUSE"(esto es un poco más avanzado, podrías necesitar unforclásico para mirar adelante o una bandera). - Reflexión: ¿Cómo puede
breakycontinue(especialmente etiquetados) ser usados para crear flujos de control complejos o difíciles de seguir?
- Crea un array de
Búsqueda en Matriz con Salida Etiquetada:
- Crea una matriz (array bidimensional) de enteros.
- Busca un valor específico en la matriz usando bucles anidados.
- Si encuentras el valor, usa
break <etiqueta>;para salir de ambos bucles inmediatamente. Imprime la posición donde lo encontraste o un mensaje si no se encontró.
A.4. Arrays y Cadenas
Gestor de Tareas Simple:
- Declara un array de
Stringpara almacenar una lista de tareas. Inicialízalo con algunas tareas. - Imprime el número de tareas usando la propiedad
length. - Itera sobre el array e imprime cada tarea con su índice.
- Intenta acceder a un índice fuera de los límites y observa la
ArrayIndexOutOfBoundsException(puedes usar untry-catchsi ya lo conoces, o simplemente dejar que ocurra para ver el mensaje).
- Declara un array de
Manipulador de Nombres de Usuario:
- Crea una
Stringque contenga un nombre de usuario, posiblemente con espacios al inicio/final y en mayúsculas/minúsculas mezcladas (p.ej.," TeStUser123 "). - Usa métodos de
Stringpara:- Eliminar espacios al inicio y al final (
trim()). - Convertir todo a minúsculas (
toLowerCase()). - Verificar si contiene un número (
contains()omatches()con una expresión regular simple). - Obtener una subcadena (p.ej., los primeros 4 caracteres).
- Reemplazar "User" por "Admin" (si existe).
- Eliminar espacios al inicio y al final (
- Imprime el resultado de cada transformación.
- Reflexión: ¿Por qué la inmutabilidad de
Stringes importante aquí? ¿Qué pasa si concatenasStrings repetidamente en un bucle?
- Crea una
Constructor de Cadenas Eficiente:
- Usa un bucle para construir una cadena concatenando números del 0 al 9, primero usando el operador
+conString. - Luego, haz lo mismo usando
StringBuildery su métodoappend(). - Aunque no medirás el rendimiento directamente en un ejercicio básico, reflexiona sobre por qué
StringBuilderes más eficiente.
- Usa un bucle para construir una cadena concatenando números del 0 al 9, primero usando el operador
Codificador/Decodificador XOR Simple para Cadenas:
- Toma una
String(p.ej., "HolaSecreto"). - Conviértela a un
byte[](p.ej., usandogetBytes("UTF-8")). - "Cifra" cada byte del array realizando una operación XOR con un byte clave (p.ej.,
0x55). - Crea una nueva
Stringa partir delbyte[]cifrado (podrías necesitar codificarla en Base64 para que sea imprimible de forma segura, o manejar la posibilidad de caracteres no imprimibles). - "Descifra" el
byte[]realizando la operación XOR nuevamente con la misma clave. - Convierte el
byte[]descifrado de nuevo a unaStringe imprímela. - Reflexión: ¿Qué problemas pueden surgir al convertir
byte[]arbitrarios directamente aStringsin una codificación adecuada como Base64? ¿Por qué es crucial especificar unCharsetcomo "UTF-8"?
- Toma una
B.1. Clases, Objetos, Métodos y Constructores
Clase
MalwareComponent:- Crea una clase llamada
MalwareComponent. - Campos: Añade los siguientes campos (variables de instancia):
String componentName(nombre del componente, ej: "NetworkListener")int version(versión del componente)boolean isActive(indica si el componente está activo)
- Métodos:
- Un método
void displayStatus()que imprima el nombre, versión y si está activo el componente. Por ejemplo: "Componente: NetworkListener, Versión: 1, Estado: Activo". - Un método
void activate()que pongaisActiveatrue. - Un método
void deactivate()que pongaisActiveafalse.
- Un método
- Relevancia en Malware: Esta clase podría representar un módulo genérico dentro de una pieza de malware.
- Crea una clase llamada
Objetos y
this:- En tu método
main(o en una clase de prueba), crea dos objetos (instancias) de la claseMalwareComponent.componenteRed = new MalwareComponent();componentePersistencia = new MalwareComponent();
- Asigna valores a los campos de
componenteReddirectamente (si no los has hecho privados aún). - Modifica el método
displayStatus()enMalwareComponentpara usarthisexplícitamente al referirse a los campos (ej.this.componentName). - Llama al método
activate()paracomponenteRedydisplayStatus()para ambos objetos para ver sus estados.
- En tu método
Constructores para
MalwareComponent:- Constructor por defecto: Si no has añadido ningún constructor, observa cómo puedes crear un objeto. Luego, añade un constructor sin parámetros que inicialice
componentNamea "GenericComponent",versiona 1, yisActiveafalse. - Constructor parametrizado: Añade un constructor que acepte
String nameyint vercomo parámetros. Este constructor debe inicializarcomponentNameyversioncon los valores recibidos, eisActiveafalse. - Constructor sobrecargado con
this(): Añade un tercer constructor que solo acepteString name. Este constructor debe llamar al constructor parametrizado (el que tomanameyver) usandothis(name, 1);, asignando una versión por defecto de 1. - En tu
main, crea objetos deMalwareComponentusando los diferentes constructores que has definido y llama adisplayStatus()para cada uno. - Relevancia en Malware: Los constructores aseguran que cada "componente" del malware se inicialice con una configuración base necesaria (ej. un
C2Connectorque necesita URL y puerto).
- Constructor por defecto: Si no has añadido ningún constructor, observa cómo puedes crear un objeto. Luego, añade un constructor sin parámetros que inicialice
B.2.a. Encapsulación y Paquetes
Modificadores de Acceso y Getters/Setters para
MalwareComponent:- Modifica la clase
MalwareComponent(del ejercicio B.1.1):- Cambia los modificadores de acceso de todos sus campos (
componentName,version,isActive) aprivate. - Crea métodos
publicgetters para cada campo (ej.getComponentName(),getVersion(),isActive()oisIsActive()). - Crea métodos
publicsetters paracomponentNameyversion(ej.setComponentName(String name)). En elsetVersion(int ver), añade una pequeña validación: si la versión es menor que 1, se debe establecer a 1.
- Cambia los modificadores de acceso de todos sus campos (
- En tu
main, intenta acceder a los campos directamente (deberías obtener un error) y luego usa los getters y setters para interactuar con los objetosMalwareComponent.
- Modifica la clase
Paquetes y Visibilidad
default(package-private):- Crea una estructura de paquetes:
com.malware.corecom.malware.utils
- Mueve tu clase
MalwareComponental paquetecom.malware.core. Asegúrate de que la claseMalwareComponenty sus métodos públicos (getters/setters) sigan siendopublic. - Dentro de
MalwareComponent(encom.malware.core), añade un nuevo campo sin modificador de acceso (package-private):String internalId;y un método también package-private:void assignInternalId(String id) { this.internalId = id; }. - Prueba 1 (Mismo Paquete): Crea una nueva clase
CoreManageren el mismo paquetecom.malware.core. Dentro deCoreManager, crea un objetoMalwareComponente intenta acceder ainternalIdy llamar aassignInternalId(). Debería funcionar. - Prueba 2 (Diferente Paquete): Crea una clase
ExternalToolen el paquetecom.malware.utils. Dentro deExternalTool, importacom.malware.core.MalwareComponent. Intenta crear un objetoMalwareComponente intenta acceder ainternalIdoassignInternalId(). No debería funcionar. Explica por qué. - Relevancia en Malware: La encapsulación y los paquetes ayudan a estructurar el malware, ocultando detalles internos de ciertos módulos a otros, o a código externo (como herramientas de análisis si no usan reflexión).
- Crea una estructura de paquetes:
B.2.b. Herencia
- Clase Base
Payloady SubclaseCommandPayload:- Crea una clase
Payloaden el paquetecom.malware.core.- Campos:
protected String payloadType;yprotected int executionPriority;. - Constructor: Un constructor que acepte
payloadTypeyexecutionPrioritypara inicializar los campos. - Método: Un método
public void displayInfo()que imprima el tipo y la prioridad del payload. - Método (para sobrescribir): Un método
public void executeAction()que imprima "Ejecutando acción genérica del payload."
- Campos:
- Crea una clase
CommandPayloaden el mismo paquete que extienda dePayload.- Campo Adicional:
private String commandToExecute;. - Constructor: Un constructor que acepte
payloadType,executionPriority, ycommandToExecute. Debe llamar al constructor de la superclase (Payload) usandosuper()para inicializarpayloadTypeyexecutionPriority, y luego inicializarcommandToExecute. - Sobrescritura (
@Override): Sobrescribe el métodoexecuteAction(). La nueva implementación debe primero llamar asuper.executeAction()y luego imprimir "Ejecutando comando específico: " seguido del valor decommandToExecute. - Método Adicional: Un getter
public String getCommandToExecute().
- Campo Adicional:
- En tu
main, crea un objeto dePayloady un objeto deCommandPayload. Llama adisplayInfo()yexecuteAction()en ambos. - Relevancia en Malware: Permite definir un comportamiento base para diferentes tipos de "cargas útiles" o acciones, y luego especializarlas (ej.
StealDataPayload,RansomEncryptPayloadpodrían heredar dePayload).
- Crea una clase
B.2.c. Polimorfismo
Manejo Polimórfico de Payloads:
- Usando las clases
PayloadyCommandPayloaddel ejercicio anterior: - En tu
main, crea un array oArrayListde tipoPayload:Payload[] payloads = new Payload[2]; - Asigna al primer elemento un nuevo objeto
Payload(ej.,new Payload("INFO", 1)) y al segundo elemento un nuevo objetoCommandPayload(ej.,new CommandPayload("CMD_EXEC", 10, "cat /etc/passwd")). - Itera sobre el array usando un bucle
for-each. Dentro del bucle, para cadapayloadenpayloads:- Llama a
payload.displayInfo(). - Llama a
payload.executeAction(). Observa cómo se ejecuta el métodoexecuteActioncorrecto para cada objeto (el dePayloadpara el primer objeto, y el sobrescrito enCommandPayloadpara el segundo). Este es el polimorfismo en acción.
- Llama a
- Usando las clases
instanceofy Casting:- Dentro del mismo bucle del ejercicio anterior, después de llamar a
executeAction():- Usa
if (payload instanceof CommandPayload)para verificar si el objeto actual es una instancia deCommandPayload. - Si es verdadero, haz un downcast del objeto
payloadaCommandPayload:CommandPayload cmdPayload = (CommandPayload) payload; - Luego, llama al método específico de
CommandPayload:System.out.println("Comando a ejecutar (obtenido por casting): " + cmdPayload.getCommandToExecute());
- Usa
- Relevancia en Malware: El polimorfismo permite que el malware maneje diferentes tipos de tareas o módulos de forma genérica. Por ejemplo, un "despachador" de tareas podría tener una lista de objetos
Task(superclase) y llamar a un métodorun()en cada uno, donde cada subclase deTaskimplementarun()de manera diferente.instanceofpodría usarse para tareas que requieren un manejo especial.
- Dentro del mismo bucle del ejercicio anterior, después de llamar a
C.1. Framework de Colecciones de Java (JCF)
Listas:
ArrayListyLinkedList(Gestión de Objetivos y Comandos)ArrayListpara Objetivos:- Crea un
ArrayList<String>llamadotargetFilespara almacenar nombres de archivos que el malware podría buscar (ej., "passwords.txt", "credentials.db", "photos/DCIM_001.jpg"). - Añade varios nombres de archivo.
- Imprime el número de archivos objetivo.
- Obtén y muestra el primer y último archivo de la lista.
- Elimina un archivo específico por su nombre (primero encuéntralo si es necesario, o por índice si lo conoces).
- Verifica si un archivo "secret_key.dat" está en la lista.
- Crea un
LinkedListpara Cola de Comandos C2:- Crea una
LinkedList<String>llamadac2CommandQueuepara simular una cola de comandos recibidos de un servidor C&C. - Añade (usando
offeroaddLast) los siguientes comandos: "UPLOAD_CONTACTS", "GET_LOCATION", "RECORD_AUDIO_5MIN". - Procesa los comandos: en un bucle, mientras la cola no esté vacía, obtén y elimina el primer comando (usando
pollopollFirst) e imprime "Procesando comando: [comando]".
- Crea una
Sets:
HashSetyLinkedHashSet(IDs Únicos y Acciones Ordenadas)HashSetpara IDs Únicos:- Crea un
HashSet<String>llamadoinfectedDeviceRegistrypara almacenar IDs de dispositivos ya "infectados" o procesados. - Añade varios IDs, incluyendo algunos duplicados (ej., "device_alpha", "device_beta", "device_alpha").
- Imprime el conjunto de IDs (observa que los duplicados no se añaden).
- Verifica si "device_gamma" ya ha sido registrado usando
contains().
- Crea un
LinkedHashSetpara Pasos de un Ataque:- Crea un
LinkedHashSet<String>llamadoattackStepspara almacenar una secuencia de pasos de un ataque, donde el orden de inserción es importante y no debe haber pasos duplicados. - Añade: "1. Reconocimiento", "2. Acceso Inicial", "3. Escalada de Privilegios", "2. Acceso Inicial" (de nuevo), "4. Exfiltración".
- Itera sobre
attackStepse imprime cada paso, observando que se mantiene el orden de la primera inserción y no hay duplicados.
- Crea un
Maps:
HashMapyLinkedHashMap(Configuración y Datos Robados)HashMappara Configuración de Malware:- Crea un
HashMap<String, String>llamadomalwareConfig. - Inserta los siguientes pares clave-valor: ("c2_url", "http://evilhost.example/api"), ("retry_interval_seconds", "60"), ("debug_mode", "false").
- Obtén e imprime la URL del C2.
- Verifica si la configuración contiene una clave "proxy_enabled".
- Elimina la entrada "debug_mode".
- Itera sobre
entrySet()del mapa e imprime cada par clave-valor.
- Crea un
LinkedHashMappara Credenciales Ordenadas:- Crea un
LinkedHashMap<String, String>llamadostolenCredentialspara almacenar pares servicio/credencial (ej., "servicio_web" -> "usuario:contraseña"). - Añade varias credenciales.
- Itera sobre el mapa e imprime las credenciales. Observa que se mantiene el orden en que las añadiste (lo cual podría ser útil para un log de exfiltración ordenado).
- Crea un
Iteradores (Recorriendo y Modificando Colecciones)
- Toma el
ArrayList<String> targetFilesdel ejercicio 1. - Obtén un
Iterator<String>para esta lista. - Usa el iterador para recorrer la lista. Imprime cada nombre de archivo.
- Durante la iteración, si un nombre de archivo contiene la palabra "credentials", elimínalo de la lista usando
iterator.remove(). - Después del bucle, imprime la lista
targetFilespara ver los cambios. - Opcional: Si usaste
ArrayList, obtén unListIteratory prueba a recorrerla hacia atrás.
- Toma el
Clase
Collections(Ordenando y Sincronizando)- Crea un
ArrayList<String>llamadopayloadModulescon nombres de módulos de payload (ej., "DataStealer", "KeyLogger", "AudioRecorder", "NetworkSniffer"). - Usa
Collections.shuffle(payloadModules)para barajarlos aleatoriamente e imprime la lista. - Usa
Collections.sort(payloadModules)para ordenarlos alfabéticamente e imprime la lista. - Imprime el número de veces que "KeyLogger" aparece en la lista usando
Collections.frequency(). - Crea una versión "inmodificable" de esta lista usando
Collections.unmodifiableList()y intenta añadir un nuevo elemento (debería lanzarUnsupportedOperationException, puedes envolverlo en untry-catchpara demostrarlo). - Conceptual: Describe brevemente por qué un malware podría necesitar
Collections.synchronizedList()si tuviera múltiples hilos accediendo a una lista de objetivos.
- Crea un
C.2. Manejo de Excepciones
try-catch-finally(Manejo de Archivos y Recursos)- Escribe un método
void attemptToReadConfig(String filePath)que intente crear unFileInputStreampara elfilePathdado. - Dentro de un bloque
try, intenta leer el primer byte (simplementefis.read();). - Añade un bloque
catchparaFileNotFoundExceptionque imprima "Error: Archivo de configuración '[filePath]' no encontrado." - Añade otro bloque
catchparaIOExceptiongenérico que imprima "Error de E/S al leer '[filePath]'." - Añade un bloque
finallyque siempre imprima "Intento de lectura de configuración finalizado para '[filePath]'." y que intente cerrar elFileInputStream(si no esnull), manejando también una posibleIOExceptional cerrar dentro delfinally. - Prueba tu método con un nombre de archivo que exista (puedes crear un archivo vacío para probar) y con uno que no exista.
- Escribe un método
try-with-resources(Simplificando el Manejo de Recursos)- Reescribe el método
attemptToReadConfigdel ejercicio anterior usando la sentenciatry-with-resourcespara elFileInputStream. - Observa cómo ya no necesitas el bloque
finallyexplícito para cerrar el stream (aunque aún puedes tener unfinallypara otras acciones si es necesario). Los bloquescatchseguirían siendo iguales.
- Reescribe el método
throwythrows(Lanzando y Declarando Excepciones)- Crea una clase
PayloadDeployer. - Dentro, escribe un método
void deployPayload(String payloadName, String targetSystemInfo) throws DeploymentException. - Dentro de este método:
- Si
payloadNameesnullo está vacío, lanzathrow new IllegalArgumentException("El nombre del payload no puede ser nulo o vacío.");. - Si
targetSystemInfocontiene la subcadena "honeypot" (simulando detección de un entorno de análisis), lanzathrow new DeploymentException("Despliegue abortado: Posible entorno de análisis detectado.");. - Si no, imprime "Desplegando [payloadName] en [targetSystemInfo]".
- Si
- Define la excepción personalizada
class DeploymentException extends Exception { public DeploymentException(String message) { super(message); } }. - En tu
main, llama adeployPayloadvarias veces con diferentes entradas, manejandoIllegalArgumentExceptionyDeploymentExceptionen un bloquetry-catch.
- Crea una clase
Excepciones Personalizadas y Robustez del Malware
- Define una excepción no verificada
class C2ConnectionTimeoutException extends RuntimeException { public C2ConnectionTimeoutException(String message, Throwable cause) { super(message, cause); } }. - Crea un método
String fetchDataFromC2(String command)que simule una comunicación con el C2.- Dentro de un
tryblock, simula una operación de red que podría tardar mucho o fallar (puedes usarThread.sleep()y luego simular un error). - Si ocurre un error "de red" (simulado), captura la excepción original (ej. una
IOExceptionsimulada) y lanzathrow new C2ConnectionTimeoutException("Timeout al intentar ejecutar comando: " + command, originalException);. - Si tiene éxito, devuelve "Datos para: " + command.
- Dentro de un
- En el código que llama a
WorkspaceDataFromC2, usa untry-catchparaC2ConnectionTimeoutException. En elcatch, el "malware" no debe crashear, sino registrar internamente el error (imprimir un mensaje como "LOG INTERNO: Fallo C2: [mensaje de excepción], causa: [causa]") y quizás intentar una acción alternativa o continuar. - Discusión: ¿Por qué un malware preferiría usar excepciones no verificadas para ciertos fallos internos y cómo el manejo de excepciones contribuye a su sigilo operacional?
- Define una excepción no verificada
Bloque
catch (Throwable t)y Evasión (Conceptual)- Escribe un pequeño fragmento de código que simule el bucle principal de una tarea de malware (ej. un
while(true)que intenta realizar una "acción maliciosa"). - Envuelve la "acción maliciosa" (que podría ser una llamada a un método que a veces falla con diferentes excepciones) dentro de un
try-catch (Throwable t). - Dentro del bloque
catch (Throwable t):- No imprimas
t.printStackTrace(). - Opcionalmente, haz un log muy discreto o simplemente pon un comentario
// Error ignorado para continuar operación.
- No imprimas
- Pregunta para reflexionar: ¿Cuáles son las implicaciones (tanto para la funcionalidad del malware como para un analista) de usar un bloque
catch (Throwable t)tan genérico y silencioso? ¿Cómo podría esto ayudar al malware a evadir una detección simple o un análisis superficial?
- Escribe un pequeño fragmento de código que simule el bucle principal de una tarea de malware (ej. un
D.1. Reflexión de Java (Java Reflection)
Obteniendo Objetos
Class:- Escribe un fragmento de código en Java que obtenga el objeto
Classpara la clasejava.util.ArrayListde las siguientes tres maneras:- Usando el literal de clase:
ArrayList.class. - Creando una instancia de
ArrayListy usandoobj.getClass(). - Usando
Class.forName("java.util.ArrayList").
- Usando el literal de clase:
- Para la tercera opción, asegúrate de manejar la
ClassNotFoundExceptioncon un bloquetry-catch. Imprime el nombre de la clase obtenido de cada objetoClasspara verificar.
- Escribe un fragmento de código en Java que obtenga el objeto
Inspeccionando una Clase "Secreta":
- Crea una clase simple llamada
AgentProfilecon los siguientes miembros:package com.example.secrets; public class AgentProfile { private String realName = "John Doe"; private int agentId = 7; public String coverName = "Operator X"; private AgentProfile(String realName, int agentId) { this.realName = realName; this.agentId = agentId; } public AgentProfile(String coverName) { this.coverName = coverName; } private void revealSecretMission() { System.out.println("Mission: Infiltrate target system. Real name: " + realName); } public void reportStatus() { System.out.println("Agent " + coverName + " (ID: " + agentId + ") reporting: All clear."); } } - En otra clase (tu clase de prueba), obtén el objeto
ClassparaAgentProfile. - Usa reflexión para:
- Listar e imprimir los nombres de todos los campos declarados (
getDeclaredFields()) enAgentProfile, incluyendo los privados. - Listar e imprimir los nombres de todos los métodos declarados (
getDeclaredMethods()) enAgentProfile, incluyendo los privados y constructores. - Listar e imprimir solo los constructores públicos (
getConstructors()).
- Listar e imprimir los nombres de todos los campos declarados (
- Crea una clase simple llamada
Accediendo y Modificando Miembros Privados con Reflexión:
- Continuando con la clase
AgentProfiledel ejercicio anterior: - Instanciación con Constructor Privado:
- Obtén el constructor privado de
AgentProfileque tomaStringyintcomo parámetros (getDeclaredConstructor(String.class, int.class)). - Usa
constructor.setAccessible(true)para hacerlo accesible. - Crea una instancia de
AgentProfileusandoconstructor.newInstance("Jane Smith", 8).
- Obtén el constructor privado de
- Acceso a Campos Privados:
- Obtén el campo privado
realNamede la instancia que acabas de crear. - Usa
field.setAccessible(true). - Lee e imprime su valor usando
field.get(instance). - Cambia el valor del campo
realNamea "Alex Rider" usandofield.set(instance, "Alex Rider").
- Obtén el campo privado
- Invocación de Métodos Privados:
- Obtén el método privado
revealSecretMission(). - Usa
method.setAccessible(true). - Invoca el método en la instancia usando
method.invoke(instance). Observa la salida.
- Obtén el método privado
- Maneja las excepciones que puedan surgir (
NoSuchFieldException,NoSuchMethodException,IllegalAccessException,InvocationTargetException,InstantiationException).
- Continuando con la clase
Ofuscación de Llamadas a APIs (Simulación):
- Imagina que quieres invocar el método
System.getProperty("java.version")pero usando reflexión para "ocultar" las cadenas "java.lang.System" y "getProperty". - Construye estas cadenas (puedes hacerlo directamente por ahora, pero piensa cómo un malware podría obtenerlas de forma ofuscada, ej. decodificando desde Base64, concatenando partes, etc.).
- Usa
Class.forName()para obtener la claseSystem. - Usa
clazz.getMethod()para obtener el métodogetPropertyque toma unStringcomo parámetro. - Invoca el método (
method.invoke(null, "java.version")-nullporque es un método estático) e imprime la versión de Java. - Pregunta de Reflexión: ¿Por qué esta técnica dificulta el análisis estático del código malicioso? ¿Qué buscaría un analista en el código si sospecha de este tipo de ofuscación?
- Imagina que quieres invocar el método
D.2. Serialización de Java
Creando una Clase
Serializablepara Datos de Malware:- Define una clase
C2Packetque implementejava.io.Serializable. - Añade los siguientes campos:
private static final long serialVersionUID = 12345L;String deviceId;String commandType; // ej. "CONFIG_UPDATE", "DATA_EXFIL"java.util.Map<String, String> dataPayload; // Para enviar datos variadostransient String networkSessionToken; // No debe ser serializado
- Añade un constructor para inicializar estos campos y un método
toString()para mostrar su contenido.
- Define una clase
Serializando y Deserializando un
C2Packet:- En tu método
main:- Crea una instancia de
C2Packet. Ponle algunos datos de ejemplo, incluyendo unnetworkSessionTokeny unHashMapparadataPayload. - Imprime el objeto original.
- Serializa el objeto a un archivo llamado "c2_packet.dat" usando
ObjectOutputStreamyFileOutputStream. - Deserializa el objeto desde "c2_packet.dat" a una nueva instancia de
C2PacketusandoObjectInputStreamyFileInputStream. - Imprime el objeto deserializado. Observa y explica qué valor tiene el campo
networkSessionTokendespués de la deserialización y por qué.
- Crea una instancia de
- Asegúrate de manejar
IOExceptionyClassNotFoundException.
- En tu método
Control Personalizado (Simulación
writeObject/readObject):- Modifica tu clase
C2Packet. - Añade un método
private void writeObject(java.io.ObjectOutputStream out) throws IOException. Dentro de este método:- Imprime "LOG: Cifrando dataPayload antes de serializar..."
- Simula el cifrado: Modifica el
dataPayloadde alguna manera simple (ej., añade un prefijo "encrypted_" a cada valor en el mapa) ANTES de llamar aout.defaultWriteObject();(que serializa los campos no transitorios y no estáticos normalmente). - Después de
defaultWriteObject(), puedes revertir el cambio si es solo una simulación para el objeto en memoria.
- Añade un método
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException. Dentro de este método:- Llama a
in.defaultReadObject();PRIMERO. - Imprime "LOG: Descifrando dataPayload después de deserializar..."
- Simula el descifrado: Revierte la modificación que hiciste en
writeObject(ej., quita el prefijo "encrypted_").
- Llama a
- Vuelve a ejecutar el ejercicio 2 para ver los mensajes de log y el efecto de tus métodos personalizados. (Nota: para un cifrado real, los datos se cifrarían y escribirían byte a byte, o se cifraría el
Mapy se escribiría conout.writeObject()).
- Modifica tu clase
Discusión sobre Deserialización Insegura:
- Basándote en el texto, explica con tus propias palabras:
- ¿Qué es una vulnerabilidad de "deserialización insegura"?
- ¿Cómo podría un malware explotar esta vulnerabilidad en otra aplicación instalada en el dispositivo Android?
- ¿Qué son los "gadgets" en el contexto de estos ataques?
- ¿Por qué la deserialización de
java.io.ObjectInputStreames considerada inherentemente peligrosa si los datos provienen de una fuente no confiable?
- Basándote en el texto, explica con tus propias palabras:
D.3. Interfaz Nativa de Java (JNI)
Dado que configurar un entorno NDK completo está fuera del alcance de estos ejercicios de Java puro, nos centraremos en la comprensión de los conceptos del lado de Java y la interacción.
Declaración de Métodos Nativos en Java:
- Crea una clase Java llamada
MalwareNativeInterfaceen un paquete de tu elección (ej.com.evilcorp.bridge). - Dentro de esta clase, declara los siguientes métodos nativos:
public native boolean checkRootStatus();private native String getHiddenData(String key);(nota que es privado)public static native int executeShellCommand(String command);
- Añade un bloque estático para cargar una biblioteca nativa llamada "droid_ops_lib".
static { try { System.loadLibrary("droid_ops_lib"); // Nombre de la lib sin "lib" ni ".so" } catch (UnsatisfiedLinkError e) { System.err.println("FALLO CRÍTICO: No se pudo cargar la biblioteca nativa droid_ops_lib. " + e.getMessage()); // El malware podría tener una lógica de fallback o terminar sigilosamente aquí. } } - En tu
main, crea una instancia deMalwareNativeInterfacee intenta llamar acheckRootStatus()(solo para ver si compila y se ejecuta la parte Java; lanzaráUnsatisfiedLinkErrorsi la biblioteca no existe, lo cual es esperado sin el NDK).
- Crea una clase Java llamada
Convención de Nombres de Funciones JNI:
- Para los tres métodos nativos que declaraste en
MalwareNativeInterfaceen el paquetecom.evilcorp.bridgeen el ejercicio anterior:checkRootStatus()getHiddenData(String key)executeShellCommand(String command)
- Escribe los nombres exactos de las funciones C/C++ que la ART (Android Runtime) esperaría encontrar en la biblioteca nativa "droid_ops_lib.so".
- Para los tres métodos nativos que declaraste en
Interacción
JNIEnv*y Tipos de Datos (Conceptual):- Imagina que estás implementando la función nativa C/C++ para
private native String getHiddenData(String key);. - El parámetro
keyllega a tu función C++ como unjstring. - Tu lógica nativa produce un resultado que es un
const char* secret_value. - Describe conceptualmente (no necesitas código C++ exacto) qué funciones del puntero
JNIEnv*necesitarías usar para:- Convertir el
jstring keyen unconst char*utilizable en C++. - Liberar cualquier recurso asociado a esa conversión una vez que ya no necesites el
const char* key_c. - Convertir tu
const char* secret_valueresultante de C++ de nuevo a unjstringpara devolverlo a Java.
- Convertir el
- Imagina que estás implementando la función nativa C/C++ para