/* OpenIDA - Outil d'analyse de fichiers binaires
* operand.c - aide à la création d'opérandes Dalvik
*
* Copyright (C) 2010-2012 Cyrille Bagard
*
* This file is part of OpenIDA.
*
* OpenIDA 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.
*
* OpenIDA 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 "operand.h"
#include
#include
/* Liste de tous les types d'opérandes */
typedef enum _DalvikOperandID
{
DOI_INVALID,
DOI_REGISTER_4,
DOI_REGISTER_8,
DOI_REGISTER_16,
DOI_IMMEDIATE_4,
DOI_IMMEDIATE_8,
DOI_IMMEDIATE_16,
DOI_IMMEDIATE_32,
DOI_IMMEDIATE_64,
DOI_IMMEDIATE_H16,
DOI_POOL_CONST,
DOI_POOL_CONST_WIDE,
DOI_TARGET_8,
DOI_TARGET_16,
DOI_TARGET_32
} DalvikOperandID;
/* Procède à la lecture d'opérandes pour une instruction. */
static bool dalvik_read_basic_operands(GArchInstruction *, const GDexFormat *, const bin_t *, off_t *, off_t, bool *, SourceEndian, DalvikOperandType, va_list);
/* Procède à la lecture d'opérandes pour une instruction. */
static bool dalvik_read_fixed_operands(GArchInstruction *, const GDexFormat *, const bin_t *, off_t *, off_t, bool *, SourceEndian, DalvikOperandType);
/* Procède à la lecture d'opérandes pour une instruction. */
static bool dalvik_read_variatic_operands(GArchInstruction *, const GDexFormat *, const bin_t *, off_t *, off_t, bool *, SourceEndian, DalvikOperandType);
/******************************************************************************
* *
* Paramètres : instr = instruction dont la définition est incomplète. [OUT]*
* format = format du fichier contenant le code. *
* data = flux de données à analyser. *
* pos = position courante dans ce flux. [OUT] *
* len = taille totale des données à analyser. *
* low = position éventuelle des 4 bits visés. [OUT] *
* endian = boutisme lié au binaire accompagnant. *
* model = type d'opérandes attendues. *
* ap = éventuels arguments complémentaires. *
* *
* Description : Procède à la lecture d'opérandes pour une instruction. *
* *
* Retour : Bilan de l'opération : true en cas de succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool dalvik_read_basic_operands(GArchInstruction *instr, const GDexFormat *format, const bin_t *data, off_t *pos, off_t len, bool *low, SourceEndian endian, DalvikOperandType model, va_list ap)
{
bool result; /* Bilan à retourner */
DalvikOperandID *types; /* Liste des chargements */
DalvikOperandID *iter; /* Boucle de parcours */
GArchOperand *op; /* Opérande unique décodé */
uint16_t value16; /* Valeur sur 16 bits */
result = true;
/* Choix des opérandes à charger */
switch (model & ~DALVIK_OP_EXTRA_MASK)
{
case DALVIK_OPT_10T:
types = (DalvikOperandID []) {
DOI_TARGET_8,
DOI_INVALID
};
break;
case DALVIK_OPT_11N:
types = (DalvikOperandID []) {
DOI_REGISTER_4,
DOI_IMMEDIATE_4,
DOI_INVALID
};
break;
case DALVIK_OPT_11X:
types = (DalvikOperandID []) {
DOI_REGISTER_8,
DOI_INVALID
};
break;
case DALVIK_OPT_12X:
types = (DalvikOperandID []) {
DOI_REGISTER_4,
DOI_REGISTER_4,
DOI_INVALID
};
break;
case DALVIK_OPT_20T:
types = (DalvikOperandID []) {
DOI_TARGET_16,
DOI_INVALID
};
break;
case DALVIK_OPT_21C:
types = (DalvikOperandID []) {
DOI_REGISTER_8,
DOI_POOL_CONST,
DOI_INVALID
};
break;
case DALVIK_OPT_21H:
types = (DalvikOperandID []) {
DOI_REGISTER_8,
DOI_IMMEDIATE_H16,
DOI_INVALID
};
break;
case DALVIK_OPT_21S:
types = (DalvikOperandID []) {
DOI_REGISTER_8,
DOI_IMMEDIATE_16,
DOI_INVALID
};
break;
case DALVIK_OPT_21T:
types = (DalvikOperandID []) {
DOI_REGISTER_8,
DOI_TARGET_16,
DOI_INVALID
};
break;
case DALVIK_OPT_22B:
types = (DalvikOperandID []) {
DOI_REGISTER_8,
DOI_REGISTER_8,
DOI_IMMEDIATE_8,
DOI_INVALID
};
break;
case DALVIK_OPT_22C:
types = (DalvikOperandID []) {
DOI_REGISTER_4,
DOI_REGISTER_4,
DOI_POOL_CONST,
DOI_INVALID
};
break;
case DALVIK_OPT_22S:
types = (DalvikOperandID []) {
DOI_REGISTER_4,
DOI_REGISTER_4,
DOI_IMMEDIATE_16,
DOI_INVALID
};
break;
case DALVIK_OPT_22T:
types = (DalvikOperandID []) {
DOI_REGISTER_4,
DOI_REGISTER_4,
DOI_TARGET_16,
DOI_INVALID
};
break;
case DALVIK_OPT_22X:
types = (DalvikOperandID []) {
DOI_REGISTER_8,
DOI_REGISTER_16,
DOI_INVALID
};
break;
case DALVIK_OPT_23X:
types = (DalvikOperandID []) {
DOI_REGISTER_8,
DOI_REGISTER_8,
DOI_REGISTER_8,
DOI_INVALID
};
break;
case DALVIK_OPT_30T:
types = (DalvikOperandID []) {
DOI_TARGET_32,
DOI_INVALID
};
break;
case DALVIK_OPT_31C:
types = (DalvikOperandID []) {
DOI_REGISTER_8,
DOI_POOL_CONST_WIDE,
DOI_INVALID
};
break;
case DALVIK_OPT_31I:
types = (DalvikOperandID []) {
DOI_REGISTER_8,
DOI_IMMEDIATE_32,
DOI_INVALID
};
break;
case DALVIK_OPT_31T:
types = (DalvikOperandID []) {
DOI_REGISTER_8,
DOI_TARGET_32,
DOI_INVALID
};
break;
case DALVIK_OPT_51L:
types = (DalvikOperandID []) {
DOI_REGISTER_8,
DOI_IMMEDIATE_64,
DOI_INVALID
};
break;
default:
types = (DalvikOperandID []) {
DOI_INVALID
};
break;
}
/* Chargement des opérandes */
for (iter = types; *iter != G_TYPE_INVALID && result; iter++)
{
switch (*iter)
{
case DOI_REGISTER_4:
op = g_dalvik_register_operand_new(data, pos, len, low, MDS_4_BITS, endian);
break;
case DOI_REGISTER_8:
op = g_dalvik_register_operand_new(data, pos, len, NULL, MDS_8_BITS, endian);
break;
case DOI_REGISTER_16:
op = g_dalvik_register_operand_new(data, pos, len, NULL, MDS_16_BITS, endian);
break;
case DOI_IMMEDIATE_4:
op = _g_imm_operand_new_from_data(MDS_4_BITS, data, pos, len, low, endian);
break;
case DOI_IMMEDIATE_8:
op = g_imm_operand_new_from_data(MDS_8_BITS, data, pos, len, endian);
break;
case DOI_IMMEDIATE_16:
op = g_imm_operand_new_from_data(MDS_16_BITS, data, pos, len, endian);
break;
case DOI_IMMEDIATE_32:
op = g_imm_operand_new_from_data(MDS_32_BITS, data, pos, len, endian);
break;
case DOI_IMMEDIATE_64:
op = g_imm_operand_new_from_data(MDS_64_BITS, data, pos, len, endian);
break;
case DOI_IMMEDIATE_H16:
result = read_u16(&value16, data, pos, len, endian);
if (result)
op = g_imm_operand_new_from_value(MDS_32_BITS_SIGNED, ((uint32_t)value16) << 16);
break;
case DOI_POOL_CONST:
op = g_dalvik_pool_operand_new(format, DALVIK_OP_GET_POOL(model), data, pos, len, MDS_16_BITS, endian);
break;
case DOI_POOL_CONST_WIDE:
op = g_dalvik_pool_operand_new(format, DALVIK_OP_GET_POOL(model), data, pos, len, MDS_32_BITS, endian);
break;
case DOI_TARGET_8:
op = g_dalvik_target_operand_new(data, pos, len, MDS_8_BITS_SIGNED, endian, va_arg(ap, vmpa_t));
break;
case DOI_TARGET_16:
op = g_dalvik_target_operand_new(data, pos, len, MDS_16_BITS_SIGNED, endian, va_arg(ap, vmpa_t));
break;
case DOI_TARGET_32:
op = g_dalvik_target_operand_new(data, pos, len, MDS_32_BITS_SIGNED, endian, va_arg(ap, vmpa_t));
break;
default:
op = NULL;
break;
}
if (op == NULL) result = false;
else g_arch_instruction_attach_extra_operand(instr, op);
}
return result;
}
/******************************************************************************
* *
* Paramètres : instr = instruction dont la définition est incomplète. [OUT]*
* format = format du fichier contenant le code. *
* data = flux de données à analyser. *
* pos = position courante dans ce flux. [OUT] *
* len = taille totale des données à analyser. *
* low = position éventuelle des 4 bits visés. [OUT] *
* endian = boutisme lié au binaire accompagnant. *
* model = type d'opérandes attendues. *
* *
* Description : Procède à la lecture d'opérandes pour une instruction. *
* *
* Retour : Bilan de l'opération : true en cas de succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool dalvik_read_fixed_operands(GArchInstruction *instr, const GDexFormat *format, const bin_t *data, off_t *pos, off_t len, bool *low, SourceEndian endian, DalvikOperandType model)
{
GArchOperand *opa; /* Opérande vA décodé */
uint8_t b; /* Nbre. de registres utilisés */
GArchOperand *target1; /* Opérande visant la table #1 */
GArchOperand *target2; /* Opérande visant la table #2 */
GArchOperand *args; /* Liste des opérandes */
uint8_t i; /* Boucle de parcours */
GArchOperand *op; /* Opérande unique décodé */
opa = g_dalvik_register_operand_new(data, pos, len, low, MDS_4_BITS, endian);
if (!read_u4(&b, data, pos, len, low, endian))
goto err_va;
target1 = g_dalvik_pool_operand_new(format, DALVIK_OP_GET_POOL(model), data, pos, len, MDS_16_BITS, endian);
if (target1 == NULL) goto err_target1;
target2 = NULL;
if (0)
{
/* FIXME */
if (target2 == NULL) goto err_target2;
}
args = g_dalvik_args_operand_new();
g_arch_instruction_attach_extra_operand(instr, args);
/* Mise en place des arguments */
for (i = 0; i < MIN(b, 4); i++)
{
op = g_dalvik_register_operand_new(data, pos, len, low, MDS_4_BITS, endian);
if (op == NULL) goto err_registers;
g_dalvik_args_operand_add(G_DALVIK_ARGS_OPERAND(args), op);
}
/* Rajout des éléments finaux déjà chargés */
if (b < 5) g_object_unref(G_OBJECT(opa));
else g_dalvik_args_operand_add(G_DALVIK_ARGS_OPERAND(args), opa);
g_arch_instruction_attach_extra_operand(instr, target1);
if (target2 != NULL)
g_arch_instruction_attach_extra_operand(instr, target2);
return true;
err_registers:
if (target2 != NULL)
g_object_unref(G_OBJECT(target2));
err_target2:
g_object_unref(G_OBJECT(target1));
err_target1:
g_object_unref(G_OBJECT(opa));
err_va:
return false;
}
/******************************************************************************
* *
* Paramètres : instr = instruction dont la définition est incomplète. [OUT]*
* format = format du fichier contenant le code. *
* data = flux de données à analyser. *
* pos = position courante dans ce flux. [OUT] *
* len = taille totale des données à analyser. *
* low = position éventuelle des 4 bits visés. [OUT] *
* endian = boutisme lié au binaire accompagnant. *
* model = type d'opérandes attendues. *
* *
* Description : Procède à la lecture d'opérandes pour une instruction. *
* *
* Retour : Bilan de l'opération : true en cas de succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool dalvik_read_variatic_operands(GArchInstruction *instr, const GDexFormat *format, const bin_t *data, off_t *pos, off_t len, bool *low, SourceEndian endian, DalvikOperandType model)
{
uint8_t a; /* Nbre. de registres utilisés */
uint16_t b; /* Indice dans la table const. */
GArchOperand *target; /* Opérande visant la table */
GArchOperand *args; /* Liste des opérandes */
uint8_t i; /* Boucle de parcours */
uint16_t c; /* Indice de registre */
GArchOperand *op; /* Opérande unique décodé */
if (!read_u8(&a, data, pos, len, endian))
return false;
if (!read_u16(&b, data, pos, len, endian))
return false;
target = g_dalvik_pool_operand_new(format, DALVIK_OP_GET_POOL(model), data, pos, len, MDS_16_BITS, endian);
if (target == NULL) return false;
/* Mise en place des arguments */
args = g_dalvik_args_operand_new();
g_arch_instruction_attach_extra_operand(instr, args);
for (i = 0; i < a; i++)
{
if (i == 0 && !read_u16(&c, data, pos, len, endian))
goto drvo_registers;
op = g_dalvik_register_operand_new_from_existing(g_dalvik_register_new(c + i));
if (op == NULL) goto drvo_registers;
g_dalvik_args_operand_add(G_DALVIK_ARGS_OPERAND(args), op);
}
/* Rajout de la cible */
g_arch_instruction_attach_extra_operand(instr, target);
return true;
drvo_registers:
g_object_unref(G_OBJECT(args));
g_object_unref(G_OBJECT(target));
return false;
}
/******************************************************************************
* *
* Paramètres : instr = instruction dont la définition est incomplète. [OUT]*
* format = format du fichier contenant le code. *
* data = flux de données à analyser. *
* pos = position courante dans ce flux. [OUT] *
* len = taille totale des données à analyser. *
* endian = boutisme lié au binaire accompagnant. *
* model = type d'opérandes attendues. *
* ... = éventuelles données complémentaires. *
* *
* Description : Procède à la lecture d'opérandes pour une instruction. *
* *
* Retour : Bilan de l'opération : true en cas de succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool dalvik_read_operands(GArchInstruction *instr, const GDexFormat *format, const bin_t *data, off_t *pos, off_t len, SourceEndian endian, DalvikOperandType model, ...)
{
bool result; /* Bilan à retourner */
bool low;
off_t old_pos;
va_list ap; /* Arguments complémentaires */
off_t length;
result = true;
old_pos = *pos;
low = true;
switch (model & ~DALVIK_OP_EXTRA_MASK)
{
case DALVIK_OPT_10T:
case DALVIK_OPT_11N:
case DALVIK_OPT_11X:
case DALVIK_OPT_12X:
case DALVIK_OPT_20T:
case DALVIK_OPT_21C:
case DALVIK_OPT_21H:
case DALVIK_OPT_21S:
case DALVIK_OPT_21T:
case DALVIK_OPT_22B:
case DALVIK_OPT_22C:
case DALVIK_OPT_22S:
case DALVIK_OPT_22T:
case DALVIK_OPT_22X:
case DALVIK_OPT_23X:
case DALVIK_OPT_30T:
case DALVIK_OPT_31C:
case DALVIK_OPT_31I:
case DALVIK_OPT_31T:
case DALVIK_OPT_51L:
va_start(ap, model);
result = dalvik_read_basic_operands(instr, format, data, pos, len, &low, endian, model, ap);
va_end(ap);
break;
case DALVIK_OPT_35C:
result = dalvik_read_fixed_operands(instr, format, data, pos, len, &low, endian, model);
break;
case DALVIK_OPT_3RC:
case DALVIK_OPT_3RMS:
case DALVIK_OPT_3RFS:
result = dalvik_read_variatic_operands(instr, format, data, pos, len, &low, endian, model);
break;
default:
break;
}
*pos = old_pos;
if (*pos < len)
{
(*pos)++;
length = DALVIK_OP_GET_LEN(model);
if (length > 1)
*pos += (length - 1) * sizeof(uint16_t);
}
return result;
}