首页 iOS.& Swift Books 高级Apple调试& Reverse Engineering

25
用sbvalue桥接桥接& Memory 由Derek Selander撰写

到目前为止,在评估JIT代码(即,通过Python脚本执行的Objective-C,Swift,C等代码),您已经使用了一小组API来评估代码。

For example, you’ve used SBDebugger and SBCommandReturnObject’s HandleCommand method to evaluate code. SBDebugger’s HandleCommand goes straight to stderr, while you have a little more control over where the SBCommandReturnObject result ends up. Once evaluated, you had to manually parse the return output for anything of interest. This manual searching of the output from the JIT code is a bit unsightly. Nobody likes stringly typed things!

因此,是时候谈论LLDB Python模块中的一个新类了, sbvalue.,以及它如何简化JIT编码输出的解析。打开一个名为Xcode项目 分配器 在里面 起动机 文件夹本章。这是一个简单的应用程序,ASICH动态生成从文本字段的输入类输入。

This is accomplished by taking the string from the text field and using it as an input to the NSClassFromString function. If a valid class is returned, it’s initialized using the plain old init method. Otherwise, an error is spat out.

Build and run the application on any iOS 12 Simulator. You’ll make zero modifications to this project, yet you’ll explore object layouts in memory through sbvalue., as well as manually with pointers through LLDB.

绕开记忆布局车道

To truly appreciate the power of the sbvalue. class, you’re going to explore the memory layout of three unique objects within the Allocator application. You’ll start with an Objective-C class, then explore a Swift class with no superclass, then finally explore a Swift class that inherits from NSObject.

这些类中的所有三个属性具有以下任务:

  • A UIColor called eyeColor.
  • A language specific string (String/NSString) called firstName.
  • A language specific string (String/NSString) called lastName.

这些类的每个实例都以相同的值初始化。他们是:

  • eyeColor will be UIColor.brown or [UIColor brownColor] depending on language.
  • firstName will be "Derek" or @"Derek" depending on language.
  • lastName will be "Selander" or @"Selander" depending on language.

Objective-C内存布局

您将首先探索目标-C课程,因为它是如何听到在记忆中所铺设的专业人士的基础。跳到 dsobjectivecobject.h. 看看它。这是您的参考:

@interface DSObjectiveCObject : NSObject

@property (nonatomic, strong) UIColor *eyeColor;
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;

@end
@implementation DSObjectiveCObject

- (instancetype)init
{
  self = [super init];
  if (self) {
    self.eyeColor = [UIColor brownColor];
    self.firstName = @"Derek";
    self.lastName = @"Selander";
  }
  return self;
}
@end
struct DSObjectiveCObject {
  Class isa;
  UIColor *eyeColor;
  NSString *firstName
  NSString *lastName
}

(lldb) po 0x600000031f80
<DSObjectiveCObject: 0x600000031f80>
(lldb) po *(id *)(0x600000031f80)
DSObjectiveCObject
(lldb) x/gx 0x600000031f80
0x600000031f80: 0x0000000108b06568
(lldb) po 0x0000000108b06568
(lldb) po *(id *)(0x600000031f80 + 0x8)
UIExtendedSRGBColorSpace 0.6 0.4 0.2 1
(lldb) po sizeof(Class)
(lldb) po *(id *)(0x600000031f80 + 0x10)
(lldb) po *(id *)(0x600000031f80 + 0x18)

Swift内存布局没有超级类

笔记:这是值得一提的前线:SWIFT ABI仍然波动。这意味着在SWIFT ABI完成之前,以下信息可能会发生变化。新版本的Xcode中的那一天在以下部分中断了信息,请随时抱怨论坛中的所有帽子!

class ASwiftClass {
  let eyeColor = UIColor.brown
  let firstName = "Derek"
  let lastName = "Selander"
  
  required init() { }
}
struct ASwiftClass {
  Class isa;

  // Simplified, see "InlineRefCounts" 
  // in //github.com/apple/swift
  uintptr_t refCounts; 

  UIColor *eyeColor;

