Feature request: display the corresponding field to GDT access
Opened this issue · 1 comments
In my sample, Blutter outputs a main.dart
which has the following:
// 0x29e54c: r0 = GDT[cid_x0 + 0x2060]()
// 0x29e54c: movz x17, #0x2060
// 0x29e550: add lr, x0, x17
// 0x29e554: ldr lr, [x21, lr, lsl #3]
// 0x29e558: blr lr
Actually cid_x0
is not defined before, but from reading the assembly, it looks this is x0
.
How can I normally know what class field this is accessing?
Which class does cid_x0
represent?
It would be helpful if the assembly output could be more detailed on GDT.
From sample analysis, I know this is SMS body taken from the Telephony package, but it's difficult to work out from assembly...
Full main.dart
:
// lib: , url: package:sms_flutter/main.dart
// class id: 66178, size: 0x8
class :: {
static _ onBackgroundMessage(/* No info */) async {
// ** addr: 0x29e3c8, size: 0x114
// 0x29e3c8: EnterFrame
// 0x29e3c8: stp fp, lr, [SP, #-0x10]!
// 0x29e3cc: mov fp, SP
// 0x29e3d0: AllocStack(0x10)
// 0x29e3d0: sub SP, SP, #0x10
// 0x29e3d4: CheckStackOverflow
// 0x29e3d4: ldr x16, [THR, #0x38] ; THR::stack_limit
// 0x29e3d8: cmp SP, x16
// 0x29e3dc: b.ls #0x29e4d4
// 0x29e3e0: r1 = 7
// 0x29e3e0: movz x1, #0x7
// 0x29e3e4: r0 = AllocateContext()
// 0x29e3e4: bl #0x355798 ; AllocateContextStub
// 0x29e3e8: mov x2, x0
// 0x29e3ec: ldr x0, [fp, #0x10]
// 0x29e3f0: stur x2, [fp, #-8]
// 0x29e3f4: StoreField: r2->field_1b = r0
// 0x29e3f4: stur w0, [x2, #0x1b]
// 0x29e3f8: r1 = Null
// 0x29e3f8: mov x1, NULL
// 0x29e3fc: r0 = _Future()
// 0x29e3fc: bl #0x1886c0 ; Allocate_FutureStub -> _Future<X0> (size=0x1c)
// 0x29e400: mov x1, x0
// 0x29e404: r0 = 0
// 0x29e404: movz x0, #0
// 0x29e408: stur x1, [fp, #-0x10]
// 0x29e40c: StoreField: r1->field_b = r0
// 0x29e40c: stur x0, [x1, #0xb]
// 0x29e410: r0 = InitLateStaticField(0x4bc) // [dart:async] Zone::_current
// 0x29e410: ldr x0, [THR, #0x80] ; THR::field_table_values
// 0x29e414: ldr x0, [x0, #0x978]
// 0x29e418: ldr x16, [PP, #0x28] ; [pp+0x28] Sentinel
// 0x29e41c: cmp w0, w16
// 0x29e420: b.ne #0x29e42c
// 0x29e424: ldr x2, [PP, #0x88] ; [pp+0x88] Field <Zone._current@4048458>: static late (offset: 0x4bc)
// 0x29e428: bl #0x354cb8
// 0x29e42c: mov x1, x0
// 0x29e430: ldur x0, [fp, #-0x10]
// 0x29e434: StoreField: r0->field_13 = r1
// 0x29e434: stur w1, [x0, #0x13]
// 0x29e438: ldur x3, [fp, #-8]
// 0x29e43c: StoreField: r3->field_13 = r0
// 0x29e43c: stur w0, [x3, #0x13]
// 0x29e440: r17 = false
// 0x29e440: add x17, NULL, #0x30 ; false
// 0x29e444: StoreField: r3->field_17 = r17
// 0x29e444: stur w17, [x3, #0x17]
// 0x29e448: StoreField: r3->field_1f = rNULL
// 0x29e448: stur NULL, [x3, #0x1f]
// 0x29e44c: r17 = 0
// 0x29e44c: movz x17, #0
// 0x29e450: StoreField: r3->field_f = r17
// 0x29e450: stur w17, [x3, #0xf]
// 0x29e454: StoreField: r3->field_23 = rNULL
// 0x29e454: stur NULL, [x3, #0x23]
// 0x29e458: mov x2, x3
// 0x29e45c: r1 = Function ':async_op': static.
// 0x29e45c: add x1, PP, #8, lsl #12 ; [pp+0x8ed0] AnonymousClosure: static (0x29e4dc), in [package:sms_flutter/main.dart] ::onBackgroundMessage (0x29e3c8)
// 0x29e460: ldr x1, [x1, #0xed0]
// 0x29e464: r0 = AllocateClosure()
// 0x29e464: bl #0x3558a4 ; AllocateClosureStub
// 0x29e468: mov x1, x0
// 0x29e46c: ldur x0, [fp, #-8]
// 0x29e470: StoreField: r0->field_27 = r1
// 0x29e470: stur w1, [x0, #0x27]
// 0x29e474: SaveReg r1
// 0x29e474: str x1, [SP, #-8]!
// 0x29e478: r0 = _asyncThenWrapperHelper()
// 0x29e478: bl #0x175240 ; [dart:async] ::_asyncThenWrapperHelper
// 0x29e47c: add SP, SP, #8
// 0x29e480: ldur x0, [fp, #-8]
// 0x29e484: LoadField: r1 = r0->field_27
// 0x29e484: ldur w1, [x0, #0x27]
// 0x29e488: DecompressPointer r1
// 0x29e488: add x1, x1, HEAP, lsl #32
// 0x29e48c: SaveReg r1
// 0x29e48c: str x1, [SP, #-8]!
// 0x29e490: r0 = _asyncErrorWrapperHelper()
// 0x29e490: bl #0x175380 ; [dart:async] ::_asyncErrorWrapperHelper
// 0x29e494: add SP, SP, #8
// 0x29e498: ldur x1, [fp, #-8]
// 0x29e49c: LoadField: r0 = r1->field_27
// 0x29e49c: ldur w0, [x1, #0x27]
// 0x29e4a0: DecompressPointer r0
// 0x29e4a0: add x0, x0, HEAP, lsl #32
// 0x29e4a4: SaveReg r0
// 0x29e4a4: str x0, [SP, #-8]!
// 0x29e4a8: ClosureCall
// 0x29e4a8: ldr x4, [PP, #0x68] ; [pp+0x68] List(5) [0, 0x1, 0x1, 0x1, Null]
// 0x29e4ac: ldur x2, [x0, #0x1f]
// 0x29e4b0: blr x2
// 0x29e4b4: add SP, SP, #8
// 0x29e4b8: ldur x1, [fp, #-8]
// 0x29e4bc: r17 = true
// 0x29e4bc: add x17, NULL, #0x20 ; true
// 0x29e4c0: StoreField: r1->field_17 = r17
// 0x29e4c0: stur w17, [x1, #0x17]
// 0x29e4c4: ldur x0, [fp, #-0x10]
// 0x29e4c8: LeaveFrame
// 0x29e4c8: mov SP, fp
// 0x29e4cc: ldp fp, lr, [SP], #0x10
// 0x29e4d0: ret
// 0x29e4d0: ret
// 0x29e4d4: r0 = StackOverflowSharedWithoutFPURegs()
// 0x29e4d4: bl #0x35684c ; StackOverflowSharedWithoutFPURegsStub
// 0x29e4d8: b #0x29e3e0
}
static void main() {
// ** addr: 0x2ab64c, size: 0x30
// 0x2ab64c: EnterFrame
// 0x2ab64c: stp fp, lr, [SP, #-0x10]!
// 0x2ab650: mov fp, SP
// 0x2ab654: CheckStackOverflow
// 0x2ab654: ldr x16, [THR, #0x38] ; THR::stack_limit
// 0x2ab658: cmp SP, x16
// 0x2ab65c: b.ls #0x2ab674
// 0x2ab660: r0 = runApp()
// 0x2ab660: bl #0x26d48c ; [package:flutter/src/widgets/binding.dart] ::runApp
// 0x2ab664: r0 = Null
// 0x2ab664: mov x0, NULL
// 0x2ab668: LeaveFrame
// 0x2ab668: mov SP, fp
// 0x2ab66c: ldp fp, lr, [SP], #0x10
// 0x2ab670: ret
// 0x2ab670: ret
// 0x2ab674: r0 = StackOverflowSharedWithoutFPURegs()
// 0x2ab674: bl #0x35684c ; StackOverflowSharedWithoutFPURegsStub
// 0x2ab678: b #0x2ab660
}
[closure] static dynamic onBackgroundMessage(dynamic, SmsMessage) {
// ** addr: 0x29e390, size: 0x38
// 0x29e390: EnterFrame
// 0x29e390: stp fp, lr, [SP, #-0x10]!
// 0x29e394: mov fp, SP
// 0x29e398: CheckStackOverflow
// 0x29e398: ldr x16, [THR, #0x38] ; THR::stack_limit
// 0x29e39c: cmp SP, x16
// 0x29e3a0: b.ls #0x29e3c0
// 0x29e3a4: ldr x16, [fp, #0x10]
// 0x29e3a8: SaveReg r16
// 0x29e3a8: str x16, [SP, #-8]!
// 0x29e3ac: r0 = onBackgroundMessage()
// 0x29e3ac: bl #0x29e3c8 ; [package:sms_flutter/main.dart] ::onBackgroundMessage
// 0x29e3b0: add SP, SP, #8
// 0x29e3b4: LeaveFrame
// 0x29e3b4: mov SP, fp
// 0x29e3b8: ldp fp, lr, [SP], #0x10
// 0x29e3bc: ret
// 0x29e3bc: ret
// 0x29e3c0: r0 = StackOverflowSharedWithoutFPURegs()
// 0x29e3c0: bl #0x35684c ; StackOverflowSharedWithoutFPURegsStub
// 0x29e3c4: b #0x29e3a4
}
[closure] static dynamic async_op(dynamic, [dynamic, dynamic, dynamic]) {
// ** addr: 0x29e4dc, size: 0x17c
// 0x29e4dc: EnterFrame
// 0x29e4dc: stp fp, lr, [SP, #-0x10]!
// 0x29e4e0: mov fp, SP
// 0x29e4e4: AllocStack(0x70)
// 0x29e4e4: sub SP, SP, #0x70
// 0x29e4e8: mov x0, x4
// 0x29e4ec: LoadField: r1 = r0->field_13
// 0x29e4ec: ldur w1, [x0, #0x13]
// 0x29e4f0: DecompressPointer r1
// 0x29e4f0: add x1, x1, HEAP, lsl #32
// 0x29e4f4: sub x0, x1, #2
// 0x29e4f8: add x1, fp, w0, sxtw #2
// 0x29e4fc: ldr x1, [x1, #0x10]
// 0x29e500: cmp w0, #2
// 0x29e504: b.lt #0x29e518
// 0x29e508: cmp w0, #4
// 0x29e50c: b.lt #0x29e518
// 0x29e510: cmp w0, #6
// 0x29e514: b.ge #0x29e518
// 0x29e518: LoadField: r2 = r1->field_17
// 0x29e518: ldur w2, [x1, #0x17]
// 0x29e51c: DecompressPointer r2
// 0x29e51c: add x2, x2, HEAP, lsl #32
// 0x29e520: stur x2, [fp, #-0x68]
// 0x29e524: CheckStackOverflow
// 0x29e524: ldr x16, [THR, #0x38] ; THR::stack_limit
// 0x29e528: cmp SP, x16
// 0x29e52c: b.ls #0x29e650
// 0x29e530: LoadField: r0 = r2->field_1b
// 0x29e530: ldur w0, [x2, #0x1b]
// 0x29e534: DecompressPointer r0
// 0x29e534: add x0, x0, HEAP, lsl #32
// 0x29e538: LoadField: r1 = r0->field_b
// 0x29e538: ldur w1, [x0, #0xb]
// 0x29e53c: DecompressPointer r1
// 0x29e53c: add x1, x1, HEAP, lsl #32
// 0x29e540: r0 = LoadClassIdInstr(r1)
// 0x29e540: ldurh w0, [x1, #1]
// 0x29e544: SaveReg r1
// 0x29e544: str x1, [SP, #-8]!
// 0x29e548: r4 = const [0, 0x1, 0x1, 0x1, null]
// 0x29e548: ldr x4, [PP, #0x68] ; [pp+0x68] List(5) [0, 0x1, 0x1, 0x1, Null]
// 0x29e54c: r0 = GDT[cid_x0 + 0x2060]()
// 0x29e54c: movz x17, #0x2060
// 0x29e550: add lr, x0, x17
// 0x29e554: ldr lr, [x21, lr, lsl #3]
// 0x29e558: blr lr
// 0x29e55c: add SP, SP, #8
// 0x29e560: SaveReg r0
// 0x29e560: str x0, [SP, #-8]!
// 0x29e564: r0 = postSms()
// 0x29e564: bl #0x29e658 ; [package:sms_flutter/api/login.dart] LoginApi::postSms
// 0x29e568: add SP, SP, #8
// 0x29e56c: r1 = Function '<anonymous closure>': static.
// 0x29e56c: add x1, PP, #8, lsl #12 ; [pp+0x8ed8] AnonymousClosure: static (0x2ab600), in [package:sms_flutter/main.dart] ::onBackgroundMessage (0x29e3c8)
// 0x29e570: ldr x1, [x1, #0xed8]
// 0x29e574: r2 = Null
// 0x29e574: mov x2, NULL
// 0x29e578: stur x0, [fp, #-0x70]
// 0x29e57c: r0 = AllocateClosure()
// 0x29e57c: bl #0x3558a4 ; AllocateClosureStub
// 0x29e580: r16 = <void?>
// 0x29e580: ldr x16, [PP, #0x2d0] ; [pp+0x2d0] TypeArguments: <void?>
// 0x29e584: ldur lr, [fp, #-0x70]
// 0x29e588: stp lr, x16, [SP, #-0x10]!
// 0x29e58c: SaveReg r0
// 0x29e58c: str x0, [SP, #-8]!
// 0x29e590: r4 = const [0x1, 0x2, 0x2, 0x2, null]
// 0x29e590: ldr x4, [PP, #0x2d8] ; [pp+0x2d8] List(5) [0x1, 0x2, 0x2, 0x2, Null]
// 0x29e594: r0 = then()
// 0x29e594: bl #0x32fba4 ; [dart:async] _Future::then
// 0x29e598: add SP, SP, #0x18
// 0x29e59c: r1 = Function '<anonymous closure>': static.
// 0x29e59c: add x1, PP, #8, lsl #12 ; [pp+0x8ee0] AnonymousClosure: static (0x2ab5c8), in [package:sms_flutter/main.dart] ::onBackgroundMessage (0x29e3c8)
// 0x29e5a0: ldr x1, [x1, #0xee0]
// 0x29e5a4: r2 = Null
// 0x29e5a4: mov x2, NULL
// 0x29e5a8: stur x0, [fp, #-0x70]
// 0x29e5ac: r0 = AllocateClosure()
// 0x29e5ac: bl #0x3558a4 ; AllocateClosureStub
// 0x29e5b0: r16 = <void?, Object>
// 0x29e5b0: add x16, PP, #8, lsl #12 ; [pp+0x8ee8] TypeArguments: <void?, Object>
// 0x29e5b4: ldr x16, [x16, #0xee8]
// 0x29e5b8: ldur lr, [fp, #-0x70]
// 0x29e5bc: stp lr, x16, [SP, #-0x10]!
// 0x29e5c0: SaveReg r0
// 0x29e5c0: str x0, [SP, #-8]!
// 0x29e5c4: r4 = const [0x2, 0x2, 0x2, 0x2, null]
// 0x29e5c4: ldr x4, [PP, #0x2800] ; [pp+0x2800] List(5) [0x2, 0x2, 0x2, 0x2, Null]
// 0x29e5c8: r0 = FutureExtensions.onError()
// 0x29e5c8: bl #0x174d60 ; [dart:async] ::FutureExtensions.onError
// 0x29e5cc: add SP, SP, #0x18
// 0x29e5d0: ldur x0, [fp, #-0x68]
// 0x29e5d4: LoadField: r1 = r0->field_13
// 0x29e5d4: ldur w1, [x0, #0x13]
// 0x29e5d8: DecompressPointer r1
// 0x29e5d8: add x1, x1, HEAP, lsl #32
// 0x29e5dc: LoadField: r2 = r0->field_1f
// 0x29e5dc: ldur w2, [x0, #0x1f]
// 0x29e5e0: DecompressPointer r2
// 0x29e5e0: add x2, x2, HEAP, lsl #32
// 0x29e5e4: LoadField: r3 = r0->field_17
// 0x29e5e4: ldur w3, [x0, #0x17]
// 0x29e5e8: DecompressPointer r3
// 0x29e5e8: add x3, x3, HEAP, lsl #32
// 0x29e5ec: stp x2, x1, [SP, #-0x10]!
// 0x29e5f0: SaveReg r3
// 0x29e5f0: str x3, [SP, #-8]!
// 0x29e5f4: r0 = _completeOnAsyncReturn()
// 0x29e5f4: bl #0x1720b0 ; [dart:async] ::_completeOnAsyncReturn
// 0x29e5f8: add SP, SP, #0x18
// 0x29e5fc: r0 = Null
// 0x29e5fc: mov x0, NULL
// 0x29e600: LeaveFrame
// 0x29e600: mov SP, fp
// 0x29e604: ldp fp, lr, [SP], #0x10
// 0x29e608: ret
// 0x29e608: ret
// 0x29e60c: sub SP, fp, #0x70
// 0x29e610: ldur x2, [fp, #-0x38]
// 0x29e614: mov x16, x1
// 0x29e618: mov x1, x0
// 0x29e61c: mov x0, x16
// 0x29e620: LoadField: r3 = r2->field_13
// 0x29e620: ldur w3, [x2, #0x13]
// 0x29e624: DecompressPointer r3
// 0x29e624: add x3, x3, HEAP, lsl #32
// 0x29e628: LoadField: r4 = r2->field_17
// 0x29e628: ldur w4, [x2, #0x17]
// 0x29e62c: DecompressPointer r4
// 0x29e62c: add x4, x4, HEAP, lsl #32
// 0x29e630: stp x1, x3, [SP, #-0x10]!
// 0x29e634: stp x4, x0, [SP, #-0x10]!
// 0x29e638: r0 = _completeOnAsyncError()
// 0x29e638: bl #0x180984 ; [dart:async] ::_completeOnAsyncError
// 0x29e63c: add SP, SP, #0x20
// 0x29e640: r0 = Null
// 0x29e640: mov x0, NULL
// 0x29e644: LeaveFrame
// 0x29e644: mov SP, fp
// 0x29e648: ldp fp, lr, [SP], #0x10
// 0x29e64c: ret
// 0x29e64c: ret
// 0x29e650: r0 = StackOverflowSharedWithoutFPURegs()
// 0x29e650: bl #0x35684c ; StackOverflowSharedWithoutFPURegsStub
// 0x29e654: b #0x29e530
}
[closure] static void <anonymous closure>(dynamic, Object?, StackTrace) {
// ** addr: 0x2ab5c8, size: 0x38
// 0x2ab5c8: EnterFrame
// 0x2ab5c8: stp fp, lr, [SP, #-0x10]!
// 0x2ab5cc: mov fp, SP
// 0x2ab5d0: CheckStackOverflow
// 0x2ab5d0: ldr x16, [THR, #0x38] ; THR::stack_limit
// 0x2ab5d4: cmp SP, x16
// 0x2ab5d8: b.ls #0x2ab5f8
// 0x2ab5dc: ldr x16, [fp, #0x18]
// 0x2ab5e0: SaveReg r16
// 0x2ab5e0: str x16, [SP, #-8]!
// 0x2ab5e4: r0 = print()
// 0x2ab5e4: bl #0x180ef8 ; [dart:core] ::print
// 0x2ab5e8: add SP, SP, #8
// 0x2ab5ec: LeaveFrame
// 0x2ab5ec: mov SP, fp
// 0x2ab5f0: ldp fp, lr, [SP], #0x10
// 0x2ab5f4: ret
// 0x2ab5f4: ret
// 0x2ab5f8: r0 = StackOverflowSharedWithoutFPURegs()
// 0x2ab5f8: bl #0x35684c ; StackOverflowSharedWithoutFPURegsStub
// 0x2ab5fc: b #0x2ab5dc
}
[closure] static void <anonymous closure>(dynamic, String) {
// ** addr: 0x2ab600, size: 0x4c
// 0x2ab600: EnterFrame
// 0x2ab600: stp fp, lr, [SP, #-0x10]!
// 0x2ab604: mov fp, SP
// 0x2ab608: CheckStackOverflow
// 0x2ab608: ldr x16, [THR, #0x38] ; THR::stack_limit
// 0x2ab60c: cmp SP, x16
// 0x2ab610: b.ls #0x2ab644
// 0x2ab614: r16 = "res"
// 0x2ab614: add x16, PP, #8, lsl #12 ; [pp+0x8ef0] "res"
// 0x2ab618: ldr x16, [x16, #0xef0]
// 0x2ab61c: ldr lr, [fp, #0x10]
// 0x2ab620: stp lr, x16, [SP, #-0x10]!
// 0x2ab624: r0 = +()
// 0x2ab624: bl #0x157310 ; [dart:core] _StringBase::+
// 0x2ab628: add SP, SP, #0x10
// 0x2ab62c: SaveReg r0
// 0x2ab62c: str x0, [SP, #-8]!
// 0x2ab630: r0 = print()
// 0x2ab630: bl #0x180ef8 ; [dart:core] ::print
// 0x2ab634: add SP, SP, #8
// 0x2ab638: LeaveFrame
// 0x2ab638: mov SP, fp
// 0x2ab63c: ldp fp, lr, [SP], #0x10
// 0x2ab640: ret
// 0x2ab640: ret
// 0x2ab644: r0 = StackOverflowSharedWithoutFPURegs()
// 0x2ab644: bl #0x35684c ; StackOverflowSharedWithoutFPURegsStub
// 0x2ab648: b #0x2ab614
}
[closure] static void main(dynamic) {
// ** addr: 0x2ab67c, size: 0x2c
// 0x2ab67c: EnterFrame
// 0x2ab67c: stp fp, lr, [SP, #-0x10]!
// 0x2ab680: mov fp, SP
// 0x2ab684: CheckStackOverflow
// 0x2ab684: ldr x16, [THR, #0x38] ; THR::stack_limit
// 0x2ab688: cmp SP, x16
// 0x2ab68c: b.ls #0x2ab6a0
// 0x2ab690: r0 = main()
// 0x2ab690: bl #0x2ab64c ; [package:sms_flutter/main.dart] ::main
// 0x2ab694: LeaveFrame
// 0x2ab694: mov SP, fp
// 0x2ab698: ldp fp, lr, [SP], #0x10
// 0x2ab69c: ret
// 0x2ab69c: ret
// 0x2ab6a0: r0 = StackOverflowSharedWithoutFPURegs()
// 0x2ab6a0: bl #0x35684c ; StackOverflowSharedWithoutFPURegsStub
// 0x2ab6a4: b #0x2ab690
}
}
cid_x0
represents value from register x0
as class id. GDT is used for polymorphic call which can be dumped from dart::DispatchTable object. For more information, read https://mrale.ph/dartvm/#global-dispatch-table-gdt
In your case, it requires finding fields type in the class (similar to recovering C/C++ struct from assembly). Then, using cid to lookup the GDT. This feature is not easy to implement. I have to implement many features for reversing basic information before.