libvmcu - Virtual MCU Library

Go To Last Post
14 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

Hello again,

 

Months ago I've started a changelog for the simulator I was working on. Recently I decided to detach the engine from the simulator in order to create a library out of it. I've created a new changelog since the library has nothing to do

with the simulator.

 

libvmcu - Virtual MCU Library

 

libvmcu is a library for static and dynamic analysis of AVR binaries.

 

It can be used to create regression tests for embedded systems or to analyze AVR programs on assembly level. You can build your own tools too.

There is also a Java binding, that supports basic functionality.

 

Features:

 

Cycle accurate realtime Simulation

Cycle accurate realtime simulation of the microcontroller including its peripherals.

 

Decode, Decompose, Disassemble, Analyze

The pipeline offers an interface for each stage: decode, decompose, disassemble and analyze. Stages can either operate on a single opcode or a whole binary.

 

Combine Static and Dynamic Analysis

Perform a static analysis on your binary to receive additional information, for example which SFRs are used by the program. Then, use this information to improve your dynamic analysis.

 

No further dependencies

libvmcu comes with no further dependencies, thus allowing easy setup and easy usage.

 

Examples:

 

       Printing disassembly of an intel hex file

int main(void) {

    /* ignoring checks for this example */
    vmcu_report_t *report = vmcu_analyze_ihex("file.hex");

    for(int32_t i = 0; i < report->progsize; i++)
        printf("%s\n", report->disassembly[i].mnem);

    vmcu_report_dtor(report);
    return EXIT_SUCCESS;
}

 

       Printing jumpers/callers of potential labels

int main(void) {

    /* ignoring checks for this example */
    vmcu_report_t *report = vmcu_analyze_ihex("file.hex");

    for(int32_t i = 0; i < report->nlabels; i++) {

        vmcu_label_t *lx = &report->labels[i];
        printf("Label %d @ 0x%04x called/jumped by:\n", lx->id, lx->addr);

        for(int32_t j = 0; j < lx->ncallers; j++)
            printf("0x%04x\n", lx->caller[j].addr);

        printf("\n\n");
    }

    vmcu_report_dtor(report);
    return EXIT_SUCCESS;
}

 

       Extracting details from opcode

int main(void) {

    vmcu_plain_t p;
    vmcu_disassemble_bytes(0xd8e0, &p);

    const VMCU_IKEY key    = p.key;        // VMCU_LDI

    const uint32_t opcode  = p.opcode;     // 0xe0d8 (big endian)
    const uint16_t addr    = p.addr;       // 0x0000 (undefined)

    const bool dword       = p.dword;      // false
    const bool exec        = p.exec;       // true

    const char *mnemonic   = p.mnem;       // ldi r29, 0x08

    vmcu_operand_t *src    = &p.src;
    vmcu_operand_t *dest   = &p.dest;

    VMCU_OPTYPE src_type   = src.type;     // VMCU_IMM8
    VMCU_OPTYPE dest_type  = dest.type;    // VMCU_REGISTER

    const uint8_t src_val  = src.value;    // 0x08
    const uint8_t dest_val = dest.value;   // (r)29

    return EXIT_SUCCESS;
}

 

       Finding endloops (rjmp -1)

int main(void) {

    /* ignoring checks for this example */
    vmcu_report_t *report = vmcu_analyze_ihex("file.hex");

    for(int32_t i = 0; i < report->progsize; i++) {

        vmcu_plain_t *p = &report->disassembly[i];

        if(p->opcode == 0xcfff) // rjmp -1 (big endian)
            printf("Endless loop at address 0x%04x\n", p->addr);
    }

    vmcu_report_dtor(report);
    return EXIT_SUCCESS;
}

 

       Unittest: Testing interrupt frequency of driver/led/led.hex

#define TESTFILE  "../../driver/led/led.hex"

#define PORTB     0x0025
#define PB5       0x05

#define bit(v, b) ((v & (1 << b)) >> b)

int main(const int argc, const char **argv) {

    uint8_t led;

    /* ignoring checks for this example */
    vmcu_report_t *report = vmcu_analyze_ihex(TESTFILE);
    vmcu_system_t *sys    = vmcu_system_ctor(report);

    do {

        vmcu_system_step(sys);
        led = vmcu_system_read_data(sys, PORTB);

    } while(bit(led, PB5) == 0x00);

    const double f    = 16000000U;
    const double c    = sys->cycles;
    const double time = (c / f);

    assert((0.95 <= time) && (time <= 1.05));
    printf("Time between LED toggle: %lf [s]\n", time);

    vmcu_report_dtor(report);
    vmcu_system_dtor(sys);

    return EXIT_SUCCESS;
}
// [Expected]    0.95 [s] <= t <= 1.05 [s]
// [Test Result] Time between LED toggle: 1.000021 [s]

