summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog12
-rw-r--r--src/analysis/disass/area.c153
-rw-r--r--tests/format/elf/Makefile5
-rw-r--r--tests/format/elf/overlapping_areas.asm115
-rw-r--r--tests/format/elf/overlapping_areas.py62
5 files changed, 335 insertions, 12 deletions
diff --git a/ChangeLog b/ChangeLog
index 8bc80cb..818be7e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
17-05-11 Cyrille Bagard <nocbos@gmail.com>
+ * src/analysis/disass/area.c:
+ Handle preloaded instructions located in two cut areas (ELF overlapping).
+
+ * tests/format/elf/Makefile:
+ Add overlapping_areas to EXECUTABLES and define rules to build the binary.
+
+ * tests/format/elf/overlapping_areas.asm:
+ * tests/format/elf/overlapping_areas.py:
+ New entries: extend the test suite.
+
+17-05-11 Cyrille Bagard <nocbos@gmail.com>
+
* plugins/readelf/strtab.c:
Handle out of bound string section length (as suggested by the test suite).
diff --git a/src/analysis/disass/area.c b/src/analysis/disass/area.c
index f4088e4..352069c 100644
--- a/src/analysis/disass/area.c
+++ b/src/analysis/disass/area.c
@@ -80,7 +80,10 @@ static bool is_range_empty_in_mem_area(mem_area *, phys_t, phys_t);
static bool is_range_busy_in_mem_area(mem_area *, phys_t, phys_t);
/* Marque une série d'octets comme ayant été traités. */
-static bool mark_range_in_mem_area_as_processed(mem_area *, GArchInstruction *, bool);
+static bool mark_range_in_mem_area_as_processed(mem_area *, GArchInstruction *, bool, bool);
+
+/* Marque une série d'octets comme ayant été traités. */
+static void mark_range_in_mem_area_as_overlapping(mem_area *, phys_t);
/* Crée une instruction issue d'un désassemblage brut. */
static GArchInstruction *load_raw_instruction_from_mem_area(mem_area *, phys_t, vmpa2t *, phys_t *);
@@ -347,9 +350,10 @@ static bool is_range_busy_in_mem_area(mem_area *area, phys_t start, phys_t len)
/******************************************************************************
* *
-* Paramètres : area = aire représentant à contenu à parcourir. *
-* instr = instruction à mémoriser pour la suite. *
-* force = impose l'enregistrement de l'instruction. *
+* Paramètres : area = aire représentant à contenu à parcourir. *
+* instr = instruction à mémoriser pour la suite. *
+* force = impose l'enregistrement de l'instruction. *
+* overlap = indique si l'instruction peut déborder de la zone. *
* *
* Description : Marque une série d'octets comme ayant été traités. *
* *
@@ -359,7 +363,7 @@ static bool is_range_busy_in_mem_area(mem_area *area, phys_t start, phys_t len)
* *
******************************************************************************/
-static bool mark_range_in_mem_area_as_processed(mem_area *area, GArchInstruction *instr, bool force)
+static bool mark_range_in_mem_area_as_processed(mem_area *area, GArchInstruction *instr, bool force, bool overlap)
{
bool result; /* Bilan d'action à renvoyer */
const vmpa2t *start; /* Adresse de départ de la zone*/
@@ -382,6 +386,36 @@ static bool mark_range_in_mem_area_as_processed(mem_area *area, GArchInstruction
offset = compute_vmpa_diff(start, addr);
+ /**
+ * On vérifie que l'instruction tient toute entière dans la zone courante.
+ *
+ * C'est normalement forcément le cas, puisque les accès aux données lues
+ * pour la mise en place d'une instruction sont restreintes à la zone couverte.
+ *
+ * Cependant ce n'est pas forcément le cas lors de l'injection des
+ * instructions préchargées. En effet, une chaîne de caractères peut être
+ * découpée sur plusieurs zones : il arrive par exemple que le segment BSS
+ * englobe une partie de la section des chaînes. Cette section est donc coupée
+ * en deux portions, ce qui conduit à la création de deux zones distinctes.
+ *
+ * Dans ce cas, on inscrit ici l'instruction résultante, et charge à
+ * l'appelant de marquer la zone suivante comme déjà partiellement traitée.
+ *
+ * Cette situation est représentée par le test 'overlapping_areas.py'.
+ */
+
+ if ((offset + len) > get_mrange_length(&area->range))
+ {
+ if (overlap)
+ len = get_mrange_length(&area->range) - offset;
+
+#ifndef NDEBUG
+ else
+ assert(false);
+#endif
+
+ }
+
/* Début des choses sérieuses */
g_mutex_lock(&area->mutex);
@@ -461,6 +495,59 @@ static bool mark_range_in_mem_area_as_processed(mem_area *area, GArchInstruction
/******************************************************************************
* *
+* Paramètres : area = aire représentant à contenu à parcourir. *
+* count = nombre d'octets à considérer comme traités. *
+* *
+* Description : Marque une série d'octets comme ayant été traités. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void mark_range_in_mem_area_as_overlapping(mem_area *area, phys_t count)
+{
+ bool status; /* Présence d'instructions ? */
+ phys_t i; /* Boucle de parcours */
+ GArchInstruction *old; /* Instruction remplacée */
+ const mrange_t *old_range; /* Emplacement de l'instruction*/
+ phys_t old_len; /* Taille de cette instruction */
+
+ assert(count <= get_mrange_length(&area->range));
+
+ g_mutex_lock(&area->mutex);
+
+ status = test_none_in_bit_field(area->processed, 0, count);
+
+ if (!status)
+ for (i = 0; i < count; i++)
+ {
+ old = area->instructions[i];
+
+ if (old != NULL)
+ {
+ old_range = g_arch_instruction_get_range(old);
+ old_len = get_mrange_length(old_range);
+
+ reset_in_bit_field(area->processed, i, old_len);
+
+ g_object_unref(G_OBJECT(old));
+ area->instructions[i] = NULL;
+
+ g_atomic_pointer_add(&area->count, -1);
+
+ }
+
+ }
+
+ g_mutex_unlock(&area->mutex);
+
+}
+
+
+/******************************************************************************
+* *
* Paramètres : area = aire représentant à contenu à parcourir. *
* offset = point de départ au sein de l'aire en question. *
* pos = tête de lecture dans l'espace global. *
@@ -705,7 +792,7 @@ void load_code_from_mem_area(mem_area *area, mem_area *list, size_t count, GProc
/* Progression dans les traitements */
- done = mark_range_in_mem_area_as_processed(area, instr, false);
+ done = mark_range_in_mem_area_as_processed(area, instr, false, false);
if (!done)
{
@@ -807,7 +894,7 @@ static void load_data_from_mem_area(mem_area *area, GProcContext *ctx, const vmp
/* Progression dans les traitements */
- done = mark_range_in_mem_area_as_processed(area, instr, false);
+ done = mark_range_in_mem_area_as_processed(area, instr, false, false);
if (!done)
{
@@ -1035,10 +1122,10 @@ static void insert_extra_instr_into_mem_areas(mem_area *areas, size_t count, GAr
/* Inscription d'une instruction (sans retour arrière possible :/ ) */
#ifndef NDEBUG
- status = mark_range_in_mem_area_as_processed(area, instr, true);
+ status = mark_range_in_mem_area_as_processed(area, instr, true, false);
assert(status);
#else
- mark_range_in_mem_area_as_processed(area, instr, true);
+ mark_range_in_mem_area_as_processed(area, instr, true, false);
#endif
return;
@@ -1577,6 +1664,11 @@ static void g_area_collector_do_insert(GAreaCollector *collector, GtkStatusStack
#ifndef NDEBUG
bool inserted; /* Validation d'une insertion */
#endif
+ vmpa2t iend; /* Position finale nominale */
+ vmpa2t pos; /* Tête de lecture */
+ phys_t remaining; /* Zone supplémentaire couverte*/
+ phys_t available; /* Zone disponible */
+ phys_t mark_len; /* Zone à marquer comme traitée*/
area = NULL;
@@ -1598,12 +1690,51 @@ static void g_area_collector_do_insert(GAreaCollector *collector, GtkStatusStack
assert(area != NULL);
#ifndef NDEBUG
- inserted = mark_range_in_mem_area_as_processed(area, instr, false);
+ inserted = mark_range_in_mem_area_as_processed(area, instr, false, true);
assert(inserted);
#else
- mark_range_in_mem_area_as_processed(area, instr, false);
+ mark_range_in_mem_area_as_processed(area, instr, false, true);
#endif
+ /**
+ * Si l'instruction n'a pas pu être accueillie dans son intégralité, on
+ * ne rassemble pas les zones pour autant.
+ *
+ * On pourrait imaginer sauver la situation pour les segments débordant
+ * sur une section, mais le problème resterait posé pour des zones
+ * définies par l'utilisateur au sein d'une section.
+ *
+ * On note donc la place occupée par le débordement éventuel ici.
+ */
+
+ compute_mrange_end_addr(g_arch_instruction_get_range(instr), &iend);
+ compute_mrange_end_addr(&area->range, &pos);
+
+ if (cmp_vmpa(&iend, &pos) > 0)
+ {
+ remaining = compute_vmpa_diff(&pos, &iend);
+
+ while (remaining > 0)
+ {
+ area = find_memory_area_by_addr(collector->areas, collector->available, &pos);
+ assert(area != NULL);
+
+ available = get_mrange_length(&area->range);
+
+ if (remaining > available)
+ mark_len = available;
+ else
+ mark_len = remaining;
+
+ mark_range_in_mem_area_as_overlapping(area, mark_len);
+
+ advance_vmpa(&pos, mark_len);
+ remaining -= mark_len;
+
+ }
+
+ }
+
gtk_status_stack_update_activity_value(status, collector->id, 1);
}
diff --git a/tests/format/elf/Makefile b/tests/format/elf/Makefile
index c32392f..8695bb1 100644
--- a/tests/format/elf/Makefile
+++ b/tests/format/elf/Makefile
@@ -1,11 +1,14 @@
-EXECUTABLES=oob_section_name
+EXECUTABLES=oob_section_name overlapping_areas
all: $(EXECUTABLES)
oob_section_name: oob_section_name.o
$(ARM_CROSS)objcopy $< -O binary $@
+overlapping_areas: overlapping_areas.o
+ $(ARM_CROSS)objcopy $< -O binary $@
+
%.o: %.asm
$(ARM_CROSS)as -c $< -o $@
diff --git a/tests/format/elf/overlapping_areas.asm b/tests/format/elf/overlapping_areas.asm
new file mode 100644
index 0000000..debcca1
--- /dev/null
+++ b/tests/format/elf/overlapping_areas.asm
@@ -0,0 +1,115 @@
+
+.macro bump addr
+ .word \addr + 0x200000
+.endm
+
+.macro label_offset lbl
+ .word \lbl - str_table
+.endm
+
+
+elf_header:
+
+ .byte 0x7F, 'E', 'L', 'F' @ e_ident
+ .byte 1 @ EI_CLASS => ELFCLASS32
+ .byte 1 @ EI_DATA => ELFDATA2LSB
+ .byte 1 @ EI_VERSION => EV_CURRENT
+ .byte 0 @ EI_OSABI => ELFOSABI_SYSV
+ .byte 0 @ EI_ABIVERSION
+
+ .word 0
+ .short 0
+ .byte 0
+
+ .short 2 @ e_type => ET_EXEC
+ .short 40 @ e_machine => EM_ARM
+ .word 1 @ e_version => EV_CURRENT
+ bump main @ e_entry
+
+ .word program_headers @ e_phoff
+ .word section_headers @ e_shoff
+
+ .word 0x80 @ e_flags => EF_ARM_NEW_ABI
+
+ .short 52 @ e_ehsize
+ .short 32 @ e_phentsize
+ .short 2 @ e_phnum
+ .short 40 @ e_shentsize
+ .short 2 @ e_shnum
+ .short 1 @ e_shstrndx
+
+
+program_headers:
+
+ .word 1 @ p_type => PT_LOAD
+ .word O @ p_offset
+ .word 0x200000 @ p_vaddr
+ .word 0x200000 @ p_paddr
+ .word bss_start @ p_filesz
+ .word bss_start @ p_memsz
+ .word 0x5 @ p_flags => PF_X | PF_R
+ .word 0x1000 @ p_align
+
+ .word 1 @ p_type => PT_LOAD
+ .word bss_start @ p_offset
+ .word 0x300000 @ p_vaddr
+ .word 0x300000 @ p_paddr
+ .word bss_end - bss_start @ p_filesz
+ .word bss_end - bss_start @ p_memsz
+ .word 0x6 @ p_flags => PF_W | PF_R
+ .word 0x1 @ p_align
+
+
+section_headers:
+
+ label_offset text_lbl @ sh_name
+ .word 1 @ sh_type => SHT_PROGBITS
+ .word 0x6 @ sh_flags => SHF_ALLOC | SHF_EXECINSTR
+ bump main @ sh_addr
+ .word main @ sh_offset
+ .word main_return - main @ sh_size
+ .word 0 @ sh_link
+ .word 0 @ sh_info
+ .word 4 @ sh_addralign
+ .word 0 @ sh_entsize
+
+ label_offset strtab_lbl @ sh_name
+ .word 3 @ sh_type => SHT_STRTAB
+ .word 0x0 @ sh_flags
+ .word 0x0 @ sh_addr
+ .word str_table @ sh_offset
+ .word str_table_end - str_table @ sh_size
+ .word 0 @ sh_link
+ .word 0 @ sh_info
+ .word 1 @ sh_addralign
+ .word 0 @ sh_entsize
+
+
+main:
+ mov r7, #1 @ __NR_exit
+ mov r0, #42 @ $?
+ svc 0
+
+main_return:
+
+
+bss_start:
+
+ .word 0x0
+ .word 0x0
+ .word 0x0
+ .word 0x0
+
+str_table:
+
+ .byte 0, 0
+text_lbl:
+ .byte '.', 't', 'e', 'x', 't', 0
+strtab_lbl:
+ .byte '.', 's', 't', 'r', 't', 'a', 'b', 0
+blabla:
+ .byte 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'
+bss_end:
+ .byte 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', 0
+
+str_table_end:
diff --git a/tests/format/elf/overlapping_areas.py b/tests/format/elf/overlapping_areas.py
new file mode 100644
index 0000000..4c78625
--- /dev/null
+++ b/tests/format/elf/overlapping_areas.py
@@ -0,0 +1,62 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+
+# Il arrive que les segments englobent partiellement des sections.
+#
+# Cela peut être problématique si une section contient une chaîne de taille
+# n qui se retrouve à cheval sur deux zones (la section des chaînes découpée
+# en deux par exemple).
+#
+# Au moment d'associer l'instruction chargée à la zone de départ, cette
+# dernière n'est pas assez grande car elle ne représente pas la section
+# en entier.
+
+
+from chrysacase import ChrysalideTestCase
+from pychrysalide.analysis import LoadedBinary
+from pychrysalide.analysis.contents import FileContent
+from threading import Event
+import os
+import sys
+
+
+class TestOverlappingAreas(ChrysalideTestCase):
+ """TestCase for BSS segment overlapping string section."""
+
+ @classmethod
+ def setUpClass(cls):
+
+ super(TestOverlappingAreas, cls).setUpClass()
+
+ cls.log('Compile binary "overlapping_areas" if needed...')
+
+ fullname = sys.modules[cls.__module__].__file__
+ dirpath = os.path.dirname(fullname)
+
+ os.system('make -C %s overlapping_areas 2>&1 > /dev/null' % dirpath)
+
+
+ def testOOBSectionName(self):
+ """Avoid crashing because of overlapping binary areas."""
+
+ fullname = sys.modules[self.__class__.__module__].__file__
+ filename = os.path.basename(fullname)
+
+ baselen = len(fullname) - len(filename)
+
+ cnt = FileContent(fullname[:baselen] + 'overlapping_areas')
+ self.assertIsNotNone(cnt)
+
+ binary = LoadedBinary(cnt)
+
+ def disass_done(binary):
+ worker.set()
+
+ binary.connect('disassembly-done', disass_done)
+
+ worker = Event()
+
+ binary.analyse()
+
+ worker.wait()