栈(stack)是现代计算机程序里最为重要的概念之一,几乎每一个程序都使用了栈,没有栈就没有函数,没有局部变量,也就没有我们如今能够看见的所有的计算机语言。 在经典的计算机科学中,栈被定义为一个特殊的容器,用户可以将数据压入栈中(入栈,push),也可以将已经压入栈中的数据弹出(出栈,pop),但栈这个容器必须遵守一条规则:先入栈的数据后出栈(First In Last Out, FIFO),多多少少像叠成一叠的书:先叠上去的书在最下面,因此要最后才能取出。 在计算机系统中,栈则是一个具有以上属性的动态内存区域。程序可以将数据压入栈中,也可以将数据从栈顶弹出。压栈操作使得栈增大,而弹出操作使栈减小。
寄存器(Register)是中央处理器内用来暂存指令、数据和地址的电脑存储器。 它就是 CPU 内部用来存储数据的存储器件,容量小、访问速度快。
而 Arm64 有 31 个通用寄存器,相关用途如下。
The 64-bit ARM (AArch64) calling convention allocates the 31 general-purpose registers as: x31 (SP): Stack pointer or a zero register, depending on context. x30 (LR): Procedure link register, used to return from subroutines. x29 (FP): Frame pointer. x19 to x29: Callee-saved. x18 (PR): Platform register. Used for some operating-system-specific special purpose, or an additional caller-saved register. x16 (IP0) and x17 (IP1): Intra-Procedure-call scratch registers. x9 to x15: Local variables, caller saved. x8 (XR): Indirect return value address. x0 to x7: Argument values passed to and results returned from a subroutine.
The vm_read and vm_read_overwrite functions read a portion of a task’s virtual memory (they enable tasks to read other tasks’ memory). The vm_read function returns the data in a dynamically allocated array of bytes; the vm_read_overwrite function places the data into a caller-specified buffer (the data_in parameter).
/** async-safe version of dladdr. * * This method searches the dynamic loader for information about any image * containing the specified address. It may not be entirely successful in * finding information, in which case any fields it could not find will be set * to NULL. * * Unlike dladdr(), this method does not make use of locks, and does not call * async-unsafe functions. * * @param address The address to search for. * @param info Gets filled out by this function. * @return true if at least some information was found. */ bool ksdl_dladdr(const uintptr_t address, Dl_info* const info);
(lldb) si (lldb) register read General Purpose Registers:
对比发现两者变化的仅仅是 lr 和 pc。
1 2 3 4 5 6 7
// test1 lr = 0x0000000104e32f90 Demo_fishhook`-[ViewController viewDidLoad] + 56 at ViewController.m:84:16 pc = 0x0000000104e32fa0 Demo_fishhook`-[ViewController viewDidLoad] + 72 at ViewController.m:84:15
// objc_msgSend lr = 0x0000000104e32fa4 Demo_fishhook`-[ViewController viewDidLoad] + 76 at ViewController.m:84:9 pc = 0x0000000104e73098 Demo_fishhook`symbol stub for: objc_msgSend
/** * 1. Moved the vm protection modifying codes to here to reduce the * changing scope. * 2. Adding VM_PROT_WRITE mode unconditionally because vm_region * API on some iOS/Mac reports mismatch vm protection attributes. * -- Lianfu Hao Jun 16th, 2021 **/ err = vm_protect (mach_task_self (), (uintptr_t)indirect_symbol_bindings, section->size, 0, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY); if (err == KERN_SUCCESS) { /** * Once we failed to change the vm protection, we * MUST NOT continue the following write actions! * iOS 15 has corrected the const segments prot. * -- Lionfore Hao Jun 11th, 2021 **/ // 修改函数指向的地方 indirect_symbol_bindings[i] = cur->rebindings[j].replacement; } goto symbol_loop; } } cur = cur->next; } symbol_loop:; } }
(lldb) image help Commands for accessing information for one or more target modules.
Syntax: target modules <sub-command> ...
The following subcommands are supported:
add -- Add a new module to the current target's modules. dump -- Commands for dumping information about one or more target modules. list -- List current executable and dependent shared library images. load -- Set the load addresses for one or more sections in a target module. lookup -- Look up information within executable and dependent shared library images. search-paths -- Commands for managing module search paths for a target. show-unwind -- Show synthesized unwind instructions for a function.
(lldb) help image lookup Look up information within executable and dependent shared library images.
-A ( --all ) Print all matches, not just the best match, if a best match is available.
-F <function-name> ( --function <function-name> ) Lookup a function by name in the debug symbols in one or more target modules.
-a <address-expression> ( --address <address-expression> ) Lookup an address in one or more target modules.
-f <filename> ( --file <filename> ) Lookup a file by fullpath or basename in one or more target modules.
-i ( --no-inlines ) Ignore inline entries (must be used in conjunction with --file or --function).
-l <linenum> ( --line <linenum> ) Lookup a line number in a file (must be used in conjunction with --file).
-n <function-or-symbol> ( --name <function-or-symbol> ) Lookup a function or symbol by name in one or more target modules.
-o <offset> ( --offset <offset> ) When looking up an address subtract <offset> from any addresses before doing the lookup.
-r ( --regex ) The <name> argument for name lookups are regular expressions.
-s <symbol> ( --symbol <symbol> ) Lookup a symbol by name in the symbol tables in one or more target modules.
-t <name> ( --type <name> ) Lookup a type by name in the debug symbols in one or more target modules.
-v ( --verbose ) Enable verbose lookup information. This command takes options and free-form arguments. If your arguments resemble option specifiers (i.e., they start with a - or --), you must use ' -- ' between the end of the command options and the beginning of the arguments.
'image' is an abbreviation for 'target modules'
比如
image list: 输出当前进程所依赖的共享库
image list -o -f: 上个命令简洁版,输出相关库的 ASLR 地址(o: offset)
image lookup -n xxx: 输出 xxx 符号的相关信息
image lookup -t xxx: 输出 xxx 符号的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
(lldb) image lookup -n NSLog 1 match found in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Foundation.framework/Foundation: Address: Foundation[0x00000000000f7762] (Foundation.__TEXT.__text + 1006242) Summary: Foundation`NSLog
(lldb) image lookup -t FBBlockStrongRelationDetector 0 match found in /Users/joakim/Library/Developer/Xcode/DerivedData/Demo_fishhook-ebwkhxbcogafxbclcdeifrppsgdu/Build/Products/Debug-iphonesimulator/Demo_fishhook.app/Demo_fishhook: id = {0x00071c41}, name = "FBBlockStrongRelationDetector", byte-size = 176, decl = FBBlockStrongRelationDetector.h:23, compiler_type = "@interface FBBlockStrongRelationDetector : NSObject{ void * forwarding; int flags; int size; void (*)(_block_byref_block *, _block_byref_block *) byref_keep; void (*)(_block_byref_block *) byref_dispose; void *[16] captured; BOOL _strong; } @property(nonatomic, assign, readwrite, getter = isStrong, setter = setStrong:) BOOL strong; @end"
// scr/widgets/binding.dart /// A concrete binding for applications based on the Widgets framework. /// /// This is the glue that binds the framework to the Flutter engine. classWidgetsFlutterBindingextendsBindingBasewithGestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding{ static WidgetsBinding ensureInitialized() { if (WidgetsBinding.instance == null) WidgetsFlutterBinding(); // 见 [2.2.1] return WidgetsBinding.instance; } }
_renderViewElement: 执行 attachToRenderTree 方法后,返回的 Element 类型的对象,可通过 WidgetsBinding.renderViewElement 获取,是 Element tree 的根节点(通过注释 The [Element] that is at the root of the hierarchy (and which wraps the [RenderView] object at the root of the rendering hierarchy).)。
2.4.2 RenderObjectToWidgetAdapter 初始化
1 2 3 4 5 6 7 8
// A bridge from a [RenderObject] to an [Element] tree. classRenderObjectToWidgetAdapter<TextendsRenderObject> extendsRenderObjectWidget{ RenderObjectToWidgetAdapter({ this.child, // widget tree 中的对象 this.container, // The [RenderObject] that is the parent of the [Element] created by this widget. this.debugShortDescription, }) : super(key: GlobalObjectKey(container)); }
RenderObjectToWidgetAdapter 是一个从 [RenderObject](root of render tree) 到 [Element](root of element tree) 的桥接类,该类的主要方法有
// The element at the root of the tree. abstractclassRootRenderObjectElementextendsRenderObjectElement{ void assignOwner(BuildOwner owner) { _owner = owner; } }
// src/widgets/framework.dart classGlobalObjectKey<TextendsState<StatefulWidget>> extendsGlobalKey<T> { /// Creates a global key that uses [identical] on [value] for its [operator==]. const GlobalObjectKey(this.value) : super.constructor();
/// The object whose identity is used by this key's [operator==]. finalObject value;
@override booloperator ==(Object other) { if (other.runtimeType != runtimeType) returnfalse; return other is GlobalObjectKey<T> && identical(other.value, value); }
void scheduleWarmUpFrame() { if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle) return;
_warmUpFrame = true; Timeline.startSync('Warm-up frame'); finalbool hadScheduledFrame = _hasScheduledFrame; // 异步执行, 执行完上面的代码才会执行 `2.4.1 attachRootWidget` // We use timers here to ensure that microtasks flush in between. Timer.run(() { assert(_warmUpFrame); handleBeginFrame(null); }); Timer.run(() { assert(_warmUpFrame); handleDrawFrame(); // We call resetEpoch after this frame so that, in the hot reload case, // the very next frame pretends to have occurred immediately after this // warm-up frame. The warm-up frame's timestamp will typically be far in // the past (the time of the last real frame), so if we didn't reset the // epoch we would see a sudden jump from the old time in the warm-up frame // to the new time in the "real" frame. The biggest problem with this is // that implicit animations end up being triggered at the old time and // then skipping every frame and finishing in the new time. resetEpoch(); _warmUpFrame = false; if (hadScheduledFrame) scheduleFrame(); });
// Lock events so touch events etc don't insert themselves until the // scheduled frame has finished. lockEvents(() async { await endOfFrame; Timeline.finishSync(); }); }
StandardMessageCodec: [MessageCodec] using the Flutter standard binary encoding. // flutter 标准的二进制编码,可以参考 Writing custom platform-specific code 的 Platform channel data types support and codecs 小节,当然 class StandardMessageCodec implements MessageCodec<dynamic> { 定义上面的注释也有解释。
StringCodec: [MessageCodec] with UTF-8 encoded String messages. // utf-8 编码的字符串消息
/// A codec for method calls and enveloped results. abstractclassMethodCodec{ /// Encodes the specified [methodCall] into binary. ByteData encodeMethodCall(MethodCall methodCall);
/// Decodes the specified [methodCall] from binary. MethodCall decodeMethodCall(ByteData? methodCall);
/// Decodes the specified result [envelope] from binary. dynamic decodeEnvelope(ByteData envelope);
/// Encodes a successful [result] into a binary envelope. ByteData encodeSuccessEnvelope(dynamic result);
/// Encodes an error result into a binary envelope. ByteData encodeErrorEnvelope({ requiredString code, String? message, dynamic details}); }
BinaryMessenger get binaryMessenger => _binaryMessenger ?? defaultBinaryMessenger;
BinaryMessenger get defaultBinaryMessenger { assert(() { if (ServicesBinding.instance == null) { throw FlutterError( 'ServicesBinding.defaultBinaryMessenger was accessed before the ' 'binding was initialized.\n' "If you're running an application and need to access the binary " 'messenger before `runApp()` has been called (for example, during ' 'plugin initialization), then you need to explicitly call the ' '`WidgetsFlutterBinding.ensureInitialized()` first.\n' "If you're running a test, you can call the " '`TestWidgetsFlutterBinding.ensureInitialized()` as the first line in ' "your test's `main()` method to initialize the binding." ); } returntrue; }()); // 见 [2.4.1] return ServicesBinding.instance!.defaultBinaryMessenger; }
class_DefaultBinaryMessengerextendsBinaryMessenger{ const _DefaultBinaryMessenger._(); Future<ByteData?> _sendPlatformMessage(String channel, ByteData? message) { // 见 [2.4.2] final Completer<ByteData?> completer = Completer<ByteData?>(); // ui.window is accessed directly instead of using ServicesBinding.instance.window // because this method might be invoked before any binding is initialized. // This issue was reported in #27541. It is not ideal to statically access // ui.window because the Window may be dependency injected elsewhere with // a different instance. However, static access at this location seems to be // the least bad option. // 见 [2.5] ui.window.sendPlatformMessage(channel, message, (ByteData? reply) { try { // 见 [3.1.1], 会在 engine 层保存起来 completer.complete(reply); } catch (exception, stack) { // ... }); return completer.future; } }
// The future's _isComplete doesn't take into account pending completions. // We therefore use _mayComplete. boolget isCompleted => !future._mayComplete; }
/// Wraps the given [callback] in another callback that ensures that the /// original callback is called in the zone it was registered in. static PlatformMessageResponseCallback? _zonedPlatformMessageResponseCallback(PlatformMessageResponseCallback? callback) { if (callback == null) returnnull;
// Store the zone in which the callback is being registered. final Zone registrationZone = Zone.current;
// platform_configuration.cc -L105 Dart_Handle SendPlatformMessage(Dart_Handle window, conststd::string& name, Dart_Handle callback, Dart_Handle data_handle){ // 获取当前 dart_state UIDartState* dart_state = UIDartState::Current(); // 没有 platform_configuration, 抛出异常 if (!dart_state->platform_configuration()) { return tonic::ToDart( "Platform messages can only be sent from the main isolate"); }
// The animator is owned by the UI thread but it gets its vsync pulses // from the platform. auto animator = std::make_unique<Animator>(*shell, task_runners, std::move(vsync_waiter));
// FlutterEngine.mm -L367 - (void)setupChannels { // This will be invoked once the shell is done setting up and the isolate ID // for the UI isolate is available. fml::WeakPtr<FlutterEngine> weakSelf = [self getWeakPtr]; // 见 [4.2] [_binaryMessenger setMessageHandlerOnChannel:@"flutter/isolate" binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) { if (weakSelf) { weakSelf.get().isolateId = [[FlutterStringCodec sharedInstance] decode:message]; } }]; // ... }
// FlutterBinaryMessengerRelay.mm -L42 - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channel binaryMessageHandler: (FlutterBinaryMessageHandler)handler { if (self.parent) { // 见 [4.3] return [self.parent setMessageHandlerOnChannel:channel binaryMessageHandler:handler]; } else { FML_LOG(WARNING) << "Communicating on a dead channel."; return-1; } }
4.3 setMessageHandlerOnChannel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// FlutterEngine.mm -L730 - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channel binaryMessageHandler: (FlutterBinaryMessageHandler)handler { NSParameterAssert(channel); if (_shell && _shell->IsSetup()) { // 见 [4.4] self.iosPlatformView->GetPlatformMessageRouter().SetMessageHandler(channel.UTF8String, handler); return _connections->AquireConnection(channel.UTF8String); } else { NSAssert(!handler, @"Setting a message handler before the FlutterEngine has been run."); // Setting a handler to nil for a not setup channel is a noop. return flutter::ConnectionCollection::MakeErrorConnection(-1); } }
/** * A factory of compatible reader/writer instances using the Flutter standard * binary encoding or extensions thereof. */ FLUTTER_EXPORT @interface FlutterStandardReaderWriter : NSObject
/** * A message encoding/decoding mechanism. */ FLUTTER_EXPORT @protocol FlutterMessageCodec
DART_EXPORT Dart_Handle Dart_InvokeClosure(Dart_Handle closure, int number_of_arguments, Dart_Handle* arguments){ DARTSCOPE(Thread::Current()); API_TIMELINE_DURATION(T); CHECK_CALLBACK_STATE(T); const Instance& closure_obj = Api::UnwrapInstanceHandle(Z, closure); if (closure_obj.IsNull() || !closure_obj.IsCallable(NULL)) { RETURN_TYPE_ERROR(Z, closure, Instance); } if (number_of_arguments < 0) { return Api::NewError( "%s expects argument 'number_of_arguments' to be non-negative.", CURRENT_FUNC); }
// Set up arguments to include the closure as the first argument. const Array& args = Array::Handle(Z, Array::New(number_of_arguments + 1)); Object& obj = Object::Handle(Z); args.SetAt(0, closure_obj); for (int i = 0; i < number_of_arguments; i++) { obj = Api::UnwrapHandle(arguments[i]); if (!obj.IsNull() && !obj.IsInstance()) { RETURN_TYPE_ERROR(Z, arguments[i], Instance); } args.SetAt(i + 1, obj); } // Now try to invoke the closure. return Api::NewHandle(T, DartEntry::InvokeClosure(args)); }
// future_impl.dart -L540 void _asyncComplete(FutureOr<T> value) { assert(!_isComplete); // Two corner cases if the value is a future: // 1. the future is already completed and an error. // 2. the future is not yet completed but might become an error. // The first case means that we must not immediately complete the Future, // as our code would immediately start propagating the error without // giving the time to install error-handlers. // However the second case requires us to deal with the value immediately. // Otherwise the value could complete with an error and report an // unhandled error, even though we know we are already going to listen to // it.
if (value is Future<T>) { _chainFuture(value); return; } // TODO(40014): Remove cast when type promotion works. // This would normally be `as T` but we use `as dynamic` to make the // unneeded check be implict to match dart2js unsound optimizations in the // user code. _asyncCompleteWithValue(value asdynamic); // Value promoted to T. }