==> Test passed.

 

Note that, this library is a work in progress, so examples might change a bit.

 

https://github.com/Milo-D/libvmc...

Last Edited: Fri. Mar 5, 2021 - 07:11 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Update (v.0.7.3)

 

VCD Trace tool written with libvmcu by pointbazaar

 

  • libvmcu supports xrefs (cross reference from) (this replaces vmcu_caller_t)
  • bug fix in 'or' assembly instruction.
  • merged VCD trace tool (driver) by pointbazaar. This driver is able to trace memory values while simulating an AVR binary.

 

printing xrefs of potential labels

int main(void) {

    /* ignoring checks for this example */
    vmcu_report_t *report = vmcu_analyze_ihex("file.hex");

    for(int32_t i = 0; i < report->nlabels; i++) {

        vmcu_label_t *lx = &report->labels[i];
        printf("0x%04x\tL%d\n\n", lx->addr, lx->id);

        for(int32_t j = 0; j < lx->nxrefs; j++) {

            vmcu_xref_t *x = &lx->xrefs[j];

            printf(" xref from 0x%04x ", x->p->addr);
            printf("(%s)\n", x->p->mnem);
        }

        printf("\n");
    }

    vmcu_report_dtor(report);
    return EXIT_SUCCESS;
}
Output:

0x0000  L0

 xref from 0x0051 (jmp +0 ; PC <- 0x0)

0x0034  L1

 xref from 0x0000 (jmp +52 ; PC <- 0x34)

0x0040  L2

 xref from 0x0044 (brne -5 ; (Z = 0): PC <- PC - 0x5 + 1)

0x0042  L3

 xref from 0x003f (rjmp +2 ; PC <- PC + 0x2 + 1)

0x0049  L4

 xref from 0x004c (brne -4 ; (Z = 0): PC <- PC - 0x4 + 1)

0x004a  L5

 xref from 0x0048 (rjmp +1 ; PC <- PC + 0x1 + 1)

0x0051  L6

 xref from 0x0002 (jmp +81 ; PC <- 0x51)
 xref from 0x0004 (jmp +81 ; PC <- 0x51)
 xref from 0x0006 (jmp +81 ; PC <- 0x51)
 xref from 0x0008 (jmp +81 ; PC <- 0x51)
 xref from 0x000a (jmp +81 ; PC <- 0x51)
 xref from 0x000c (jmp +81 ; PC <- 0x51)
 xref from 0x000e (jmp +81 ; PC <- 0x51)
 xref from 0x0010 (jmp +81 ; PC <- 0x51)
 xref from 0x0012 (jmp +81 ; PC <- 0x51)
 xref from 0x0014 (jmp +81 ; PC <- 0x51)
 xref from 0x0016 (jmp +81 ; PC <- 0x51)
 xref from 0x0018 (jmp +81 ; PC <- 0x51)
 xref from 0x001a (jmp +81 ; PC <- 0x51)
 xref from 0x001c (jmp +81 ; PC <- 0x51)
 xref from 0x001e (jmp +81 ; PC <- 0x51)
 xref from 0x0020 (jmp +81 ; PC <- 0x51)
 xref from 0x0022 (jmp +81 ; PC <- 0x51)
 xref from 0x0024 (jmp +81 ; PC <- 0x51)
 xref from 0x0026 (jmp +81 ; PC <- 0x51)
 xref from 0x0028 (jmp +81 ; PC <- 0x51)
 xref from 0x002a (jmp +81 ; PC <- 0x51)
 xref from 0x002e (jmp +81 ; PC <- 0x51)
 xref from 0x0030 (jmp +81 ; PC <- 0x51)
 xref from 0x0032 (jmp +81 ; PC <- 0x51)

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
I am currently looking for contributors for the following submodules:

Bindings
------------------

I am planning a binding for python. A python binding would allow us
to examine and simulate AVR binaries (or parts of it) without having to
compile and link a program. It would be a very interactive way of analyzing
and simulating binaries.

