Runbook Crytox
Choix de la cible#
Groupe actif depuis 2020, toujours actif en 2025 (rapport Talos IR Q1 2025, rapport Halcyon mi/fin 2025 sur un nouvel encrypteur maison qui n'utilise plus HRSword pour désactiver l'EDR). Deux rapports publics documentent l'implémentation crypto :
- Zscaler ThreatLabz, 2022-09-21, "Technical Analysis of Crytox Ransomware"
(Romain Dumont) : affirme que la clé AES par fichier provient d'un
générateur pseudo-aléatoire maison, seedé par
GetTickCount()(32 bits) combine à une constanteconfig_seedfigée dans la configuration du malware. Citation directe du rapport : "Using a 32-bit integer as the seed is not sufficient with today's computational power." Neuf hashs fournis, un seul présent sur MalwareBazaar (voir Acquisition). - Halcyon, 2025, "Crytox Consistently Evades Endpoint Security via
PowerShell" : confirme l'activité récente (mi-2025, dizaines de
victimes), décrit un nouvel encrypteur maison (RSA-2048 + AES-256 par
fichier, extension
.wait, note de rançonHelp.hta, contact via uTox). Un hash cité (d3d0bbaf2108ef7cee7ae795e31450135acff39fd95b3ec3af5da9a198d90f97) mais absent de MalwareBazaar (échantillon d'engagement IR, non soumis publiquement) : impossible à récupérer par le canal habituel. Le rapport ne confirme ni n'infirme si le générateur seedé parGetTickCountde la version 2022 est toujours en place dans ce nouvel encrypteur.
Question ouverte à trancher (même forme que Gunra/Rhysida déjà traités dans ce dépôt) : le défaut de seed 32 bits documenté par Zscaler en 2022 est-il toujours présent dans les échantillons qu'on peut effectivement obtenir, et jusqu'à quelle date ? On ne peut pas répondre pour la version 2025 précise du rapport Halcyon faute d'échantillon public, mais on peut confirmer/détailler le mécanisme exact sur les échantillons 2020-2021 disponibles, ce qui n'a pas été fait au niveau code (Zscaler ne montre pas le détail du générateur, juste sa conclusion).
Acquisition#
Recherche par tag MalwareBazaar/ThreatFox infructueuse (Crytox n'existe
pas comme tag exploitable, illegal_tag sur les variantes avec
underscore). Les 9 hashs cités par le rapport Zscaler et le hash cité par
Halcyon vérifiés un par un via get_info : un seul présent sur
MalwareBazaar.
# Hash directement cite par Zscaler/ThreatLabz comme echantillon analyse
curl -s -X POST https://mb-api.abuse.ch/api/v1/ \
-H "Auth-Key: $(cat ../.mb_api_key)" \
-d "query=get_file&sha256_hash=32eef267a1192a9a739ccaaae0266bc66707bb64768a764541ecb039a50cba67" \
-o binaries/32eef267.zip
7z x -pinfected -y -obinaries binaries/32eef267.zip
# Pivot par imphash (365b1d12b684a96b167a74679ec9e4e3, meme builder) :
# get_imphash renvoie exactement 2 resultats, tous les deux 2020-2021
curl -s -X POST https://mb-api.abuse.ch/api/v1/ \
-H "Auth-Key: $(cat ../.mb_api_key)" \
-d "query=get_file&sha256_hash=d60dc6965f6d68a3e7c82d42e90bfda7ad3c5874d2c59a66df6212aef027b455" \
-o binaries/d60dc696.zip
7z x -pinfected -y -obinaries binaries/d60dc696.zip
Deux échantillons obtenus, même taille (1 287 168 octets), même imphash :
32eef267a1192a9a739ccaaae0266bc66707bb64768a764541ecb039a50cba67.exe(first_seen MalwareBazaar 2021-04-08, échantillon directement analysé par Zscaler)d60dc6965f6d68a3e7c82d42e90bfda7ad3c5874d2c59a66df6212aef027b455.exe(nom originalworker 5 -t.exe, first_seen 2020-04-18, même famille)
Depackaging#
file binaries/*.exe
# PE32+ ... 3 sections -> sections nommees UPX0/UPX1/.rsrc : UPX confirme
# (coherent avec la note Zscaler "packed with UPX")
upx -d -o binaries_unpacked/32eef267....unpacked.exe binaries/32eef267....exe
upx -d -o binaries_unpacked/d60dc696....unpacked.exe binaries/d60dc696....exe
# 1 287 168 -> 3 070 464 octets (ratio 41.92%), UPX standard, depackaging propre
Point d'attention : le point d'entrée de l'exécutable depackage
(0x2e8000, très proche de la fin de l'image) et une table d'imports
statique réduite à 10 entrées (GlobalAlloc, GlobalFree, CreateFileA,
WriteFile, CloseHandle, ExitProcess, GetModuleFileNameA,
GetLastError, RegCloseKey, MessageBoxA) suggèrent que le payload
résout lui-même le reste de ses API (dont très probablement
GetTickCount et les fonctions Crypt*) dynamiquement à l'exécution
(GetProcAddress sur des noms construits/lus en mémoire), plutôt que par
table d'imports statique -- motif d'évasion courant, cohérent avec
Halcyon qui décrit explicitement des versions récentes conçues pour
échapper aux outils de sécurité. Confirme par strings :
GetTickCount apparaît comme chaîne littérale dans le binaire
depackage, mais pas dans la table d'imports résolue par pefile :
strings -n6 binaries_unpacked/32eef267*.exe | grep -iE "GetTickCount|Crypt"
# -> GetTickCount (chaine seule, pas d'autre match Crypt*)
Chaîne complète de désassemblage : dropper -> PE embarqué -> shellcode injecteur -> payload final#
Plutôt que de suivre la piste 1 telle quelle (xrefs Ghidra sur la chaîne
"GetTickCount"), la chaîne complète a été demontée couche par couche,
combinant lecture statique et émulation Unicorn ciblée (tools/
emulate_shellcode.py, tools/emulate_stage3_keygen.py,
tools/crack_api_hash.py). Quatre niveaux d'indirection en tout :
- Dropper depackage UPX (
entry()à0x1402e8000) : écrit un PE embarque sur disque (WriteFile), gère une clé de registre (RegOpenKeyExA/RegQueryValueExA, tailles attendues0x114/0x500), puis énumère les processus en cours (Process32First/Next) pour injecter un shellcode (FUN_1402e8547) dans un processus cible viaVirtualAllocEx/WriteProcessMemory/CreateRemoteThread. - PE embarqué (extrait à l'offset RVA
0x1000du dropper,3 043 328octets, PE32 i386, sha2560a563967a7d48e702adaa6f21f22a80b7db6aae7b7149ede51b0fde583e37283) : confirme être le client uTox complet (chaînes "uTox", "utox.io", version "0.14.0"), cohérent avec le protocole de contact par messagerie uTox documenté publiquement (Halcyon, K7). Utilise ici uniquement comme "processus légitime" pour l'injection, pas comme charge utile active. - Shellcode injecte (
0x1142= 4418 octets, extrait de0x1402e889edans le dropper) : résout ses appels d'API par hash (ROR327 bits sur le nom de module UTF-16 majuscule+NUL,ROR3211 bits sur le nom de fonction ASCII+NUL, somme, cftools/ crack_api_hash.py-- algorithme rétro-concu empiriquement, confirmé sur plus de 70 hash cassés avec succès au total sur ce dépôt). Rouvre le FICHIER DROPPER ORIGINAL sur disque (chemin reçu en paramètre), mappe le fichier (CreateFileA/CreateFileMappingA/MapViewOfFile), extrait un blob de0x4400(17408) octets à l'offset fixe0x135ca4, zeroise puis supprime le fichier original (autodestruction anti-forensique confirmée dynamiquement :rep stossur toute la vue mappée avantDeleteFileA). Déchiffré un petit blob de configuration embarquée (0xc0octets, AES-NI, clé dérivée des tables S-box) révélant des noms de fichiers (pghdn.txt,rwjfk.bat), écrit et exécuté réellement viaShellExecuteAun script de sabotage anti-récupération :for /F "tokens=*" %1 in ('wevtutil.exe el') DO wevtutil.exe cl "%1" vssadmin.exe Delete Shadows /All /Quiet diskshadow.exe /s ../pghdn.txt(purge des journaux d'événements Windows + suppression de tous les clichés instantanés, capture intégrale dynamique, cftools/emulate_shellcode.py). Énumère ensuite les processus à la recherche deEXPLORER.EXE(hash0xdcf164cd) ou, à défaut,SVCHOST.EXE(0x561f1820), pour y injecter (mêmes primitivesVirtualAllocEx/WriteProcessMemory/CreateRemoteThread) le blob de0x4400octets déchiffré (même routine AES-NI, clé dérivée des tables S-box à[r12+0x428]) : sortie20 480octets, capturée intégralement via le hookWriteProcessMemory.
Deux bugs réels du HARNAIS D'ÉMULATION (pas du malware) trouvés et
corrigés pendant cette étape, tous deux des inversions de paramètres
dans les stubs d'API simulées en Python (à chaque fois confirmés par
comparaison avec la convention d'appel x64 réelle observée au
désassemblage) :
- memcpy(dest,src,n) simulé avec n/src/dest sur les mauvais
registres (RCX pris pour la longueur au lieu de la destination) :
la copie du blob de 0x4400 octets vers le paramètre du thread
suivant échouait silencieusement (vérification de bornes toujours
fausse), laissant le blob a zero.
- CryptAcquireContextA simulé avec le pointeur de sortie sur R9
(en réalité dwProvType, un entier) au lieu de RCX (le vrai
phProv) : ecriture mémoire a l'adresse littérale 0x1, plantage
immédiat au tout premier appel API du payload final.
- Payload final (les
20 480octets déchiffrés et injectés, position-indépendant, même mécanisme de résolution d'API par hash) : surface d'API cassée viacrack_api_hash.py(44 constantes de hash extraites du binaire, la grande majorité résolues) confirmant sans ambiguïté qu'il s'agit du vrai module de chiffrement de fichiers :CryptAcquireContextA/CryptGenKey/CryptExportKey/CryptImportKey/CryptEncrypt/CryptDestroyKey(CryptoAPI, génération et export de la paire de clés RSA de session -- cohérent avec la description Halcyon "génère une paire de clés RSA-2048 de session... chiffre la clé privée de session cinq fois avec la clé publique de l'attaquant"),GetLogicalDrives+boucle sur les 26 lettres (spawn d'un threadCreateThreadpar lecteur local détecté),WNetOpenEnumW/WNetEnumResourceW/WNetCloseEnum(énumération des lecteurs réseau, confirme la portée "lecteurs locaux ET réseau" documentée publiquement),FindFirstFileW/FindNextFileW/FindClose(parcours récursif),CreateFileW/ReadFile/WriteFile/SetEndOfFile/SetFilePointerEx/SetFileAttributesW/MoveFileW(E/S de chiffrement par fichier + renommage),AdjustTokenPrivileges/LookupPrivilegeValueA/OpenProcessToken(SeDebugPrivilege ou similaire),RegOpenKeyExA/RegQueryValueExA/RegSetValueExA/RegCloseKey(persistance d'un identifiant victime).
Le générateur pseudo-aléatoire : reconstruction complète et exacte#
Localise par lecture directe dans le payload final (offset 0x1396 du
blob de 20 480 octets, immédiatement precede d'un appel GetTickCount
dont le hash 0xe2b00ace est résolu). Code source
reconstitué (variables nommées d'après leur rôle, traduction directe du
désassemblage x86-64, aucune conjecture) :
// r12 : structure de configuration partagee (dechiffree au demarrage du
// payload par AES-NI depuis un blob de 0x2f30 octets embarque).
// [r12+0x10] : etat 64 bits PERSISTANT du generateur, partage entre TOUS
// les appels de cette fonction sur tout le run (jamais remis
// a zero -- confirme par le site d'appel unique de cette
// fonction dans tout le payload, place juste apres
// SetFilePointerEx/ReadFile sur le fichier courant : appelee
// UNE FOIS PAR FICHIER, coherent avec Zscaler "each file is
// encrypted with a unique AES-256 key generated at runtime").
// [r12+0x58] : diviseur/modulus (valeur issue de la configuration
// dechiffree, pas une constante figee dans le CODE lui-meme
// -- seuls le multiplicateur et l'incrementateur ci-dessous
// sont des constantes de code en dur).
//
// rax porte le "fil" de calcul d'une iteration a l'autre : au tout debut
// du tout premier appel de cette fonction dans tout le run, rax vaut
// GetTickCount() (appele a l'entree de CHAQUE appel, mais seule sa toute
// premiere valeur compte vraiment : ensuite rax est entierement regi par
// la recurrence ci-dessous). A l'interieur d'un tour, rax porte le
// QUOTIENT de la division precedente vers l'iteration suivante ; entre
// deux tours, rax porte le MOT DE SORTIE du tour precedent (pas remis a
// GetTickCount()).
void generer_cle_32_octets(uint64_t *sortie /* rdi, 4 x 8 octets */) {
uint64_t rax = GetTickCount(); // SEULE injection d'entropie fraiche
for (int tour = 0; tour < 4; tour++) { // 4 tours -> 32 octets
uint64_t acc = 0;
for (int i = 0; i < 64; i++) { // 64 iterations par tour
rax = rax + r12->accumulateur; // 0x13b7
rax = rax * 0x7D6C3ULL + 0x159EC7ULL; // 0x13bc-0x13c3, LCG
r12->accumulateur = rax; // 0x13c9, PERSISTE
uint64_t haut = rax >> 16; // 0x13ce
uint64_t cube = haut * haut * haut; // 0x13d8/0x13dc, deux imul
uint64_t quotient = cube / r12->modulus; // 0x13e0
uint64_t reste = cube % r12->modulus;
rax = quotient; // reinjecte dans l'iteration SUIVANTE
acc = (acc >> 1) | ((reste & 1) << 63); // 0x13e5, shrd : 1 bit/iteration
}
*sortie++ = acc; // 8 octets de sortie par tour, 4 tours = 32 octets
rax = acc; // reinjecte au tour SUIVANT (pas remis a GetTickCount())
}
}
Réimplémentation Python complète et testée : tools/crytox_lcg_poc.py (fonction
derive_key/derive_key_chain, cf docstring pour le détail).
Constat : l'intégralité de l'entropie de la clé
AES-256 (32 octets = 256 bits) dérivé d'un unique appel GetTickCount
(nombre de millisecondes depuis le démarrage de Windows, 32 bits,
résolution typique de 10 à 16 ms sur la plupart des systèmes). Le reste du
mécanisme (multiplicateur/incrémentateur LCG fixés dans le code,
élévation au cube, division modulaire, extraction bit a bit via shrd)
est de la mise en forme déterministe et publique : aucune information
supplémentaire n'est nécessaire pour reproduire intégralement la clé
au-delà de la valeur de GetTickCount au moment de l'infection (bornée
par la durée de fonctionnement du système en millisecondes, un espace de
recherche dérisoire face à un keyspace AES-256 réel). Confirmé et détaillé
au niveau assembleur ce que Zscaler avait seulement pu affirmer par sa
conclusion ("32-bit integer... not sufficient") sans montrer le
mécanisme. [r12+0x58] (le "modulus") n'a pas été pin été à une valeur
numérique précise (dépend du contenu de la configuration déchiffrée,
elle-même chiffrée par une clé non extraite ici) mais cela ne change rien
au constat : quel que soit ce modulus, la chaîne entière est une fonction
PUREMENT DÉTERMINISTE d'un seul entier 32 bits public/devinable
(GetTickCount).
Vérification dynamique partielle : l'émulation Unicorn du payload
final (tools/emulate_stage3_keygen.py) confirmé dynamiquement
l'intégralité de la chaîne jusqu'au spawn du thread de chiffrement par
lecteur (bug de paramètre CryptAcquireContextA trouvé et corrige au
passage, cf ci-dessus), et jusqu'au tout début de ce thread (un appel
d'API supplémentaire non résolu, hash 0xada80e4c, non identifié faute de
temps). L'appel réel à generer_cle_32_octets n'a pas été atteint
dynamiquement (nécessiterait de résoudre ce dernier hash et de poursuivre
la simulation du parcours de fichiers), mais la lecture statique du code
ci-dessus est sans aucune ambiguïté (structure de boucles, constantes,
séquence d'instructions parfaitement idiomatique d'un LCG) : le niveau de
confiance sur ce point ne dépend pas de cette dernière étape d'émulation.
Exploitabilité : preuve de concept de récupération de clé#
Confirmation directe du site d'appel : generer_cle_32_octets (offset
0x1396) n'est appelée qu'une seule fois dans tout le binaire, juste
après SetFilePointerEx/ReadFile sur le descripteur du fichier en cours
de traitement (offset 0xc0b, vérifié par recherche de tous les call
rel32 ciblant 0x1396) : confirme un appel par
fichier, cohérent avec Zscaler ("each file is encrypted with a unique
AES-256 key generated at runtime"). L'accumulateur [r12+0x10] n'est en
revanche jamais réinitialisé entre deux fichiers : la clé du fichier N
dépend de l'état laissé par le fichier N-1, pas seulement d'un nouvel
appel GetTickCount isole.
Conséquence directe pour la récupération : retrouver la valeur
GetTickCount() utilisée pour le premier fichier chiffre d'un run
suffit, en rejouant exactement la même récurrence, à reconstruire les clés
de tous les fichiers suivants du même run (l'accumulateur est
entièrement déterministe une fois amorce). Le coût de la récupération
complète d'une victime se ramène donc, au pire, au coût d'un seul
brute-force sur un espace de 2^32 valeurs (GetTickCount est un compteur
32 bits, résolution ~10-16 ms, boucle tous les ~49,7 jours), pas un
brute-forcé par fichier.
Extraction réelle du modulus, de la configuration, et validation bit-à-bit#
Suite directe de la section précédente : les trois manques identifiés
("modulus non extrait", "format IV/footer inconnu", "aucun fichier de
test") ont été attaques un par un par émulation Unicorn poussée
(tools/emulate_stage3_keygen.py, plusieurs passes successives
réutilisant la même instance mémoire pour rejouer les threads que
Unicorn, mono-thread, ne peut pas exécuter nativement).
Le modulus, obtenu en exécutant réellement le déchiffrement de la config#
Hook placé juste après le retour de la routine AES-NI de déchiffrement de
configuration (offset 0x1292, l'epilogue commun aux deux branches de
FUN_0047xxxx-équivalent) : dump direct de la mémoire à [r12+0x58]
après déchiffrement réel (pas de réimplémentation manuelle de l'AES
nécessaire, Unicorn/QEMU émulé nativement aesenc/aesenclast).
Modulus réel obtenu : 0xa77efce7 (2 810 117 351), pour l'échantillon
32eef267a1192a9a739ccaaae0266bc66707bb64768a764541ecb039a50cba67.
Bonus inattendu : la configuration déchiffrée (12 080 octets) contient bien plus que le modulus. Structure identifiée par inspection directe des octets décryptés :
- Octets 0-3 : le modulus lui-même (
e7 fc 7e a7, LE) -- confirme que[r12+0x58]pointe simplement vers le tout début du buffer de sortie du déchiffrement, pas un champ spécial. - Octets 4 à ~11460 : la note de rançon complète au format HTA
(
<html><head>...<HTA:APPLICATION...), interprétable directement (police, styles CSS, image en base64, script VBScript lançant%windir%\utox.exe). Contenu de la note : "YOUR FILES ARE ENCRYPTED", délai de 5 jours avant suppression de la clé privée, contact prioritaire via uTox (ID d'opérateur204E91D375BADE81DC528EFCC105A5D046DB92FCC4B75F08E151053DCD8D5025DA55D689165D), email de secoursColin_Kaan@aol.com, limite de 5 Mo pour les fichiers de test envoyés par email. - Chaînes suivantes (extraites proprement, mélange de chaînes larges UTF-16 et étroites ASCII selon le champ) :
windows,ReadMe.hta: nom du fichier de note déployé\\?\C:\*.*: motif d'énumération récursive (chemin long, tous fichiers).waiting: extension de fichier chiffré, confirme le format documente publiquement par ZscalerSOFTWARE\Microsoft\Windows\CurrentVersion\Run\: clé de persistanceSeBackupPrivilege,SeRestorePrivilege: privilèges ajustés (cohérent avecAdjustTokenPrivileges/LookupPrivilegeValueAdéjà relevés dans la surface d'API)C:\Windows\System32\mshta.exe "C:\ReadMe.hta": commande de persistance/affichage de la note au démarrage.waiting\shell\open\command\,open: association d'extension (double-clic sur un fichier chiffre relance la note)MM/dd/yyyy: format de date (probablement pour le compte à rebours de la note, dont le template contient un01/01/2000factice non substitue dans cet échantillon)- Clé publique RSA-2048 de l'attaquant, format Microsoft CryptoAPI
PUBLICKEYBLOBstandard (magicRSA1à l'offset0x2de0de la config,bType=0x06,bVersion=0x02,aiKeyAlg=CALG_RSA_KEYX,bitlen=2048, exposant65537, module extrait et vérifié valide sur 2048 bits). Clé spécifique à ce build/cette victime (comme documenté par le mécanisme de personnalisation par build déjà observé sur d'autres familles de ce dépôt, ex. Akira), pas une constante Crytox universelle.
Toutes ces valeurs sont propres à CET échantillon/build précis : un autre échantillon Crytox aurait très probablement un modulus, une clé RSA et un contact différents (même mécanisme de personnalisation par configuration chiffrée que les autres familles de ce dépôt), à re-extraire de la même façon sur un autre sample.
Validation bit-à-bit de l'algorithme LCG reconstruit#
En poussant l'émulation en trois passes successives (bootstrap ->
thread par lecteur 0x7c7 -> fonction renommer+chiffrer 0xa73,
capturée et rejouée directement une fois son adresse et son paramètre
identifiés via CreateThread), l'appel réel à generer_cle_32_octets
(0x1396) a été atteint et son exécution RÉELLE tracée pas a pas
(hooks sur 0x13c9 et 0x13e0 dans tools/emulate_stage3_keygen.py),
avec GetTickCount figé à une valeur connue.
Point à ne pas rater, visible en comparant l'état interne itération
par itération entre la sortie réelle et tools/crytox_lcg_poc.py :
l'instruction réelle est div DWORD PTR [r12+0x58], une division 32
BITS (EDX:EAX avec EDX=0 confirmé par traçage, donc en pratique
EAX / diviseur), et non une division 64 bits sur le cube entier. Seuls les 32 bits bas du cube
comptent, et le quotient qui en ressort (réinjecte à l'itération
suivante) tient sur 32 bits (RAX zéro-étendu par l'instruction DIV
elle-même). tools/crytox_lcg_poc.py suit cette convention.
Validation stricte : derive_key(tick, 0xa77efce7)
en Python reproduit exactement, octet pour octet, la sortie de la
fonction réelle exécutée dans Unicorn, vérifie sur deux valeurs
GetTickCount de test indépendantes (0x123456 et 0x7777777). Le
brute-forceur par plaintext connu (bruteforce_known_plaintext) a
également été revalidé avec ce modulus réel sur un cas synthétique :
tick=12345 retrouvé en 0,9 seconde.
Ce qui reste ouvert#
- Le format exact de l'IV et du footer du fichier chiffré n'a
toujours pas été tracé jusqu'au bout : la fonction
0xa73(renommer + chiffrer un fichier) a été localisée et son début confirmé dynamiquement (renommage viaMoveFileWavant chiffrement, extension.waitingajoutée,CreateFileWsur le fichier renommé), mais la boucle de lecture/chiffrement/écriture elle-même (au-delà de l'appel au générateur de clé) n'a pas été rejouée jusqu'à la construction de l'IV ni jusqu'au format exact du footer contenant la clé enveloppée en RSA. - Aucun fichier réellement chiffré par Crytox n'est disponible pour
valider l'oracle contre du ciphertext authentique plutôt qu'un test
synthétique -- aucun trouve publiquement (voir §Acquisition), et une
exécution réelle du malware (même en VM isolée jetable, protocole déjà
utilise sur Akira dans ce dépôt, cf
annexes/preparation-vm-dynamique.md) n'a pas été tentée ici : décision à prendre à tête reposée avant de l'entreprendre, vu la nature du risque (exécution réelle d'un ransomware), même isolé. - Question ouverte non tranchable avec les échantillons actuels (2020/ 2021) : comparer avec la description Halcyon 2025 (nouvel encrypteur maison) pour savoir si ce même défaut persiste -- nécessiterait un échantillon 2024/2025 réel, non trouvé sur MalwareBazaar/ThreatFox à ce jour (2026-07-31).
Environnement#
Identique aux autres analyses de ce dépôt (Ghidra headless via PyGhidra,
venv ~/.venvs/ctf-reverse sur disque local, PAS sur la clé USB exFAT --
voir la mémoire dédiée à ce piège). Outils génériques copiés depuis
gunra-time-seed-rng/tools/ (list_functions.py, decompile_by_name.py,
find_api_callers.py, decompile_addr.py, find_string_refs.py,
find_constant_refs.py).