  // Simplified, see "_StringGuts" 
  // in //github.com/apple/swift
  struct _StringCore {
    uintptr_t _object;    // packed bits for string type
    uintptr_t rawBits;    // raw data
  } firstName;

  struct _StringCore {
    uintptr_t _object;    // packed bits for string type
    uintptr_t rawBits;    // raw data
  } lastName;
}
// ## _StringObject bit layout
//
// x86-64 and arm64: (one 64-bit word)
// +---+---+---|---+------+------------------------------------------+
// + t | v | o | w | uuuu | payload (56 bits)                        |
// +---+---+---|---+------+------------------------------------------+
// most significant bit                          least significatn bit
//
// where t: is-a-value, i.e. a tag bit that says not to perform ARC
//       v: sub-variant bit, i.e. set for isCocoa or isSmall
//       o: is-opaque, i.e. opaque vs contiguously stored strings
//       w: width indicator bit (0: ASCII, 1: UTF-16)
//       u: unused bits
//
// payload is:
//   isNative: the native StringStorage object
//   isCocoa: the Cocoa object
//   isOpaque & !isCocoa: the _OpaqueString object
//   isUnmanaged: the pointer to code units
//   isSmall: opaque bits used for inline storage // TODO: use them!
//

<Allocator.ASwiftClass: 0x61800009d830>
(lldb) po 0x61800009d830
<Allocator.ASwiftClass: 0x61800009d830>
(lldb) po [0x61800009d830 superclass]
SwiftObject
(lldb) po *(id *)0x61800009d830
(lldb) po *(id *)(0x61800009d830 + 0x8)
0x0000000000000002
(lldb) po [0x61800009d830 retain]
(lldb) po *(id *)(0x61800009d830 + 0x8)
0x0000000200000002
(lldb) po [0x61800009d830 release]
(lldb) po *(id *)(0x61800009d830 + 0x8)
0x0000000000000002
(lldb) po *(id *)(0x61800009d830 + 0x10)
UIExtendedSRGBColorSpace 0.6 0.4 0.2 1
(lldb) x/gt '0x61800009d830 + 0x18'
0x600003c3aa18: 0b1110010100000000000000000000000000000000000000000000000000000000
typedef struct {
  char spillover[7];
  char bits; // msb (tvow) bit types, lsb (uuuu) string length
  char start[8]; // start address of String
} SmallUTF8String;
(lldb) x/s '0x61800009d830 + 0x20'
"Derek"
(lldb) x/gx '0x61800009d830 + 0x18'
0x61800009d848: 0xe500000000000000
p/d *(int *)(0x61800009d830 + 0x18 + 7) & 0xf 

使用NSObject超类Swift Memory布局

最后一节。你知道该怎么做,所以我们将破解这个一起来了一下,跳过实际调试会话。

class ASwiftNSObjectClass: NSObject {
  let eyeColor = UIColor.brown
  let firstName = "Derek"
  let lastName = "Selander"
  
  required override init() { }
}
struct ASwiftNSObjectClass {
  Class isa;
  UIColor *eyeColor;

  struct _StringCore {
    uintptr_t _object;
    uintptr_t rawBits;
  } firstName;

  struct _StringCore {
    uintptr_t _object;
    uintptr_t rawBits;
  } lastName;
}

sbvalue.

耶!是时候谈论这个令人敬畏的课程。

(lldb) po [DSObjectiveCObject new]
<DSObjectiveCObject: 0x61800002eec0>
(lldb) script lldb.frame.EvaluateExpression('[DSObjectiveCObject new]')
<lldb.SBValue; proxy of <Swig Object of type 'lldb::SBValue *' at 0x10ac78b10> >
(lldb) script print lldb.target.EvaluateExpression('[DSObjectiveCObject new]')
(DSObjectiveCObject *) $2 = 0x0000618000034280
(lldb) script a = lldb.target.EvaluateExpression('[DSObjectiveCObject new]')
(lldb) script print a
(DSObjectiveCObject *) $0 = 0x0000608000033260
(lldb) script print a.description
<DSObjectiveCObject: 0x608000033260>
(lldb) script print a.value
0x0000608000033260
(lldb) po 0x0000608000033260
<DSObjectiveCObject: 0x608000033260>
(lldb) script print a.signed
106102872289888
(lldb) p/x 106102872289888
(long) $3 = 0x0000608000033260

