/* Chrysalide - Outil d'analyse de fichiers binaires
* operand.c - aide à la création d'opérandes Dalvik
*
* Copyright (C) 2010-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 "operand.h"
#include
#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;
/* Crée un opérande visant une instruction Dalvik. */
static GArchOperand *dalvik_build_target_operand(const GBinContent *, vmpa2t *, MemoryDataSize , SourceEndian, const vmpa2t *);
/* Procède à la lecture d'opérandes pour une instruction. */
static bool dalvik_read_basic_operands(GArchInstruction *, GDexFormat *, const GBinContent *, vmpa2t *, bool *, SourceEndian, DalvikOperandType, ...);
/* Procède à la lecture d'opérandes pour une instruction. */
static bool dalvik_read_fixed_operands(GArchInstruction *, GDexFormat *, const GBinContent *, vmpa2t *, bool *, SourceEndian, DalvikOperandType);
/* Procède à la lecture d'opérandes pour une instruction. */
static bool dalvik_read_variatic_operands(GArchInstruction *, GDexFormat *, const GBinContent *, vmpa2t *, bool *, SourceEndian, DalvikOperandType);
/******************************************************************************
* *
* Paramètres : content = flux de données à analyser. *
* pos = position courante dans ce flux. [OUT] *
* size = taille de l'opérande. *
* endian = ordre des bits dans la source. *
* base = adresse de référence pour le calcul. *
* *
* Description : Crée un opérande visant une instruction Dalvik. *
* *
* Retour : Opérande mis en place. *
* *
* Remarques : - *
* *
******************************************************************************/
static GArchOperand *dalvik_build_target_operand(const GBinContent *content, vmpa2t *pos, MemoryDataSize size, SourceEndian endian, const vmpa2t *base)
{
GArchOperand *result; /* Structure à retourner */
phys_t offset; /* Emplacement de base */
int8_t val8; /* Valeur sur 8 bits */
int16_t val16; /* Valeur sur 16 bits */
int32_t val32; /* Valeur sur 32 bits */
bool test; /* Bilan de lecture */
phys_t address; /* Adresse finale visée */
offset = get_phy_addr(base);
switch (size)
{
case MDS_8_BITS_SIGNED:
test = g_binary_content_read_s8(content, pos, &val8);
address = offset + val8 * sizeof(uint16_t);
break;
case MDS_16_BITS_SIGNED:
test = g_binary_content_read_s16(content, pos, endian, &val16);
address = offset + val16 * sizeof(uint16_t);
break;
case MDS_32_BITS_SIGNED:
test = g_binary_content_read_s32(content, pos, endian, &val32);
address = offset + val32 * sizeof(uint16_t);
break;
default:
test = false;
break;
}
if (!test)
return NULL;
result = g_imm_operand_new_from_value(MDS_32_BITS, address);
return result;
}
/******************************************************************************
* *
* Paramètres : instr = instruction dont la définition est incomplète.[OUT]*
* format = format du fichier contenant le code. *
* content = flux de données à analyser. *
* pos = position courante dans ce flux. [OUT] *
* low = position éventuelle des 4 bits visés. [OUT] *
* endian = boutisme lié au binaire accompagnant. *
* model = type d'opérandes attendues. *
* ... = é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, GDexFormat *format, const GBinContent *content, vmpa2t *pos, bool *low, SourceEndian endian, DalvikOperandType model, ...)
{
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 */
DalvikPoolType pool_type; /* Type de table à manipuler */
va_list ap; /* Arguments complémentaires */
const vmpa2t *base; /* Base pour les sauts de code */
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_32X:
types = (DalvikOperandID []) {
DOI_REGISTER_16,
DOI_REGISTER_16,
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++)
{
op = NULL; /* Nul de GCC */
switch (*iter)
{
case DOI_REGISTER_4:
op = g_dalvik_register_operand_new(content, pos, low, MDS_4_BITS, endian);
break;
case DOI_REGISTER_8:
op = g_dalvik_register_operand_new(content, pos, NULL, MDS_8_BITS, endian);
break;
case DOI_REGISTER_16:
op = g_dalvik_register_operand_new(content, pos, NULL, MDS_16_BITS, endian);
break;
case DOI_IMMEDIATE_4:
op = _g_imm_operand_new_from_data(MDS_4_BITS, content, pos, low, endian);
break;
case DOI_IMMEDIATE_8:
op = g_imm_operand_new_from_data(MDS_8_BITS, content, pos, endian);
break;
case DOI_IMMEDIATE_16:
op = g_imm_operand_new_from_data(MDS_16_BITS, content, pos, endian);
break;
case DOI_IMMEDIATE_32:
op = g_imm_operand_new_from_data(MDS_32_BITS, content, pos, endian);
break;
case DOI_IMMEDIATE_64:
op = g_imm_operand_new_from_data(MDS_64_BITS, content, pos, endian);
break;
case DOI_IMMEDIATE_H16:
result = g_binary_content_read_u16(content, pos, endian, &value16);
if (result)
op = g_imm_operand_new_from_value(MDS_32_BITS_SIGNED, ((uint32_t)value16) << 16);
break;
case DOI_POOL_CONST:
pool_type = DALVIK_OP_GET_POOL(model);
op = g_dalvik_pool_operand_new(format, pool_type, content, pos, MDS_16_BITS, endian);
break;
case DOI_POOL_CONST_WIDE:
pool_type = DALVIK_OP_GET_POOL(model);
op = g_dalvik_pool_operand_new(format, pool_type, content, pos, MDS_32_BITS, endian);
break;
case DOI_TARGET_8:
va_start(ap, model);
base = va_arg(ap, const vmpa2t *);
op = dalvik_build_target_operand(content, pos, MDS_8_BITS_SIGNED, endian, base);
va_end(ap);
break;
case DOI_TARGET_16:
va_start(ap, model);
base = va_arg(ap, const vmpa2t *);
op = dalvik_build_target_operand(content, pos, MDS_16_BITS_SIGNED, endian, base);
va_end(ap);
break;
case DOI_TARGET_32:
va_start(ap, model);
base = va_arg(ap, const vmpa2t *);
op = dalvik_build_target_operand(content, pos, MDS_32_BITS_SIGNED, endian, base);
va_end(ap);
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. *
* content = flux de données à analyser. *
* pos = position courante dans ce flux. [OUT] *
* 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, GDexFormat *format, const GBinContent *content, vmpa2t *pos, bool *low, SourceEndian endian, DalvikOperandType model)
{
GArchOperand *opg; /* Opérande G décodé */
uint8_t a; /* Nbre. de registres utilisés */
GArchOperand *target; /* Opérande visant la table #1 */
GArchOperand *args; /* Liste des opérandes */
uint8_t i; /* Boucle de parcours */
GArchOperand *op; /* Opérande unique décodé */
opg = g_dalvik_register_operand_new(content, pos, low, MDS_4_BITS, endian);
if (!g_binary_content_read_u4(content, pos, low, &a))
goto err_va;
if (a == 5 && opg == NULL)
goto err_no_opg;
target = g_dalvik_pool_operand_new(format, DALVIK_OP_GET_POOL(model), content, pos, MDS_16_BITS, endian);
if (target == NULL) goto err_target;
/* Mise en place des arguments */
args = g_dalvik_args_operand_new();
for (i = 0; i < MIN(a, 4); i++)
{
op = g_dalvik_register_operand_new(content, pos, low, MDS_4_BITS, endian);
if (op == NULL) goto err_registers;
args = G_ARCH_OPERAND(g_dalvik_args_operand_add(G_DALVIK_ARGS_OPERAND(args), op, NULL));
}
/* Consommation pleine et entière */
for (; i < 4; i++)
if (!g_binary_content_read_u4(content, pos, low, (uint8_t []) { 0 }))
goto err_padding;
/* Rajout des éléments finaux déjà chargés */
if (a == 5)
args = G_ARCH_OPERAND(g_dalvik_args_operand_add(G_DALVIK_ARGS_OPERAND(args), opg, NULL));
else
{
if (opg != NULL)
g_object_unref(G_OBJECT(opg));
}
g_arch_instruction_attach_extra_operand(instr, args);
/* Rajout de la cible */
g_arch_instruction_attach_extra_operand(instr, target);
return true;
err_padding:
err_registers:
g_object_unref(G_OBJECT(target));
err_target:
if (opg != NULL)
g_object_unref(G_OBJECT(opg));
err_no_opg:
err_va:
return false;
}
/******************************************************************************
* *
* Paramètres : instr = instruction dont la définition est incomplète.[OUT]*
* format = format du fichier contenant le code. *
* content = flux de données à analyser. *
* pos = position courante dans ce flux. [OUT] *
* 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, GDexFormat *format, const GBinContent *content, vmpa2t *pos, bool *low, SourceEndian endian, DalvikOperandType model)
{
uint8_t a; /* Nbre. de registres utilisés */
uint16_t c; /* Indice de registre */
GArchOperand *target; /* Opérande visant la table */
GArchOperand *args; /* Liste des opérandes */
uint8_t i; /* Boucle de parcours */
GArchOperand *op; /* Opérande unique décodé */
if (!g_binary_content_read_u8(content, pos, &a))
return false;
target = g_dalvik_pool_operand_new(format, DALVIK_OP_GET_POOL(model), content, pos, MDS_16_BITS, endian);
if (target == NULL) return false;
if (!g_binary_content_read_u16(content, pos, endian, &c))
return false;
/* Mise en place des arguments */
args = g_dalvik_args_operand_new();
for (i = 0; i < a; i++)
{
op = g_dalvik_register_operand_new_from_existing(g_dalvik_register_new(c + i));
if (op == NULL) goto drvo_registers;
args = G_ARCH_OPERAND(g_dalvik_args_operand_add(G_DALVIK_ARGS_OPERAND(args), op, NULL));
}
g_arch_instruction_attach_extra_operand(instr, args);
/* Rajout de la cible */
g_arch_instruction_attach_extra_operand(instr, target);
return true;
drvo_registers:
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. *
* content = flux de données à analyser. *
* pos = position courante dans ce flux. [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 : - *
* *
******************************************************************************/
bool dalvik_read_operands(GArchInstruction *instr, GExeFormat *format, const GBinContent *content, vmpa2t *pos, SourceEndian endian, DalvikOperandType model)
{
bool result; /* Bilan à retourner */
GDexFormat *dformat; /* Autre version du format */
bool low; /* Partie d'octets à lire */
#ifndef NDEBUG
vmpa2t old; /* Position avant traitements */
#endif
vmpa2t base; /* Base pour les sauts de code */
vmpa2t *extra; /* Information complémentaire */
#ifndef NDEBUG
phys_t expected; /* Consommation attendue */
phys_t consumed; /* Consommation réelle */
#endif
result = true;
dformat = G_DEX_FORMAT(format);
low = true;
#ifndef NDEBUG
copy_vmpa(&old, pos);
#endif
/* Récupération de la base ? */
if (DALVIK_OP_GET_MNEMONIC(model) == 'T')
{
extra = &base;
copy_vmpa(extra, pos);
deminish_vmpa(extra, 1);
}
else extra = NULL;
/* Bourrage : ØØ|op ? */
switch (model & ~DALVIK_OP_EXTRA_MASK)
{
case DALVIK_OPT_10X:
case DALVIK_OPT_20T:
case DALVIK_OPT_30T:
case DALVIK_OPT_32X:
result = g_binary_content_seek(content, pos, 1);
break;
default:
break;
}
/* Décodage... */
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_32X:
case DALVIK_OPT_51L:
result = dalvik_read_basic_operands(instr, dformat, content, pos, &low, endian, model, extra);
break;
case DALVIK_OPT_35C:
result = dalvik_read_fixed_operands(instr, dformat, content, pos, &low, endian, model);
break;
case DALVIK_OPT_3RC:
case DALVIK_OPT_3RMS:
case DALVIK_OPT_3RFS:
result = dalvik_read_variatic_operands(instr, dformat, content, pos, &low, endian, model);
break;
default:
break;
}
#ifndef NDEBUG
/* Vérification d'implémentation */
if (result)
{
expected = DALVIK_OP_GET_LEN(model) * 2;
consumed = 1 + compute_vmpa_diff(&old, pos);
assert(consumed == expected);
}
#endif
return result;
}
/******************************************************************************
* *
* Paramètres : instr = instruction dont la définition est incomplète. *
* *
* Description : Procède à la lecture d'opérandes pour une instruction. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void dalvik_mark_first_operand_as_written(GArchInstruction *instr)
{
GArchOperand *operand; /* Première opérande visé */
operand = g_arch_instruction_get_operand(instr, 0);
g_dalvik_register_operand_mark_as_written(G_DALVIK_REGISTER_OPERAND(operand));
}