worawit / blutter

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

Feature request: display the corresponding field to GDT access #72

Open cryptax opened 4 weeks ago

cryptax commented 4 weeks ago

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
  }
}
worawit commented 1 week ago

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.