/* Chrysalide - Outil d'analyse de fichiers binaires
* format.c - support des différents formats binaires
*
* Copyright (C) 2009-2017 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* Chrysalide is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Chrysalide is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar. If not, see .
*/
#include "format.h"
#include
#include
#include
#include
#include "format-int.h"
#include "dex/dex.h"
#include "dwarf/dwarf.h"
#include "elf/elf.h"
#include "java/java.h"
#include "pe/pe.h"
#include "../arch/processor.h"
#include "../common/sort.h"
#include "../gui/panels/log.h"
#include "../plugins/pglist.h"
/* Initialise la classe des formats binaires génériques. */
static void g_binary_format_class_init(GBinFormatClass *);
/* Initialise une instance de format binaire générique. */
static void g_binary_format_init(GBinFormat *);
/* Retire un symbole de la collection du format binaire. */
static void _g_binary_format_remove_symbol(GBinFormat *, size_t);
/* Supprime les éventuels doublons au sein des symboles. */
static void g_binary_format_delete_duplicated_symbols(GBinFormat *);
/* Recherche le symbole associé à une adresse. */
static bool _g_binary_format_find_symbol(const GBinFormat *, const vmpa2t *, __compar_fn_t, size_t *, GBinSymbol **);
/* Indique le type défini pour un format binaire générique. */
G_DEFINE_TYPE(GBinFormat, g_binary_format, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des formats binaires génériques. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_format_class_init(GBinFormatClass *klass)
{
}
/******************************************************************************
* *
* Paramètres : format = instance à initialiser. *
* *
* Description : Initialise une instance de format binaire générique. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_format_init(GBinFormat *format)
{
g_rw_lock_init(&format->pt_lock);
g_rw_lock_init(&format->syms_lock);
}
/******************************************************************************
* *
* Paramètres : format = instance à traiter. *
* status = barre de statut à tenir informée. *
* *
* Description : Effectue les ultimes opérations de chargement d'un binaire. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_complete_loading(GBinFormat *format, GtkStatusStack *status)
{
handle_binary_format(PGA_FORMAT_LOADER_LAST, format, status);
g_binary_format_delete_duplicated_symbols(format);
return true;
}
/* FIXME : g_rw_lock_clear(&format->syms_lock);*/
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
* content = contenu binaire à parcourir. *
* *
* Description : Définit le contenu binaire à analyser. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_format_set_content(GBinFormat *format, GBinContent *content)
{
assert(format->content == NULL);
g_object_ref(G_OBJECT(content));
format->content = content;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
* *
* Description : Fournit une référence vers le contenu binaire analysé. *
* *
* Retour : Gestionnaire de contenu binaire en place. *
* *
* Remarques : - *
* *
******************************************************************************/
GBinContent *g_binary_format_get_content(const GBinFormat *format)
{
g_object_ref(G_OBJECT(format->content));
return format->content;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
* *
* Description : Indique le boutisme employé par le format binaire analysé. *
* *
* Retour : Boutisme associé au format. *
* *
* Remarques : - *
* *
******************************************************************************/
SourceEndian g_binary_format_get_endianness(const GBinFormat *format)
{
return G_BIN_FORMAT_GET_CLASS(format)->get_endian(format);
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à compléter. *
* pt = point de l'espace mémoire à considérer. *
* entry = nature du point fourni. *
* *
* Description : Enregistre une adresse comme début d'une zone de code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_format_register_code_point(GBinFormat *format, virt_t pt, bool entry)
{
g_rw_lock_writer_lock(&format->pt_lock);
if (entry)
{
format->entry_points = (virt_t *)realloc(format->entry_points,
++format->ep_count * sizeof(virt_t));
format->entry_points[format->ep_count - 1] = pt;
}
else
{
if (format->xp_count == format->xp_allocated)
{
format->xp_allocated += EXTRA_POINT_BLOCK;
format->extra_points = (virt_t *)realloc(format->extra_points,
format->xp_allocated * sizeof(virt_t));
}
format->extra_points[format->xp_count++] = pt;
}
g_rw_lock_writer_unlock(&format->pt_lock);
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
* ctx = contexte de désassemblage à préparer. *
* *
* Description : Fournit un contexte initialisé pour un désassemblage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_format_setup_disassembling_context(GBinFormat *format, GProcContext *ctx)
{
size_t i; /* Boucle de parcours */
g_rw_lock_reader_lock(&format->pt_lock);
for (i = 0; i < format->ep_count; i++)
g_proc_context_push_drop_point(ctx, DPL_ENTRY_POINT, format->entry_points[i]);
for (i = 0; i < format->xp_count; i++)
g_proc_context_push_drop_point(ctx, DPL_SYMBOL, format->extra_points[i]);
g_rw_lock_reader_unlock(&format->pt_lock);
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à compléter. *
* symbol = symbole à ajouter à la liste. *
* *
* Description : Ajoute un symbole à la collection du format binaire. *
* *
* Retour : true si le symbole était bien localisé et a été inséré. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_add_symbol(GBinFormat *format, GBinSymbol *symbol)
{
bool result; /* Statut d'ajout à retourner */
#ifndef NDEBUG
const mrange_t *range; /* Couverture du symbole */
const vmpa2t *addr; /* Emplacement du symbole */
#endif
size_t index; /* Indice du point d'insertion */
GBinRoutine *routine; /* Nouvelle routine à insérer */
/**
* Pour que les fonctions de recherche basées sur _g_binary_format_find_symbol()
* fassent bien leur office, il faut que les symboles soient triés.
*
* Cependant, les localisations à satisfaire lors d'une recherche recontrent
* un problème si les positions physiques ne sont pas renseignées. En effet
* les adresses virtuelles en sont potentiellement décorrélées (c'est le cas
* avec le format ELF par exemple, où les zones en mémoire ne suivent pas le
* même ordre que les segments du binaire).
*
* Comme les comparaisons entre localisations se réalisent sur les éléments
* renseignés communs, à commencer par la position physique si c'est possible,
* une localisation s'appuyant uniquement sur une adresse virtuelle va être
* analysée suivant une liste non triée d'adresses virtuelles.
*
* On corrige donc le tir si besoin est en forçant la comparaison via les
* positions physiques.
*/
#ifndef NDEBUG
range = g_binary_symbol_get_range(symbol);
addr = get_mrange_addr(range);
assert(has_phys_addr(addr));
#endif
g_rw_lock_writer_lock(&format->syms_lock);
/**
* Avec tous les traitements parallèles, il est possible que plusieurs chemins d'exécution
* amènent à la création d'un même symbole.
*
* Plutôt que de verrouiller la liste des symboles en amont (et donc assez longtemps)
* pour faire une vérification avant construction puis ajout, on préfère limiter
* l'état figé à cette seule fonction, quitte à annuler le travail fourni pour la
* construction du symbole dans les cas peu fréquents où le symbole était déjà en place.
*/
result = bsearch_index(&symbol, format->symbols, format->symbols_count,
sizeof(GBinSymbol *), (__compar_fn_t)g_binary_symbol_cmp, &index);
if (!result)
{
format->symbols = _qinsert(format->symbols, &format->symbols_count,
sizeof(GBinSymbol *), &symbol, index);
result = true;
switch (g_binary_symbol_get_target_type(symbol))
{
case STP_ROUTINE:
case STP_ENTRY_POINT:
routine = g_binary_symbol_get_routine(symbol);
format->routines = qinsert(format->routines, &format->routines_count,
sizeof(GBinRoutine *), (__compar_fn_t)g_binary_routine_compare,
&routine);
break;
default:
break;
}
}
else
g_object_unref(G_OBJECT(symbol));
g_rw_lock_writer_unlock(&format->syms_lock);
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à compléter. *
* index = indice du symbole à retirer de la liste. *
* *
* Description : Retire un symbole de la collection du format binaire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void _g_binary_format_remove_symbol(GBinFormat *format, size_t index)
{
GBinSymbol *symbol; /* Symbole visé par l'opération*/
GBinRoutine *routine; /* Eventuelle routine associée */
size_t i; /* Boucle de parcours */
/**
* TODO : envoyer un signal pour avertir les opérandes concernées.
* TODO : vérifier les conditions d'accès (verrou).
*/
symbol = format->symbols[index];
switch (g_binary_symbol_get_target_type(symbol))
{
case STP_ROUTINE:
case STP_ENTRY_POINT:
routine = g_binary_symbol_get_routine(symbol);
for (i = 0; i < format->routines_count; i++)
if (format->routines[i] == routine)
break;
assert(i < format->routines_count);
if ((i + 1) < format->routines_count)
memmove(&format->routines[i], &format->routines[i + 1],
(format->routines_count - i - 1) * sizeof(GBinRoutine *));
format->routines = (GBinRoutine **)realloc(format->routines,
--format->routines_count * sizeof(GBinRoutine *));
break;
default:
break;
}
assert(index < format->symbols_count);
if ((index + 1) < format->symbols_count)
memmove(&format->symbols[index], &format->symbols[index + 1],
(format->symbols_count - index - 1) * sizeof(GBinSymbol *));
format->symbols = (GBinSymbol **)realloc(format->symbols,
--format->symbols_count * sizeof(GBinSymbol *));
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à compléter. *
* symbol = symbole à retirer de la liste. *
* *
* Description : Retire un symbole de la collection du format binaire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_format_remove_symbol(GBinFormat *format, GBinSymbol *symbol)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < format->symbols_count; i++)
if (format->symbols[i] == symbol)
break;
_g_binary_format_remove_symbol(format, i);
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à corriger si besoin est. *
* *
* Description : Supprime les éventuels doublons au sein des symboles. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_format_delete_duplicated_symbols(GBinFormat *format)
{
size_t i; /* Boucle de parcours */
const mrange_t *range; /* Emplacement à consulter */
mrange_t last; /* Dernière localisation vue */
size_t index; /* Indice de suppression */
g_rw_lock_writer_lock(&format->syms_lock);
if (format->symbols_count > 1)
{
range = g_binary_symbol_get_range(format->symbols[0]);
copy_mrange(&last, range);
}
for (i = 1; i < format->symbols_count; i++)
{
range = g_binary_symbol_get_range(format->symbols[i]);
if (cmp_vmpa(get_mrange_addr(&last), get_mrange_addr(range)) == 0)
{
/* TODO : établir une meilleur comparaison pour trouver le bon candidat */
/* TODO : (éviter de) traiter selon le type aussi */
index = (get_mrange_length(&last) == 0 ? i - 1 : i);
/*
printf("DELETE @ %zu / 0x%08x '%s'\n", index,
(unsigned int)last.addr.virtual,
g_binary_symbol_get_label(format->symbols[index]));
*/
_g_binary_format_remove_symbol(format, index);
i--;
}
copy_mrange(&last, range);
}
g_rw_lock_writer_unlock(&format->syms_lock);
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* count = taille du tableau créé. [OUT] *
* *
* Description : Fournit la liste de tous les symboles détectés. *
* *
* Retour : Tableau créé ou NULL si aucun symbole trouvé. *
* *
* Remarques : - *
* *
******************************************************************************/
GBinSymbol **g_binary_format_get_symbols(const GBinFormat *format, size_t *count)
{
*count = format->symbols_count;
return format->symbols;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* addr = adresse liée à la chaîne à traiter. *
* length = taille de la chaîne à représenter. *
* *
* Description : Construit une désignation pour chaîne de caractères. *
* *
* Retour : Chaîne de caractères à libérer de la mémoire. *
* *
* Remarques : - *
* *
******************************************************************************/
char *create_string_label(GBinFormat *format, const vmpa2t *addr, size_t length)
{
char *result; /* Nouvelle chaîne à retourner */
vmpa2t pos; /* Tête de lecture modifiable */
const bin_t *base; /* Contenu complet et original */
unsigned int wc; /* Nombre de mots rencontrés */
size_t iter; /* Tête d'écriture de recopie */
size_t i; /* Boucle de parcours */
bool empty; /* Base de l'étiquette vide ? */
GBinSymbol *found; /* Symbole similaire trouvé */
VMPA_BUFFER(last_sfx); /* Dernier suffixe à intégrer */
copy_vmpa(&pos, addr);
base = g_binary_content_get_raw_access(format->content, &pos, length);
if (base == NULL)
return NULL;
result = (char *)calloc(length + 5 + VMPA_MAX_LEN + 1, sizeof(char));
wc = 0;
iter = 0;
for (i = 0; i < length; i++)
{
if (isalnum(base[i])) result[iter++] = tolower(base[i]);
else if (iter > 0)
{
if (result[iter - 1] != '_') wc++;
if (wc == 3) break;
if (result[iter - 1] != '_') result[iter++] = '_';
}
}
/* Détermination du suffixe suffisant */
empty = (iter == 0);
if (iter > 0 && result[iter - 1] != '_') result[iter++] = '_';
strcpy(&result[iter], "str");
iter += 3;
found = NULL;
if (empty || g_binary_format_find_symbol_by_label(format, result, &found))
{
if (found != NULL)
g_object_unref(G_OBJECT(found));
if (iter > 0 && result[iter - 1] != '_') result[iter++] = '_';
assert(has_phys_addr(addr) || has_virt_addr(addr));
/* TODO : use_phy_instead_of_virt */
if (has_virt_addr(addr))
vmpa2_virt_to_string(addr, MDS_UNDEFINED, last_sfx, NULL);
else
vmpa2_phys_to_string(addr, MDS_UNDEFINED, last_sfx, NULL);
strcpy(&result[iter], last_sfx);
}
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* label = étiquette à retrouver lors des recherches. *
* symbol = éventuel symbole trouvé à déréfenrencer. [OUT] *
* *
* Description : Recherche le symbole correspondant à une étiquette. *
* *
* Retour : true si l'opération a été un succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_find_symbol_by_label(GBinFormat *format, const char *label, GBinSymbol **symbol)
{
bool result; /* Bilan à retourner */
size_t i; /* Boucle de parcours */
const char *cur_lbl; /* Etiquette courante */
result = false;
g_rw_lock_reader_lock(&format->syms_lock);
for (i = 0; i < format->symbols_count && !result; i++)
{
cur_lbl = g_binary_symbol_get_label(format->symbols[i]);
if (cur_lbl == NULL) continue;
if (strcmp(label, cur_lbl) == 0)
{
*symbol = format->symbols[i];
g_object_ref(G_OBJECT(*symbol));
result = true;
}
}
g_rw_lock_reader_unlock(&format->syms_lock);
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* addr = adresse à cibler lors des recherches. *
* fn = méthode de comparaison des symboles. *
* index = indice de l'éventuel symbole trouvé ou NULL. [OUT] *
* symbol = éventuel symbole trouvé à déréfenrencer. [OUT] *
* *
* Description : Recherche le symbole associé à une adresse. *
* *
* Retour : true si l'opération a été un succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool _g_binary_format_find_symbol(const GBinFormat *format, const vmpa2t *addr, __compar_fn_t fn, size_t *index, GBinSymbol **symbol)
{
bool result; /* Bilan à retourner */
void *found; /* Résultat de recherches */
/**
* Pour ce qui est des justifications quant à la vérification suivante,
* se référer aux commentaires placés dans g_binary_format_add_symbol().
*/
assert(has_phys_addr(addr));
found = bsearch(addr, format->symbols, format->symbols_count, sizeof(GBinSymbol *), fn);
if (found != NULL)
{
if (index != NULL)
*index = (GBinSymbol **)found - format->symbols;
if (symbol != NULL)
{
*symbol = *(GBinSymbol **)found;
g_object_ref(G_OBJECT(*symbol));
}
result = true;
}
else
{
*symbol = NULL;
result = false;
}
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* addr = adresse à cibler lors des recherches. *
* symbol = éventuel symbole trouvé à déréfenrencer. [OUT] *
* *
* Description : Recherche le symbole correspondant à une adresse. *
* *
* Retour : true si l'opération a été un succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_find_symbol_at(GBinFormat *format, const vmpa2t *addr, GBinSymbol **symbol)
{
bool result; /* Bilan à retourner */
int find_symbol(const vmpa2t *addr, const GBinSymbol **sym)
{
const mrange_t *range; /* Espace mémoire parcouru */
range = g_binary_symbol_get_range(*sym);
return cmp_vmpa(addr, get_mrange_addr(range));
}
g_rw_lock_reader_lock(&format->syms_lock);
result = _g_binary_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, NULL, symbol);
g_rw_lock_reader_unlock(&format->syms_lock);
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* addr = adresse à cibler lors des recherches. *
* symbol = éventuel symbole trouvé à déréfenrencer. [OUT] *
* *
* Description : Recherche le symbole contenant une adresse. *
* *
* Retour : true si l'opération a été un succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_find_symbol_for(GBinFormat *format, const vmpa2t *addr, GBinSymbol **symbol)
{
bool result; /* Bilan à retourner */
int find_symbol(const vmpa2t *addr, const GBinSymbol **sym)
{
const mrange_t *range; /* Espace mémoire parcouru */
range = g_binary_symbol_get_range(*sym);
return cmp_mrange_with_vmpa(range, addr);
}
g_rw_lock_reader_lock(&format->syms_lock);
result = _g_binary_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, NULL, symbol);
g_rw_lock_reader_unlock(&format->syms_lock);
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* addr = adresse à cibler lors des recherches. *
* symbol = éventuel symbole trouvé à déréfenrencer. [OUT] *
* *
* Description : Recherche le symbole suivant celui lié à une adresse. *
* *
* Retour : true si l'opération a été un succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_find_next_symbol_at(GBinFormat *format, const vmpa2t *addr, GBinSymbol **symbol)
{
bool result; /* Bilan à retourner */
size_t index; /* Indice à considérer */
int find_symbol(const vmpa2t *addr, const GBinSymbol **sym)
{
const mrange_t *range; /* Espace mémoire parcouru */
range = g_binary_symbol_get_range(*sym);
return cmp_mrange_with_vmpa(range, addr);
}
g_rw_lock_reader_lock(&format->syms_lock);
result = _g_binary_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, &index, NULL);
if (result && (index + 1) < format->symbols_count)
{
*symbol = format->symbols[index + 1];
g_object_ref(G_OBJECT(*symbol));
}
else
{
*symbol = NULL;
result = false;
}
g_rw_lock_reader_unlock(&format->syms_lock);
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* addr = adresse à cibler lors des recherches. *
* strict = indication de tolérance acceptée. *
* symbol = éventuel symbole trouvé à déréfenrencer. [OUT] *
* diff = décallage entre l'adresse et le symbole. [OUT] *
* *
* Description : Recherche le symbole correspondant à une adresse. *
* *
* Retour : true si l'opération a été un succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_resolve_symbol(GBinFormat *format, const vmpa2t *addr, bool strict, GBinSymbol **symbol, phys_t *diff)
{
bool result; /* Bilan à retourner */
const mrange_t *range; /* Espace mémoire parcouru */
if (strict)
result = g_binary_format_find_symbol_at(format, addr, symbol);
else
result = g_binary_format_find_symbol_for(format, addr, symbol);
if (result)
{
range = g_binary_symbol_get_range(*symbol);
*diff = compute_vmpa_diff(get_mrange_addr(range), addr);
assert(!strict || *diff == 0);
}
else
*diff = 0;
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* count = taille du tableau créé. [OUT] *
* *
* Description : Fournit le prototype de toutes les routines détectées. *
* *
* Retour : Tableau créé ou NULL si aucun symbole de routine trouvé. *
* *
* Remarques : - *
* *
******************************************************************************/
GBinRoutine **g_binary_format_get_routines(const GBinFormat *format, size_t *count)
{
*count = format->routines_count;
return format->routines;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* count = taille de la liste retournée. [OUT] *
* defsrc = fichier de code principal. [OUT] *
* *
* Description : Fournit la liste des fichiers source détectés. *
* *
* Retour : Liste de noms de fichier ou NULL si aucun. *
* *
* Remarques : - *
* *
******************************************************************************/
const char * const *g_binary_format_get_source_files(const GBinFormat *format, size_t *count, size_t *defsrc)
{
*count = format->src_count;
*defsrc = format->def_source;
return format->src_files;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* buffer = tampon mis à disposition pour la sortie. *
* filename = nom du fichier source à cibler. *
* *
* Description : Procède à la décompilation complète du format. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
#if 0
void g_binary_format_decompile(const GBinFormat *format, GCodeBuffer *buffer, const char *filename)
{
GBinRoutine **routines;
size_t count;
size_t i;
GDecInstruction *instr;
if (format->decompile != NULL)
format->decompile(format, buffer, filename);
routines = g_binary_format_get_routines(format, &count);
for (i = 0; i < count; i++)
{
//printf(" -- %s --\n", g_binary_routine_get_name(routines[i]));
//if (strcmp("cryptself", g_binary_routine_get_name(routines[i])) == 0)
{
instr = g_binary_routine_get_decomp_instructions(routines[i]);
if (instr == NULL) continue;
//g_dec_instruction_print(instr, buffer, NULL, g_java_output_new());
}
}
}
#endif