worawit / blutter

Flutter Mobile Application Reverse Engineering Tool
MIT License
752 stars 128 forks source link

Lost parts? #40

Open mragonias opened 5 months ago

mragonias commented 5 months ago

Hi, It works very well with blutter, but sometimes I find that information has been lost. For example in this code fragment (dart 3.1.3). As you can see in blutter, the conditional part does not appear (in bold) or perhaps it becomes very complicated to follow. (in the pp file, there is no result for showlock either)

Original

class LockedFeature extends StatelessWidget {
  const LockedFeature(
      {required this.child, this.actionAfter, this.showLock = false, Key? key})
      : super(key: key);
  final Widget child;
  final Function? actionAfter;
  final bool showLock;

  override
  Widget build(BuildContext context) {
    Widget child = IgnorePointer(child: this.child);
    **if (showLock)
      child = Stack(
        alignment: Alignment.center,
        children: [
          IgnorePointer(child: this.child),
          Icon(appStateSettings["outlinedIcons"]
              ? Icons.lock_outlined
              : Icons.lock_rounded),
        ],
      );**
    return Tappable(
      onTap: () async {
        bool result = await premiumPopupPushRoute(context);
        if (actionAfter != null && result == true) actionAfter!();
      },
      borderRadius: 20,
      color: Colors.transparent,
      child: child,
    );
  }
}

Blutter

class LockedFeature extends StatelessWidget {