Bindings need to be maintained, since the libvcmu interface can change over time.
In general, a binding forms a subsystem of the repository for which
you would be responsible.
If the binding can not be built for a longer time, it is no problem, the binding becomes
archived until someone agrees to resume.
Bindings for other languages ​​are not planned, but I would have
nothing against it, as long as these are also maintained. Currently pointbazaar maintains the Java Binding.
Bindings require in general, because the library interface
can change and you have to adjust the binding, high effort.

Driver
--------------
Drivers, on the other hand, are less expensive in time. If you have little time
but still want to contribute, creating a driver might be the right choice.
A driver is an utility or example program, that fulfills certain functions by using the libvmcu library.
An example of a driver would be the "findisr" driver, which
locates ISRs in an AVR binary by using libvmcu.
There are no precise specifications for the drivers. You can create anything you want as long as
it combines libvmcu functions in order to achieve something new.

I will maintain the drivers afterwards to ensure that they withstand library changes.

Tests
------------

More testing. 
Generally there are so many tests
needed that I won't be able to keep up with it on my own.

Thanks for reading.
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You might want to look at other efforts and coordinate, etc.  Here is the list I have.

 

   + https://github.com/buserror/simavr
   + https://www.nongnu.org/simulavr/
   + https://github.com/ghewgill/emulino/
   + http://www.avr-asm-tutorial.net/avr_sim/index_en.html
   + https://github.com/Milo-D/libvmcu-Virtual-MCU-Library

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
- Improved SFR analyzer module
  - added vmcu_sfr_t to vmcu_report_t
  - added vmcu_xref_t to vmcu_sfr_t
  - removed mnemonic manipulation in sfr analyzer
  - sfr analyzer is now collecting SFRs and storing xrefs

- Minor change in naming convention
  - using singular of structnames instead of plural
  - using prefix n_ for list counts
  - example: lists of structs in report: structname
  - example: list count of structs in report: n_structname

- Bugfix in occurence driver
- COMn(A/B)x bits are now overriding normal port function

 

      Printing SFRs and their xrefs

#define FILE "avr_filesystem/avr_filesystem.hex"

int main(const int argc, const char **argv) {

    /* ignoring checks for this example */
    vmcu_report_t *report = vmcu_analyze_ihex(FILE);

    for(int32_t i = 0; i < report->n_sfr; i++) {

        vmcu_sfr_t *sfr = &report->sfr[i];
        printf("SFR ID: %d\n", sfr->id);

        for(int32_t j = 0; j < sfr->n_xref; j++) {

            vmcu_xref_t *x = &sfr->xref[j];

            printf("  xref from 0x%04x ", x->p->addr);
            printf("(%s)\n", x->p->mnem);
        }

        printf("\n");
    }

    vmcu_report_dtor(report);
    return EXIT_SUCCESS;
}

 

       Output

SFR ID: 0
  xref from 0x0112 (out 0x01, r16 ; IO[addr] <- R16)
  xref from 0x011c (out 0x00, r16 ; IO[addr] <- R16)

SFR ID: 33
  xref from 0x011f (in r16, 0x31 ; R16 <- IO[addr])