探索properties through SBValue offsets

What about those properties stuffed inside that DSObjectiveCObject instance? Let’s explore those!

(lldb) script print a.GetNumChildren()
(lldb) script print a.GetChildAtIndex(0)
(NSObject) NSObject = {
  isa = DSObjectiveCObject
}
(lldb) script print a.GetChildAtIndex(1)
(UICachedDeviceRGBColor *) _eyeColor = 0x0000608000070e00
(lldb) script print a.GetChildAtIndex(2)
(__NSCFConstantString *) _firstName = 0x000000010db83368 @"Derek"
(lldb) script print a.GetChildAtIndex(3)
(__NSCFConstantString *) _lastName = 0x000000010db83388 @"Selander"
(lldb) script print a.GetChildAtIndex(2).description
Derek
(lldb) script a.size
8
(lldb) script a.deref.size
(lldb) script print a.type.name
DSObjectiveCObject *
(lldb) script print a.deref.type.name
DSObjectiveCObject

通过sbvalue查看原始数据

您甚至可以将原始数据转储出来 data property in sbvalue.! This is represented by a class named SBData,这是另一类您可以自己查看的课程。

(lldb) script print a.data
60 32 03 00 80 60 00 00                          `2...`..
(lldb) script print a.value

(lldb) script print a.deref.data
f0 54 b8 0d 01 00 00 00 00 0e 07 00 80 60 00 00  .T...........`..
68 33 b8 0d 01 00 00 00 88 33 b8 0d 01 00 00 00  h3.......3......

sbextressionOptions.

As mentioned when discussing the EvaluateExpression API, there’s an optional second parameter that will take an instance of type sbextressionOptions.。您可以使用此命令通过JIT执行的特定选项。

(lldb) script options = lldb.SBExpressionOptions()
(lldb) script options.SetLanguage(lldb.eLanguageTypeSwift)
(lldb) script options.SetCoerceResultToId()
expression -lswift -O -- your_expression_here
(lldb) e -lswift -O -- ASwiftClass()
error: <EXPR>:3:1: error: use of unresolved identifier 'ASwiftClass'
ASwiftClass()
^~~~~~~~~~~
(lldb) e -lswift -- import Allocator
(lldb) e -lswift -O -- ASwiftClass()
(lldb) script b = lldb.target.EvaluateExpression('ASwiftClass()', options)

用sbvalue命名名称引用变量

Referencing child sbvalue.s via GetChildAtIndex from sbvalue. is a rather ho-hum way to navigate to an object in memory. What if the author of this class added a property before eyeColor that totally screwed up your offset logic when traversing this sbvalue.?

(lldb) script print b.GetValueForExpressionPath('.firstName')

LLDB.Value.

One final cool thing you can do is create a Python reference that contains the sbvalue.’s properties as the Python object’s properties (wait… what?). Think of this as an object through which you can reference variables using Python properties instead of Strings.

(lldb) script c = lldb.value(b)
(lldb) script print c.firstName
(lldb) script print c.firstName.sbvalue.signed

然后去哪儿?

Holy cow… how dense was that chapter!? Fortunately you have come full circle. You can use the options provided by your custom command to dynamically generate your JIT script code. From the return value of your JIT code, you can write scripts that have custom logic based upon the return sbvalue. that is parsed through the EvaluateExpression APIs.

有一个技术问题?想报告一个错误吗? 你可以问的问题和bug报告本书的作者在我们的官书论坛 这里.

有反馈分享在线阅读体验吗? 如果您有关于UI,UX的反馈,请高在线到设计团队:

© 2021 Razeware LLC

您可以免费读取,本章的部分显示为 混淆了 文本。解锁这本书,以及我们整个书籍和视频目录,带有Raywenderlich.com的专业订阅。

现在解锁

为了突出或做笔记,你需要通过自己拥有这本书的订阅或能力。