  _ build(/* No info */) {
    // ** addr: 0xa97b08, size: 0xbc
    // 0xa97b08: EnterFrame
    //     0xa97b08: stp             fp, lr, [SP, #-0x10]!
    //     0xa97b0c: mov             fp, SP
    // 0xa97b10: AllocStack(0x40)
    //     0xa97b10: sub             SP, SP, #0x40
    // 0xa97b14: CheckStackOverflow
    //     0xa97b14: ldr             x16, [THR, #0x38]  ; THR::stack_limit
    //     0xa97b18: cmp             SP, x16
    //     0xa97b1c: b.ls            #0xa97bbc
    // 0xa97b20: r1 = 2
    //     0xa97b20: movz            x1, #0x2
    // 0xa97b24: r0 = AllocateContext()
    //     0xa97b24: bl              #0xd60fcc  ; AllocateContextStub
    // 0xa97b28: mov             x1, x0
    // 0xa97b2c: ldr             x0, [fp, #0x18]
    // 0xa97b30: stur            x1, [fp, #-0x10]
    // 0xa97b34: StoreField: r1->field_f = r0
    //     0xa97b34: stur            w0, [x1, #0xf]
    // 0xa97b38: ldr             x2, [fp, #0x10]
    // 0xa97b3c: StoreField: r1->field_13 = r2
    //     0xa97b3c: stur            w2, [x1, #0x13]
    // 0xa97b40: LoadField: r2 = r0->field_b
    //     0xa97b40: ldur            w2, [x0, #0xb]
    // 0xa97b44: DecompressPointer r2
    //     0xa97b44: add             x2, x2, HEAP, lsl #32
    // 0xa97b48: stur            x2, [fp, #-8]
    // 0xa97b4c: r0 = IgnorePointer()
    //     0xa97b4c: bl              #0x7fab60  ; AllocateIgnorePointerStub -> IgnorePointer (size=0x18)
    // 0xa97b50: mov             x1, x0
    // 0xa97b54: r0 = true
    //     0xa97b54: add             x0, NULL, #0x20  ; true
    // 0xa97b58: stur            x1, [fp, #-0x18]
    // 0xa97b5c: StoreField: r1->field_f = r0
    //     0xa97b5c: stur            w0, [x1, #0xf]
    // 0xa97b60: ldur            x0, [fp, #-8]
    // 0xa97b64: StoreField: r1->field_b = r0
    //     0xa97b64: stur            w0, [x1, #0xb]
    // 0xa97b68: r0 = Tappable()
    //     0xa97b68: bl              #0x7fab24  ; AllocateTappableStub -> Tappable (size=0x34)
    // 0xa97b6c: ldur            x2, [fp, #-0x10]
    // 0xa97b70: r1 = Function '<anonymous closure>':.
    //     0xa97b70: add             x1, PP, #0x4e, lsl #12  ; [pp+0x4e418] AnonymousClosure: (0xa97bc4), in [package:budget/pages/premiumPage.dart] LockedFeature::build (0xa97b08)
    //     0xa97b74: ldr             x1, [x1, #0x418]
    // 0xa97b78: stur            x0, [fp, #-8]
    // 0xa97b7c: r0 = AllocateClosure()
    //     0xa97b7c: bl              #0xd610e4  ; AllocateClosureStub
    // 0xa97b80: ldur            x16, [fp, #-8]
    // 0xa97b84: ldur            lr, [fp, #-0x18]
    // 0xa97b88: stp             lr, x16, [SP, #0x18]
    // 0xa97b8c: r16 = 20.000000
    //     0xa97b8c: add             x16, PP, #0x19, lsl #12  ; [pp+0x19de0] 20
    //     0xa97b90: ldr             x16, [x16, #0xde0]
    // 0xa97b94: stp             x16, x0, [SP, #8]
    // 0xa97b98: r16 = Instance_Color
    //     0xa97b98: ldr             x16, [PP, #0x7950]  ; [pp+0x7950] Obj!Color@d456c1
    // 0xa97b9c: str             x16, [SP]
    // 0xa97ba0: r4 = const [0, 0x5, 0x5, 0x2, borderRadius, 0x3, color, 0x4, onTap, 0x2, null]
    //     0xa97ba0: add             x4, PP, #0x2e, lsl #12  ; [pp+0x2e6f0] List(11) [0, 0x5, 0x5, 0x2, "borderRadius", 0x3, "color", 0x4, "onTap", 0x2, Null]
    //     0xa97ba4: ldr             x4, [x4, #0x6f0]
    // 0xa97ba8: r0 = Tappable()
    //     0xa97ba8: bl              #0x7fa748  ; [package:budget/widgets/tappable.dart] Tappable::Tappable
    // 0xa97bac: ldur            x0, [fp, #-8]
    // 0xa97bb0: LeaveFrame
    //     0xa97bb0: mov             SP, fp
    //     0xa97bb4: ldp             fp, lr, [SP], #0x10
    // 0xa97bb8: ret
    //     0xa97bb8: ret             
    // 0xa97bbc: r0 = StackOverflowSharedWithoutFPURegs()
    //     0xa97bbc: bl              #0xd61f60  ; StackOverflowSharedWithoutFPURegsStub
    // 0xa97bc0: b               #0xa97b20
  }
  [closure] Future<void> <anonymous closure>(dynamic) async {
    // ** addr: 0xa97bc4, size: 0xa8
    // 0xa97bc4: EnterFrame
    //     0xa97bc4: stp             fp, lr, [SP, #-0x10]!
    //     0xa97bc8: mov             fp, SP
    // 0xa97bcc: AllocStack(0x20)
    //     0xa97bcc: sub             SP, SP, #0x20
    // 0xa97bd0: SetupParameters(LockedFeature this /* r1 */)
    //     0xa97bd0: stur            NULL, [fp, #-8]
    //     0xa97bd4: movz            x0, #0
    //     0xa97bd8: add             x1, fp, w0, sxtw #2
    //     0xa97bdc: ldr             x1, [x1, #0x10]
    //     0xa97be0: ldur            w2, [x1, #0x17]
    //     0xa97be4: add             x2, x2, HEAP, lsl #32
    //     0xa97be8: stur            x2, [fp, #-0x10]
    // 0xa97bec: CheckStackOverflow
    //     0xa97bec: ldr             x16, [THR, #0x38]  ; THR::stack_limit
    //     0xa97bf0: cmp             SP, x16
    //     0xa97bf4: b.ls            #0xa97c64
    // 0xa97bf8: InitAsync() -> Future<void?>
    //     0xa97bf8: ldr             x0, [PP, #0x78]  ; [pp+0x78] TypeArguments: <void?>
    //     0xa97bfc: bl              #0x573038
    // 0xa97c00: ldur            x0, [fp, #-0x10]
    // 0xa97c04: LoadField: r1 = r0->field_13
    //     0xa97c04: ldur            w1, [x0, #0x13]
    // 0xa97c08: DecompressPointer r1
    //     0xa97c08: add             x1, x1, HEAP, lsl #32
    // 0xa97c0c: str             x1, [SP]
    // 0xa97c10: r0 = premiumPopupPushRoute()
    //     0xa97c10: bl              #0x90f4cc  ; [package:budget/pages/premiumPage.dart] ::premiumPopupPushRoute
    // 0xa97c14: mov             x1, x0
    // 0xa97c18: stur            x1, [fp, #-0x18]
    // 0xa97c1c: r0 = Await()
    //     0xa97c1c: bl              #0x572cf0  ; AwaitStub
    // 0xa97c20: r16 = true
    //     0xa97c20: add             x16, NULL, #0x20  ; true
    // 0xa97c24: cmp             w0, w16
    // 0xa97c28: b.ne            #0xa97c5c
    // 0xa97c2c: ldur            x0, [fp, #-0x10]
    // 0xa97c30: LoadField: r1 = r0->field_f
    //     0xa97c30: ldur            w1, [x0, #0xf]
    // 0xa97c34: DecompressPointer r1
    //     0xa97c34: add             x1, x1, HEAP, lsl #32
    // 0xa97c38: LoadField: r0 = r1->field_f
    //     0xa97c38: ldur            w0, [x1, #0xf]
    // 0xa97c3c: DecompressPointer r0
    //     0xa97c3c: add             x0, x0, HEAP, lsl #32
    // 0xa97c40: str             x0, [SP]
    // 0xa97c44: r4 = 0
    //     0xa97c44: movz            x4, #0
    // 0xa97c48: ldr             x0, [SP]
    // 0xa97c4c: r16 = UnlinkedCall_0x5489f8
    //     0xa97c4c: add             x16, PP, #0x4e, lsl #12  ; [pp+0x4e420] UnlinkedCall: 0x5489f8 - SwitchableCallMissStub
    //     0xa97c50: add             x16, x16, #0x420
    // 0xa97c54: ldp             x5, lr, [x16]
    // 0xa97c58: blr             lr
    // 0xa97c5c: r0 = Null
    //     0xa97c5c: mov             x0, NULL
    // 0xa97c60: r0 = ReturnAsyncNotFuture()
    //     0xa97c60: b               #0x57300c  ; ReturnAsyncNotFutureStub
    // 0xa97c64: r0 = StackOverflowSharedWithoutFPURegs()
    //     0xa97c64: bl              #0xd61f60  ; StackOverflowSharedWithoutFPURegsStub
    // 0xa97c68: b               #0xa97bf8
  }
}
worawit commented 5 months ago

Dart compiler can remove dead code. If the compiler detect showLock field is never set to true, the if block will be removed.

Dart compiler can do more interest thing such as removing unused function parameters, replace function parameter with constant if it is called only one time with a constant, inline function.

mragonias commented 5 months ago

Very interesting, thank you very much for the clarification.

mragonias commented 5 months ago

This is not very important, but I think that before this did not appear like this. Now in comments it always appears true even when it should appear false:

0x4dbed8: r0 = false
    //     0x4dbed8: add             x0, NULL, #0x30  ; true
    // 0x4dbedc: StoreField: r3->field_1b = r0
    //     0x4dbedc: stur            w0, [x3, #0x1b]
    // 0x4dbee0: r4 = true
    //     0x4dbee0: add             x4, NULL, #0x20  ; true
worawit commented 5 months ago

Thank for report. Fixed it.

mragonias commented 4 months ago

Thank for report. Fixed it.

Still happens, 0x30 appears as a true

worawit commented 4 months ago

After git pull, you have to run command with --rebuild option to force rebuilding the blutter executable.

By default, the script reuses built executable if it is existed.