From 8ee7fc5db965adaa835ca87bb3d2e2d43e52fbbb Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Thu, 11 May 2017 21:09:07 +0200
Subject: Handled preloaded instructions located in two cut areas (ELF
 overlapping).

---
 ChangeLog                              |  12 +++
 src/analysis/disass/area.c             | 153 ++++++++++++++++++++++++++++++---
 tests/format/elf/Makefile              |   5 +-
 tests/format/elf/overlapping_areas.asm | 115 +++++++++++++++++++++++++
 tests/format/elf/overlapping_areas.py  |  62 +++++++++++++
 5 files changed, 335 insertions(+), 12 deletions(-)
 create mode 100644 tests/format/elf/overlapping_areas.asm
 create mode 100644 tests/format/elf/overlapping_areas.py

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()
-- 
cgit v0.11.2-87-g4458