SFR ID: 38
  xref from 0x0152 (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x016f (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x018d (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x01a0 (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x01c5 (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x01df (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x01fc (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x020c (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x021c (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x0223 (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x0264 (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x026d (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x0296 (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x029e (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x02e1 (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x02e8 (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x0357 (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x03f4 (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x03fd (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x04c2 (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x04d0 (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x04d8 (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x0a35 (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x0a44 (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x0b65 (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x0b96 (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x0b9d (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x0c34 (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x0c3a (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x0c41 (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x0ca1 (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x0caf (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x0cb6 (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x0d24 (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x0d33 (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x0d8e (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x0d95 (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x12a4 (in r28, 0x3d ; R28 <- IO[addr])
  xref from 0x12ac (out 0x3d, r28 ; IO[addr] <- R28)
  xref from 0x12c6 (out 0x3d, r28 ; IO[addr] <- R28)

SFR ID: 39
  xref from 0x0151 (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x0170 (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x018e (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x01a1 (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x01c6 (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x01e0 (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x01fd (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x020d (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x021d (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x0221 (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x0262 (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x026e (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x0297 (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x029c (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x02df (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x02e9 (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x0358 (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x03f2 (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x03fe (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x04c0 (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x04d1 (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x04d6 (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x0a33 (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x0a45 (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x0b66 (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x0b97 (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x0b9b (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x0c32 (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x0c3b (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x0c3f (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x0c9f (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x0cb0 (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x0cb4 (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x0d22 (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x0d34 (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x0d8f (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x0d93 (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x12a5 (in r29, 0x3e ; R29 <- IO[addr])
  xref from 0x12aa (out 0x3e, r29 ; IO[addr] <- R29)
  xref from 0x12c4 (out 0x3e, r29 ; IO[addr] <- R29)

SFR ID: 40
  xref from 0x014e (out 0x3f, r1 ; IO[addr] <- R1)
  xref from 0x021f (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x0222 (out 0x3f, r0 ; IO[addr] <- R0)
  xref from 0x0260 (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x0263 (out 0x3f, r0 ; IO[addr] <- R0)
  xref from 0x029a (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x029d (out 0x3f, r0 ; IO[addr] <- R0)
  xref from 0x02dd (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x02e0 (out 0x3f, r0 ; IO[addr] <- R0)
  xref from 0x03f0 (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x03f3 (out 0x3f, r0 ; IO[addr] <- R0)
  xref from 0x04be (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x04c1 (out 0x3f, r0 ; IO[addr] <- R0)
  xref from 0x04d4 (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x04d7 (out 0x3f, r0 ; IO[addr] <- R0)
  xref from 0x0a31 (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x0a34 (out 0x3f, r0 ; IO[addr] <- R0)
  xref from 0x0b99 (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x0b9c (out 0x3f, r0 ; IO[addr] <- R0)
  xref from 0x0c30 (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x0c33 (out 0x3f, r0 ; IO[addr] <- R0)
  xref from 0x0c3d (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x0c40 (out 0x3f, r0 ; IO[addr] <- R0)
  xref from 0x0c9d (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x0ca0 (out 0x3f, r0 ; IO[addr] <- R0)
  xref from 0x0cb2 (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x0cb5 (out 0x3f, r0 ; IO[addr] <- R0)
  xref from 0x0d20 (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x0d23 (out 0x3f, r0 ; IO[addr] <- R0)
  xref from 0x0d91 (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x0d94 (out 0x3f, r0 ; IO[addr] <- R0)
  xref from 0x12a8 (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x12ab (out 0x3f, r0 ; IO[addr] <- R0)
  xref from 0x12c2 (in r0, 0x3f  ; R0 <- IO[addr])
  xref from 0x12c5 (out 0x3f, r0 ; IO[addr] <- R0)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Update (v.0.8.0)

 

- Added devices and device models

 

A device model is an abstraction over a microcontroller type. It contains MCU specific

data, like memory sections and layouts.

 

Each implementation of a virtual microcontroller has a device loader which fills the device model with data. The device model is then

used to supply the analyzer with all the relevant data it needs.

 

This feature might be the first step to bring static analysis to many different AVR microcontrollers.

 

Steps to add a new microcontroller to the static analysis:

 

  • create a new device arch/device/new_avr.c
  • create a device loader function called load_device_new_avr within arch/device/new_avr.c
  • fill the device model with MCU specific data within load_device_new_avr
  • connect the device loader to the device model constructor

 

That's it. new_avr should be now available for static analysis.

The device model currently only contains basic information and will be extended whenever new fields are required.

 

- added missing vmcu_ prefix for some functions (no name clashes possible anymore)

 

- added decoder tests (com -> sbic)

 

- some minor bug fixes

 

Device Model Example

int main(const int argc, const char **argv) {

    /* ignoring checks for this example */

    vmcu_model_t  *m328p  = vmcu_model_ctor(VMCU_M328P);       // ATmega328P
    vmcu_report_t *report = vmcu_analyze_ihex(argv[1], m328p);

    for(int32_t i = 0; i < report->progsize; i++) {

        vmcu_plain_t *p = &report->disassembly[i];

        if(p->src.type == VMCU_REGISTER && p->src.value == 16) // filter src = r16
            printf("0x%04x\t%s\n", p->addr, p->mnem);
    }

    vcmu_report_dtor(report);
    vmcu_model_dtor(m328p);

    return EXIT_SUCCESS;
}

First we create a device model for the ATmega328P (vmcu_model_t *m328p). Now this model can be used to disassemble, analyze, etc. for a specific microcontroller, in this example the ATmega328P.

Last Edited: Sun. Mar 14, 2021 - 01:19 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can you elaborate on the use of static and dynamic analyses and what they achieve?  

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MattRW wrote:

Can you elaborate on the use of static and dynamic analyses and what they achieve?  

 

 

Hey Matt,

 

sure.

 

The dynamic analysis can be used to simulate small snippets or to create automated tests for the ATmega328 family. Although some few peripherals are supported, the simulation still lacks support for other peripherals like USART, ADC, etc. Other simulators are definitely better suited, especially since the simulation currently only focuses on the ATmega328 family. So if someone is purely interested in simulating AVRs, I would suggest another tool.

 

The static part takes care of the preparation of raw data, which can then be further processed by other programs. The goal here is to make it possible to interact programmatically with AVR source code.

 

Currently achieving

 

  •  extracting and classifying usage of special function registers
  •  extracting jump/call destinations (potential label position)

 

  • providing disassembly
  • classifying instructions
  • classifying and extracting operands

 

  • providing cross references within disassembly

 

Planned to achieve

 

 

  • subroutine analysis (start/end of a subroutine plus subroutine cross references, ...)
  • cycle analysis of ISRs and subroutines (min. cycles, max. cycles, average cycles)
  • controlflow graph in order to navigate statically through the disassembly
  • dead code analysis
  • dataflow analysis

 

  • instruction groups and subgroups (like arithmetic, controlflow, etc.)
  • instruction addressing modes
  • which flags a certain instruction affects
  • [...]

 

Edit: I must confess, I am interested in analyzing binaries. It might not be that interesting for everybody ;)

Last Edited: Sun. Mar 14, 2021 - 03:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks. Sounds like fun..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Update (v.0.8.1)

 

  • Added: Instruction Groups

    • arithmetic/logic instructions (VMCU_GROUP_MATH_LOGIC)
    • data transfer instructions (VMCU_GROUP_TRANSFER)
    • MCU control instructions (VMCU_GROUP_SYS_CTRL)
    • flow control instructions (VMCU_GROUP_FLOW)
    • bit and bit-test instructions (VMCU_GROUP_BIT)

 

  • rename vmcu_plain_t to vmcu_instr_t

    • this might be a more obvious name than "plain"
  • fixed some bugs

  • added decoder tests (sbis -> sez)

    • added decoder tests (sleep -> st(Y))

 

      instruction groups example (filtering all flow instructions in file.hex)

int main(const int argc, const char **argv) {

    /* ignoring checks for this example */
    vmcu_model_t  *m328p  = vmcu_model_ctor(VMCU_M328P);
    vmcu_report_t *report = vmcu_report_ctor("file.hex", m328p);

    for(int32_t i = 0; i < report->progsize; i++) {

        vmcu_instr_t *instr = &report->disassembly[i];

        if(instr->group != VMCU_GROUP_FLOW)
            continue;

        printf("0x%04x\t", instr->addr);
        printf("%s\n", instr->mnem);
    }

    vmcu_report_dtor(report);
    vmcu_model_dtor(m328p);

    return EXIT_SUCCESS;
}

      output

0x0000  jmp +52                   ; PC <- 0x34
0x0002  jmp +81                   ; PC <- 0x51
0x0004  jmp +81                   ; PC <- 0x51
0x0006  jmp +81                   ; PC <- 0x51
0x0008  jmp +81                   ; PC <- 0x51
0x000a  jmp +81                   ; PC <- 0x51
0x000c  jmp +81                   ; PC <- 0x51
0x000e  jmp +81                   ; PC <- 0x51
0x0010  jmp +81                   ; PC <- 0x51
0x0012  jmp +81                   ; PC <- 0x51
0x0014  jmp +81                   ; PC <- 0x51
0x0016  jmp +81                   ; PC <- 0x51
0x0018  jmp +81                   ; PC <- 0x51
0x001a  jmp +81                   ; PC <- 0x51
0x001c  jmp +81                   ; PC <- 0x51
0x001e  jmp +81                   ; PC <- 0x51
0x0020  jmp +81                   ; PC <- 0x51
0x0022  jmp +81                   ; PC <- 0x51
0x0024  jmp +81                   ; PC <- 0x51
0x0026  jmp +81                   ; PC <- 0x51
0x0028  jmp +81                   ; PC <- 0x51
0x002a  jmp +81                   ; PC <- 0x51
0x002c  jmp +83                   ; PC <- 0x53
0x002e  jmp +81                   ; PC <- 0x51
0x0030  jmp +81                   ; PC <- 0x51
0x0032  jmp +81                   ; PC <- 0x51
0x003f  rjmp +2                   ; PC <- PC + 0x2 + 1
0x0044  brne -5                   ; (Z = 0): PC <- PC - 0x5 + 1
0x0048  rjmp +1                   ; PC <- PC + 0x1 + 1
0x004c  brne -4                   ; (Z = 0): PC <- PC - 0x4 + 1
0x004d  call +130                 ; PC <- 0x82
0x004f  jmp +134                  ; PC <- 0x86
0x0051  jmp +0                    ; PC <- 0x0
0x0071  cpse r24, r1              ; (R24 = R1): PC <- skip
0x0072  rjmp +9                   ; PC <- PC + 0x9 + 1
0x007b  reti                      ; PC <- DATA[SP]
0x0081  rjmp -14                  ; PC <- PC - 0xe + 1
0x0085  rjmp -1                   ; PC <- PC - 0x1 + 1
0x0087  rjmp -1                   ; PC <- PC - 0x1 + 1

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Update (v.0.8.2)

 

  • Added: interrupt vector analysis

    • finding vectors in disassembly at default table position
    • storing vector ID (VMCU_VECT enum), vector address and a xref-to vector's ISR

 

  • Added: vtable section in vmcu_model_t.

    • holds data related to the MCU's vector table

 

  • Created analyzer/util/util.h for frequently (> 1) used subroutines

    • moved get_abs_addr to analyzer/util/util.h:vmcu_get_abs_addr

 

      printing interrupt vectors and their xref-to

int main(const int argc, const char **argv) {

    /* ignoring checks for this example */
    vmcu_model_t  *m328p  = vmcu_model_ctor(VMCU_M328P);
    vmcu_report_t *report = vmcu_analyze_ihex("file.hex", m328p);

    for(int32_t i = 0; i < report->n_vector; i++) {

        vmcu_vector_t *vect = &report->vector[i];
        vmcu_instr_t  *isr  = vect->xto->i;

        printf("Vector ID %d @ 0x%04x\n", vect->id, vect->addr);
        printf(" interrupt service routine at 0x%04x", isr->addr);
        printf("\n\n");
    }

    vmcu_report_dtor(report);
    vmcu_model_dtor(m328p);

    return EXIT_SUCCESS;
}

 

      output

Vector ID 16 @ 0x0020
 interrupt service routine at 0x03f5

Vector ID 17 @ 0x0022
 interrupt service routine at 0x008a

Vector ID 18 @ 0x0024
 interrupt service routine at 0x03c3

Vector ID 19 @ 0x0026
 interrupt service routine at 0x039d
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
    vmcu_model_t  *m328p  = vmcu_model_ctor(VMCU_M328P);
    vmcu_model_dtor(m328p);

This intrigues me - I can see by the names you are thinking of this in a very "C++ like" way. If so is C++ not the obvious choice ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

    vmcu_model_t  *m328p  = vmcu_model_ctor(VMCU_M328P);
    vmcu_model_dtor(m328p);

This intrigues me - I can see by the names you are thinking of this in a very "C++ like" way. If so is C++ not the obvious choice ?

 

Hey clawson,

 

I've actually rewritten this project from C++ to C (one year ago). I like the simplicity and syntax of C. It was a personal choice ^^

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

v.0.8.6

 

- Added submodule for discovering strings in binary

- supporting ASCII encoding, and maybe UTF16 in the future

 

- structure vmcu_string_t represents a string found in the binary

  - holds string start address in flash memory

  - holds string length

  - holds actual bytes of string

 

- Bug fixes

 

Discovering strings in binary

int main(const int argc, const char **argv) {

    /* ignoring checks for this example */
    vmcu_model_t  *m328p  = vmcu_model_ctor(VMCU_DEVICE_M328P);
    vmcu_report_t *report = vmcu_analyze_ihex("file.hex", m328p);

    for(int32_t i = 0; i < report->n_string; i++) {

        vmcu_string_t *str = &report->string[i];

        printf("Found string \"%s", str->bytes);
        printf("\" l = %d", str->length);
        printf(" @ 0x%04x\n", str->addr);
    }

    printf("\nTotal strings found: %d\n", report->n_string);

    vmcu_report_dtor(report);
    vmcu_model_dtor(m328p);

    return EXIT_SUCCESS;
}

 

Output

Found string "Welcome " l = 8 @ 0x092e
Found string "[1] Login\n" l = 11 @ 0x0933
Found string "[2] Memory management\n" l = 23 @ 0x0939
Found string "Please authenticate yourself with your hardware token\n" l = 55 @ 0x0946
Found string "Please insert token. (%d characters)\n" l = 38 @ 0x0962
Found string "Token can only contain the characters [A-Z/a-z/0-9]\n" l = 53 @ 0x0975

Total strings found: 6

 

Note that this example does not handle non printable bytes. You'll have to write your own small function to handle non printable bytes.