Author Topic: Using radare2 for analysis  (Read 283 times)

t3r4n

  • New to the forum
  • *
  • Posts: 48
Using radare2 for analysis
« on: June 22, 2018, 08:28:54 PM »
Hi everyone,
I did this post the originally over at the QEMU thread as most of the stuff in here will be using the QEMU gdbserver as the host system. a1ex suggested I open a new sticky thread here (@a1ex I think for the sticky part one needs to be a bit higher in hierarchy  ;) )
I got some PNs here regarding the use of radare2 for debugging. I thought I might share some of the things I use and some ideas I have for it on the qemu side and maybe someone joins in on it. 
So first of all why radare2 its not the standard used by the other guys here. Well frankly IDA pro with ARM debugging cost more than a Camera which has features of MagicLantern available and on gdb was never my thing. I like that r2 is free and there are lots of articles on reversing with it. It can do ARM and ARM Thumb and has scripting (python, java ruby ...) interface. It has even an build in emulation which is quite capable of running emulating stuff without qemu.
So lets start.
First you can connect radare2 to QEMU like you would with gdb (-s -S or in the nc qemu.monitor and gdbserver)
The start r2 with the following:
Code: [Select]
r2 -i load_db.r2 -d gdb://localhost:1234 so what is load_db.r2? It is a file giving lots of commands for a good setup here are some of the things I put in it:
Code: [Select]
## Pretty stuff
# Solarized theme
eco solarized
# Use UTF-8 to show cool arrows
e scr.utf8 = true
e scr.utf8.curvy=true
# Show comments at right of disassembly if they fit in screen
e asm.cmtright=true
## Processor stuff
# set arch and cpu type
e io.va = true
e asm.arch = arm
e asm.bits = 16
e asm.cpu=cortex
# anal.armthumb (aae computes arm/thumb changes (lot of false positives ahead))
e anal.armthumb=true
# Shows pseudocode in disassembly. Eg mov eax, str.ok = > eax = str.ok
e asm.pseudo = true
# (Show ESIL instead of mnemonic)
# e asm.esil = true
# Selected: asm.describe (Show opcode description)
e asm.describe = false
#asm.emu (Run ESIL emulation analysis on disasm)
e asm.emu = true
e asm.section.sub = true
e io.va=true
that was quite generic and the comments should tell ya whats happening. The following is camera specific it sets up memory regions and gives names to these regions.
Code: [Select]
S 0x00000000 0x00000000 0x00003fff tcmcode mrwx #00000000 - 00003FFF: eos.tcm_code
S 0x00004000 0x00004000 0x1FFFC000 eosram mrw- #00004000 - 1FFFFFFF: eos.ram
S 0x40000000 0x40000000 0x00004000 eosramuncached0 mrw- #40000000 - 40003FFF: eos.ram_uncached0
S 0x40004000 0x40004000 0x1FFFC000 eosramuncached mrw- #40004000 - 5FFFFFFF: eos.ram_uncached
S 0x80000000 0x80000000 0x00010000 tcmram mrw- #80000000 - 8000FFFF: eos.tcm_data
S 0xBFE00000 0xBFE00000 0x00200000 eosramextra mrw- #BFE00000 - BFFFFFFF: eos.ram_extra
S 0xc0000000 0xc0000000 0x20000000 eosiomem mrw- #C0000000 - DFFFFFFF: eos.iomem
S 0xfc000000 0xfc000000 0x20000000 eosrom1 mr-x #FC000000 - FDFFFFFF: eos.rom1
S 0xfe000000 0xfe000000 0x20000000 eosrom1m mr-x#FE000000 - FFFFFFFF: eos.rom1_mirror
the next lines will setup analysis and define some flags in memory taken from debugmsgs.gdb. r2 uses flags for everything and if I understand documentation right functions follow a fcn.<name> scheme. I have so far not been able to use the afn command to create functions but more later. It would be possible to define these flags as breakpoints and put a modified version of a1ex script for indentifiying functions here. Speaking of which.
I used the script of a1ex to create as described above in this thread an .idc file.
Here I modified the header as follows:
Code: [Select]
#include "stubshelper.h"

