mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-24 20:00:37 +00:00
feature: App 逆向防护
This commit is contained in:
@@ -12,7 +12,7 @@ BSS段(bss segment):通常用来存储程序中未被初始化的全局变
|
||||
|
||||
代码段(code segment):通常是指用来存储程序可执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量。
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ struct NSObject_IMPL {
|
||||
|
||||
因此可以知道,OC 的类底层是由 c/c++ 的继承实现的。
|
||||
|
||||
<img src="./../assets/OCObjectLayoutWhenISA.png" style="zoom:45%">
|
||||
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/OCObjectLayoutWhenISA.png" style="zoom:45%">
|
||||
|
||||
由于 obj 对象没有任何属性和方法,只有一个 isa 指针,且类的本质就是结构体,所以当结构体只有1个成员时,该成员的地址值,就是该结构体的地址。
|
||||
|
||||
@@ -118,7 +118,7 @@ struct Student_IMPL {
|
||||
|
||||
类的本质是结构体,结构体成员内存紧挨着。内存布局如图所示:
|
||||
|
||||
<img src="./../assets/StudentClassLayout.png" style="zoom:45%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/StudentClassLayout.png" style="zoom:45%">
|
||||
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ struct Student_IMPL {
|
||||
|
||||
如果上述结论正确,那是不是可以声明一个 ` Student_IMPL` 类型的结构体指针,指向 st 指针指向的对象。然后通过结构体指针访问成员变量,看看取值是不是正确的
|
||||
|
||||
<img src="./../assets/StructPointerVistorClassIvars.png" style="zoom:25%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/StructPointerVistorClassIvars.png" style="zoom:25%">
|
||||
|
||||
发现是可以正确访问的。
|
||||
|
||||
@@ -193,7 +193,7 @@ struct Student_IMPL {
|
||||
|
||||
为什么 `class_getInstanceSize([Person class])` 也是16,不是8+4吗?因为存在内存对齐,结构体的大小必须是最大成员大小的倍数(Person 中也就是8的倍数)
|
||||
|
||||
<img src="./../assets/StudentClassExtendsFromPersonClass.png" style="zoom:25%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/StudentClassExtendsFromPersonClass.png" style="zoom:25%">
|
||||
|
||||
|
||||
|
||||
@@ -330,12 +330,12 @@ int main(int argc, const char * argv[]) {
|
||||
|
||||
`Person *p1 = [Person new];` 这句代码在内存分配原理如下图所示
|
||||
|
||||

|
||||

|
||||
|
||||
**结论**
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
**可以 看到Person类的3个对象p1、p2、p3的isa的值相同。**
|
||||
|
||||
@@ -707,7 +707,7 @@ iOS 中,系统分配内存,都是16的倍数。pageSize?系统在分配内
|
||||
GUN 都存在内存对齐这个概念。
|
||||
`sizeof` 本质是运算符。在 Xcode 编译后就替换为真正的值。通过指令 `xcrun --sdk iphoneos clang -arch arm64 -S -emit-llvm ViewController.m -o ViewController.ll` 查看 IR。
|
||||
|
||||
<img src="./../assets/XcoedeViewSizeOfViaAssembly.png" style="zoom:25%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/XcoedeViewSizeOfViaAssembly.png" style="zoom:25%">
|
||||
|
||||
|
||||
|
||||
@@ -718,21 +718,21 @@ GUN 都存在内存对齐这个概念。
|
||||
|
||||
`objc_getClass()` 如果传递 instance 实例对象,返回 class 类对象;传递 Class 类对象,返回 meta-class 元类对象;传递 meta-class 元对象,则返回 NSObject(基类)的 meta-class 对象
|
||||
|
||||

|
||||

|
||||
|
||||
instance 的 isa 指向 Class。当调用方法时,通过 instance 的 isa 找到 Class,最后找到对象方法的实现进行调用
|
||||
class 的 isa 指向 meta-class。当调用类方法的时,通过 class 的 isa 找到 meta-class,最后找到类方法进行调用。
|
||||
|
||||

|
||||

|
||||
|
||||
当 Student 实例对象调用 Person 的对象方法时,首先根据 Student 对象的 isa 找到 Stduent 的 Class 类对象,然后根据 Stduent Class 类对象中的 superClass 找到 Person 的 Class 类对象,在类对象的对象方法列表中找到方法实现并调用。
|
||||
当 Stduent 实例对象调用 init 方法时候,首先根据 Student 对象的 isa 找到 Stduent 的 Class 类对象,然后根据 Stduent Class 类对象中的 superClass 找到 Person 的 Class 类对象,找到 Person Claas 的 superClass 到 NSObject 类对象,在 NSObject 类对象的方法列表中找到 `init` 方法并调用。
|
||||
|
||||

|
||||

|
||||
|
||||
当 Stduent 对象调用类方法的时候,先根据 isa 找到 Student 的元类对象,然后在元类对象的 superclass 找到 Person 的元类对象,再根据 Person 元类对象的 superClass 找到 NSObject 的元类对象。最后找到元类对象的方法列表,调用到对象方法。
|
||||
|
||||

|
||||

|
||||
|
||||
```objectivec
|
||||
@interface Student : NSObject
|
||||
@@ -937,7 +937,7 @@ class_rw_t *personMetaClassData = personClass->metaClass()->data();
|
||||
内存对齐是指数据在内存中存储时按照一定规则对齐到特定的地址上。在 iOS 开发中,内存对齐是为了提高内存访问的效率和性能。内存对齐的原因主要包括以下几点:
|
||||
1. 提高访问速度:内存对齐可以使数据在内存中的存储更加高效,因为大部分计算机体系结构都要求数据按照特定的边界对齐,这样可以减少内存访问的次数,提高访问速度。CPU访问非对齐的内存时需要进行多次拼接。
|
||||
如下图,比如需要读取从[2, 5]的内存,需要分别读取两次,然后还需要做位移的运算,最后才能得到需要的数据。这中间的损耗就会影响访问速度。
|
||||
<img src="./../assets/MemoryAlignReason.png" style="zoom:30%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/MemoryAlignReason.png" style="zoom:30%">
|
||||
|
||||
2. 为了方便移植。CPU是一块块的进行进行内存访问。有一些硬件平台不允许随机访问,只能访问对齐后的内存地址,否则会报异常。
|
||||
很多 CPU(如基于 Alpha,IA-64,MIPS,和 SuperH 体系的)拒绝读取未对齐数据。当一个程序要求这些 CPU 读取未对齐数据时,这时 CPU 会进入异常处理状态并且通知程序不能继续执行。举个例子,在 ARM,MIPS,和 SH 硬件平台上,当操作系统被要求存取一个未对齐数据时会默认给应用程序抛出硬件异常。
|
||||
@@ -1017,7 +1017,7 @@ NSLog(@"%zd", malloc_size(temp));
|
||||
成员变量占用8字节对齐,每个对象的第一个都是 isa 指针,必须要占用8字节。举例一个极端 case,假设 n 个对象,其中 m 个对象没有成员变量,只有 isa 指针占用8字节,其中的 n-m个对象既有 isa 指针,又有成员变量。每个类交错排列,那么 CPU 在访问对象的时候会耗费大量时间去识别具体的对象。很多时候会取舍,这个 case 就是时间换空间。以16字节对齐,会加快访问速度(参考链表和数组的设计)
|
||||
|
||||
上述是 Apple 官方的角度出发探究的,其他系统,比如 Linux 也是存在内存对齐的。由于 Linux 也是采用 GNU 的东西,所以探索下 GNU 下 glibc malloc 的实现。从[这里](https://ftp.gnu.org/gnu/glibc/)下载 glibc 源码。然后拖到 Xcode 中查看
|
||||
<img src="./../assets/GlibcInXcodeProject.png" style="zoom:25%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/GlibcInXcodeProject.png" style="zoom:25%">
|
||||
|
||||
可以看到 GNU 源码里面,内存对齐 `MALLOC_ALIGNMENT`
|
||||
在 i386 里面是16,在非 i386 里面有个判断
|
||||
@@ -1031,7 +1031,7 @@ NSLog(@"%zd", malloc_size(temp));
|
||||
# define INTERNAL_SIZE_T size_t
|
||||
```
|
||||
在 Xcode 打印输出, `__alignof__ (long double)` 为16,`sizeof(size_t)` 为8,即 `2 * SIZE_SZ` = 16,所以不管怎么看,在 GUN 里面内存对齐一定都是16.
|
||||
<img src="./../assets/GLibcMallocAlignment.png" style="zoom:25%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/GLibcMallocAlignment.png" style="zoom:25%">
|
||||
Todo: 研究探索 libmalloc 源码
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user