dartreverse-engineeringdecompileridaghidra

Hex values doubled in IDA/Ghidra decompiled code


I have the following piece of dart code, which check if the input is "ABCDE" and prints correct if the input matches, and it works fine.

import 'dart:io';
final List<int> flag = [65, 66, 67, 68, 69];
void main() {
  print("\ngive flag: ");
  List<int> input = stdin.readLineSync(encoding: latin1)!.codeUnits;
  if (input.length != flag.length) {
    print("\nnot flag");
    return;
  }
  for (int i = 0; i < flag.length; i++) {
    if (input[i] != flag[i]) {
      print("\nnot flag");
      return;
    }
  }
  StringBuffer newFlag = new StringBuffer();
  input.forEach((e) {
    newFlag.writeCharCode(e);
  });
  print("correct");
}

I compiled the code with dart compile aot-snapshot exmaple.dart and then tried to decompile it with IDA and Ghidra. However, the original decimal values seem to be doubled in the decompiled code.

Ghidra decompiled code:

void flag(void)

{
  long lVar1;
  long extraout_RAX;
  long unaff_R14;
  
  if (&stack0xfffffffffffffff8 < *(undefined **)(ulong *)(unaff_R14 + 0x40) ||
      &stack0xfffffffffffffff8 == *(undefined **)(ulong *)(unaff_R14 + 0x40)) {
    (**(code **)(unaff_R14 + 0x238))();
  }
  lVar1 = Precompiled_Stub__iso_stub_AllocateArrayStub();
  *(undefined8 *)(lVar1 + 0x17) = 0x82;
  *(undefined8 *)(lVar1 + 0x1f) = 0x84;
  *(undefined8 *)(lVar1 + 0x27) = 0x86;
  *(undefined8 *)(lVar1 + 0x2f) = 0x88;
  *(undefined8 *)(lVar1 + 0x37) = 0x8a;
  new__GrowableList._withData();
  *(undefined8 *)(extraout_RAX + 0xf) = 10;
  return;
}

0x82, 0x84, 0x86, 0x88, 0x8a in decimal are 130, 132, 134, 136, 138.
The dec values are double of what was originally in the code (65, 66, 67, 68, 69). I am not sure why this is happening...

My aim is to be able to tell that "ABCDE" is the required input just by the .aot file. Thanks :)


Solution

  • This is smi encoding, which is an implementation detail of how the VM represents object references internally.

    Normally an object reference is a pointer to a heap-allocated object structure.

    However, to save space and time, the Dart VM internally represents small integers ("Smi"s for short) directly as a specially formatted (tagged) reference, instead of them being pointers to heap objects. (In a 64-bit VM, "small" integers are actually all integers that can be stored in 63 bits.)

    The representation of a small integer is precisely the integer shifted left by one, aka. multiplied by two, with a zero in the zeroth bit. This has the advantage that adding or subtracting Smis can be done directly on the tagged values, as long as it doesn't overflow.

    All references to heap objects instead have the zeroth bit set to 1. Accessing members of an object is done by indexing relative to the object reference anyway, so it doesn't cost extra to use an offset of n - 1 instead of n.