La ejecución simbólica, al final, no es más que ir actualizando un par de estructuras de datos (path_constraint, y variable_map). Eso sí, metiéndole muchísimas horas luego para traducir cada lenguaje o arquitectura a transformaciones sobre esas estructuras. Me gusta explicarlo con un ejemplo sencillo donde se construye en muy pocas líneas de código un motor de ejecución simbólica desde cero, limitadísimo a unas pocas instrucciones en ensamblador, pero conceptualmente tiene todo lo que tiene que tener. Esta parte la voy a adaptar de un proyecto que hago en mi training (https://furalabs.com/trainings), si queréis que os haga spoiler tan solo decídmelo y os mando un documento con esto.
Cuanto a aplicaciones en desofuscación, el estado del arte utiliza ejecución simbólica para extraer fórmulas a partir del código, y luego mirar de simplificar esas fórmulas con la ayuda de un SMT solver, con técnicas de optimización de compiladores (https://cs.gmu.edu/~zeng/papers/deobfuscation-icics2017.pdf), con program synthesis (https://www.usenix.org/system/files/conference/usenixsecurity17/sec17-blazytko.pdf, https://profs.scienze.univr.it/~ceccato/papers/2020/bar2020.pdf, https://github.com/arnaugamez/tfg) o un poco de magia con mates (https://github.com/arnaugamez/tfm). A partir de aquí, también se pueden atacar máquinas virtuales de ofuscación con la ayuda de ejecución simbólica (https://cs.gmu.edu/~zeng/papers/deobfuscation-icics2017.pdf), por ejemplo desofuscando cada handler de la máquina virtual y reconstruyendo el flujo instrucción a instrucción de forma semiautomática con el motor de ejecución simbólica. Tengo cosas sobre esto hechas para mi training también que adaptaré al formato de la charla.
Cuanto a aplicaciones en fuzzing, hay cosillas ad-hoc, pero lo más interesante viene a partir de estos dos papers (SymCC para código fuente: https://github.com/eurecom-s3/symcc, SymQEMU para binarios: https://github.com/eurecom-s3/symqemu). Solo indagar en ellos daría para una o dos charlas, pero la idea es sencilla, y se puede transmitir fácilmente: ten el programa que estás fuzzeando corriendo con un motor de ejecución simbólica en paralelo que te pueda sacar restricciones para branching al que no estés llegando con las mutaciones propias del fuzzer, de manera que este pueda (con la ayuda de un SMT solver) producir inputs válidos que permitan llegar a explorar nuevos paths dentro de la sesión de fuzzing.»