int  main(void)
{
  MakeAutoNamedFunc(0xFE0FD5C9, "LoadScript");

and another file stubhelper.h 
Code: [Select]
#include <stdio.h>


void MakeAutoNamedFunc(unsigned int ea ,char name[])
{
  printf("af @ 0x%08X\n",ea);
  printf("afn 0x%08X %s\n",ea,name);
}

void NSTUB(unsigned int ea ,char name[])
{
  printf("af @  0x%08X\n",ea);
  printf("afn 0x%08X %s\n",ea,name);
}
compile and pipe the output to your load_db.r2
Loading can Take a while now
Code: [Select]
s <name>
VV
to inspect a function.


Ideas:
- Use the strings file generate from disassemble.pl to define Strings in the same way as the functions.
- Radare provides a scripting interface. Use python script to search through memory for e.g. Frambuffer
- define Names for IO areas to have them marked in the assembly
- is it possible to use the signaturez function of radare to help speedup new firmware ports or new ports.
- as a1ex suggest generating the memory map from qemu
- write my own hook into register function to create the functions.
- use projects to store the reverse engineered data
- ...

Questions:
- anyone got a better idea on how to define functions
- functions from the qemu idc file sometimes seem to be "one off"

Further reading:
- i found this Video of a talk very helpful where the inventor of radare describes how to use it to reverse an ARM based radio: http://radare.org/r/talks.html the talk in 2017. I have not yet looked into the possibility of emulation of io devices via the scripted breakpoints ...

t3r4n

  • New to the forum
  • *
  • Posts: 48
Re: Using radare2 for analysis
« Reply #1 on: July 02, 2018, 09:00:35 PM »
So a task proposed by @a1ex was to make qemu export function calls directly in r2 format.
Here is what I came up with. To be honest I just poked the code and it magically worked right away. (I'm still trying to shut my mouth on the techniques used in qemu_log.c to produce the input parameter including command line doc )
Here's what I did. In log.h add a line (around line 85):
Code: [Select]
#define EOS_LOG_R2         (1LL << 59)      /* export unique calls to radare2 */
in qemu_log.c below EOS_LOG_IDC:
Code: [Select]
   
{ EOS_LOG_R2  | EOS_LOG_CALLSTACK | CPU_LOG_TB_NOCHAIN, "r2",
        "EOS: export called functions to radare2 (implies callstack,nochain,singlestep)" },
and finally in logging.c
Code: [Select]
static FILE * r2 = NULL;

/* QEMU is usually closed with CTRL-C, so call this when finished */
static void close_r2(void)
{
    fprintf(r2, "\n");
    fclose(r2);
    fprintf(stderr, "%s saved.\n", idc_path);
}

static void eos_r2_log_call(EOSState *s, CPUState *cpu, CPUARMState *env,
    TranslationBlock *tb, uint32_t prev_pc, uint32_t prev_lr, uint32_t prev_size)
{
    static int stderr_dup = 0;

    if (!r2)
    {
        snprintf(idc_path, sizeof(idc_path), "%s.r2", MACHINE_GET_CLASS(current_machine)->name);
        fprintf(stderr, "Exporting called functions to %s.\n", idc_path);
        r2 = fopen(idc_path, "w");
        assert(r2);

        atexit(close_r2);

        fprintf(r2, "# List of functions called during execution. */");
        fprintf(r2, "# Generated from QEMU. \n\n");

        stderr_dup = dup(fileno(stderr));
    }

    /* bit array for every possible PC & ~3 */
    static uint32_t saved_pcs[(1 << 30) / 32] = {0};

    uint32_t pc = env->regs[15];
    uint32_t lr = env->regs[14];
    uint32_t sp = env->regs[13];

    /* log each called function to IDC, only once */
    int pca = pc >> 2;
    if (!(saved_pcs[pca/32] & (1 << (pca%32))))
    {
        saved_pcs[pca/32] |= (1 << pca%32);

        /* log_target_disas writes to stderr; redirect it to our output file */
        /* todo: any other threads that might output to stderr? */
        assert(stderr_dup);
        fflush(stderr); fflush(r2);
        dup2(fileno(r2), fileno(stderr));
        fprintf(stderr, "  /* from "); log_target_disas(cpu, prev_pc, prev_size, 0);
        fprintf(stderr, "   *   -> "); log_target_disas(cpu, tb->pc, tb->size, 0);
        char * task_name = eos_get_current_task_name(s);
        fprintf(stderr, "   * %s%sPC:%x->%x LR:%x->%x SP:%x */\n",
            task_name ? task_name : "", task_name ? " " : "",
            prev_pc, pc, prev_lr, lr, sp
        );
        fprintf(stderr, "  s 0x%X \n", pc);
        env->thumb == 1? fprintf(stderr, "  afB 16\n af @ 0x%X\n", pc):fprintf(stderr, "  af @ 0x%X\n", pc);
        //TODO find solution for name   printf("afn 0x%08X %s\n",ea,name);
        fprintf(stderr, "\n");
        dup2(stderr_dup, fileno(stderr));
    }
}

Maybe I find some time and integrate the function  with the original idc function and don't duplicate code.
And then use the script from a1ex to create the "afn" commands.