Opaque Predicates

Los opaque predicates insertan en tu código condiciones cuyo resultado es fijo y conocido por el ofuscador, pero imposible de determinar para un atacante mediante análisis estático.

  • AndroidAndroid
  • AppleiOS
TL;DR

Un opaque predicate es una condición cuyo resultado es siempre el mismo, el ofuscador lo sabe, pero un analizador no puede demostrarlo. Los opaque predicates se usan para añadir ramas que parecen reales, anclar código muerto y hacer que el control flow sea imposible de seguir con seguridad.

Definición

¿Qué es un opaque predicate?

Un opaque predicate es una expresión condicional cuyo resultado está predeterminado. Siempre evalúa a true, o siempre a false, pero el valor no se puede deducir mediante análisis estático sin llegar a ejecutar el código.

Por sí solos, los opaque predicates no hacen nada visible. Su poder está en lo que habilitan. Son la pieza de construcción que hace que el código muerto parezca alcanzable y que el control flow ofuscado resista el análisis.

Cuando un atacante analiza un método, necesita saber qué ramas pueden ejecutarse. Los opaque predicates dejan esa pregunta sin respuesta solo con el código, forzando un análisis lento e incierto.

Mecanismo

Cómo funcionan los opaque predicates

Los opaque predicates se construyen a partir de propiedades matemáticamente garantizadas pero difíciles de evaluar de forma estática:

  • Condiciones siempre verdaderas y siempre falsas. Expresiones construidas de modo que su resultado es fijo pero no evidente.

  • Anclaje de código muerto. Una rama protegida por un opaque predicate puede contener código señuelo que parece alcanzable.

  • Soporte al control flow obfuscation. El control flow flattened usa opaque predicates para que las transiciones de estado no se puedan predecir.

El analizador ve una rama real y no puede descartarla, así que debe considerar todos los caminos.

Ejemplo

Ejemplo de opaque predicates

El mismo método, antes y después de insertar opaque predicates. Ambos se comportan igual. Solo uno de ellos le parece predecible a un decompilador.

Original

Un decompilador puede determinar qué ramas son alcanzables y podar el resto, simplificando su vista del método.

Con opaque predicates

Todas las ramas parecen posibles. El decompilador no puede podar nada y el método mantiene la complejidad que le dio el ofuscador.

Original
fun process(input: String) {
    if (input.isNotEmpty()) {
        send(input)
    }
}
Con opaque predicates
fun process(input: String) {
    val x = System.currentTimeMillis() and 0xFL
    if ((x * x + x) % 2L == 0L && input.isNotEmpty()) {
        send(input)
    } else if ((x * 7L) % 3L > 4L) {
        decoy()
    }
}

Casos de uso

Cuándo usar opaque predicates

Los opaque predicates son especialmente útiles cuando tu aplicación:

  • Usa control flow obfuscation y necesita que resista el análisis.
  • Usa dead code injection y necesita que los señuelos parezcan alcanzables.
  • Debe resistir decompiladores automáticos que podan el código inalcanzable.

Los opaque predicates rara vez se usan solos. Son el mecanismo que hace efectivos el control flow obfuscation y la dead code injection.

Disponibilidad por plataforma

Opaque predicates según la plataforma

  • Android

    Opaque predicates inyectados en el control flow del bytecode.

  • iOS

    Opaque predicates insertados durante la compilación del binario.

Preguntas frecuentes

¿Qué es un opaque predicate?
Un opaque predicate es una condición cuyo resultado es siempre el mismo y conocido por el ofuscador, pero imposible de determinar para un analizador estático. Se usa para añadir ramas que parecen reales y anclar código muerto.
¿Los opaque predicates afectan al rendimiento?
El impacto es mínimo. Cada predicate es una expresión pequeña que se evalúa una vez allí donde aparece.
¿Para qué se usan los opaque predicates?
Hacen que el control flow ofuscado resista el análisis y que el código muerto inyectado parezca alcanzable. Son una pieza de construcción más que una protección en sí misma.
¿Se pueden detectar los opaque predicates?
Un análisis avanzado puede identificar algunos patrones con el tiempo, por eso una implementación fuerte varía las construcciones empleadas. El objetivo es hacer que la poda no sea fiable.
¿Qué plataformas soportan opaque predicates?
ByteHide Shield usa opaque predicates en Android e iOS, y como parte del control flow obfuscation en .NET y JavaScript.
+10.000 desarrolladores y empresas protegen sus aplicaciones con ByteHide

Protege tu aplicación con
ByteHide Shield

Los opaque predicates son una de las más de 20 protecciones de ByteHide Shield. Aplícalos a tu aplicación Android o iOS.

ByteHide runtime dashboard showing live threat monitoring and protection metrics