0%

阅读全文 »

为什么要这么做

在出租屋里的路由器配置了SSR,所有设备科学上网已经很方便了。回到老家后,出现了一些需要科学上网的场景却又很难实现,比如玩switch游戏时需要科学上网,懵逼了。于是想到了自己的MBP有SSR,能开启全局代理然后分享出来吗?

正题

配置

这个方法实现了的效果是,同一局域网内的设备使用MBP上的全局代理,这里不说SSR怎么配置。

下载安装privoxy,注意对应平台。根据环境,我使用的是Privoxy 3.0.26 64 bit.pkg这个包。

下载安装完毕后,修改一些需要自定义的配置,打开/usr/local/etc/privoxy目录下的config文件,搜索forward-socks5t,并将端口号改为自己SSR里配置的端口(下面的1086是笔者使用的端口)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#  Examples:
#
# From the company example.com, direct connections are made to
# all "internal" domains, but everything outbound goes through
# their ISP's proxy by way of example.com's corporate SOCKS 4A
# gateway to the Internet.
#
# forward-socks4a / socks-gw.example.com:1080 www-cache.isp.example.net:8080
# forward .example.com .
#
# A rule that uses a SOCKS 4 gateway for all destinations but no
# HTTP parent looks like this:
#
# forward-socks4 / socks-gw.example.com:1080 .
#
# To chain Privoxy and Tor, both running on the same system, you
# would use something like:
#
forward-socks5t / 127.0.0.1:1086 .
#
# Note that if you got Tor through one of the bundles, you may
# have to change the port from 9050 to 9150 (or even another
# one). For details, please check the documentation on the Tor
# website.

继续搜索listen-address,将127.0.0.1修改为0.0.0.0,端口任意修改为未占用的(笔者使用的是2134)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#  Example:
#
# Suppose you are running Privoxy on a machine which has the
# address 192.168.0.1 on your local private network
# (192.168.0.0) and has another outside connection with a
# different address. You want it to serve requests from inside
# only:
#
# listen-address 192.168.0.1:8118
#
# Suppose you are running Privoxy on an IPv6-capable machine and
# you want it to listen on the IPv6 address of the loopback
# device:
#
# listen-address [::1]:8118
#
listen-address 0.0.0.0:2134
#
阅读全文 »

进程和线程

进程和线程在操作系统中是比较重要的内容,面试中的基础提问也经常会出现,如进程和线程的区别等等,如果没有仔细梳理,还真的很难说清楚。本文仅列出进程和线程的基本内容及区别。

在一些操作系统的书籍中,通常会介绍到多任务系统,现代的操作系统基本都是支持 “多任务”,简单地说,多任务指的是操作系统可以同时运行多个任务。比如我现在一边在MacDown写东西,一边听音乐,一边在用浏览器看文章。

现在,多核CPU非常普及,在这之前使用的单核CPU,也是可以执行多任务,由于CPU执行代码都是顺序执行,那么单核CPU如何执行多任务?答案是操作系统按一定时间分配CPU给任务1、任务2…,任务之间交替使用CPU执行,由于CPU的执行速度快且任务之间的切换时间间隔很短,因此就有多个任务同时执行一样。

在多核CPU上能够实现真正的并行执行多任务,操作系统自动把多个任务轮流调度到CPU的每个核心上执行。

对操作系统而言,一个任务就是一个进程(Process)。进程有可能不止同时做一件事,比如音乐播放器可以一边播放音乐,一边搜索歌曲。在进程内部同时执行的多个“子任务”,它们是在多个线程(Thread)上执行。

所以,进程至少有一个线程。同样地,在单核CPU中,多个线程之间可以快速切换实现多线程并发;在多核CPU中,多个线程在不同核心上执行,实现了多线程并行。

CPU 进程 线程
单核 多进程并发,多个进程时间片轮换 多线程并发,同一进程内的线程轮换
多核 并行,不同进程的线程使用不同核心 并行,多个线程使用不同的核心

进程

程序本身只是指令、数据及其组织形式的描述,进程才是程序的真正运行实例。在分时系统中,进程是基本的运作单位,但是在当代多数面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器。进程内包含一个或多个线程(每一个线程都代表一个进程内的一个独立执行上下文)

阅读全文 »

内存分配

在早期的计算机中,程序是直接运行在物理内存上,也就是说,程序运行时所访问的地址都是物理地址,如果计算机只运行一个程序且该程序所需的内存空间不超过物理内存大小,就不会有问题。现在计算机需要同时运行多个程序,那么如何将有限的物理内存分配给多个程序使用?

直接分配的弊端

假设一台计算机有64MB内存,程序A运行需要50MB,程序运行需要10MB,需要同时运行这两个程序,比较直接的做法就是将0MB~50MB分配给A,50MB~60MB分配给B。

这样的分配策略会带来很多问题:

  • 地址空间不隔离 所有程序都直接访问物理地址,程序之间使用的地址空间共享物理内存,很容易发生恶意程序改写其他程序内存数据的情况;另外本身有bug的程序也有可能影响到其他程序的执行。这造成了程序运行不稳定的情况。
  • 程序运行时地址不确定 在程序装入运行时,需要分配一块足够大的空闲区域,而这个位置不确定,那么在程序编写时,指令的跳转需要你自己计算得出绝对地址,这是十分麻烦的。
  • 内存使用效率低 执行一个程序就将整个程序加载到内存,若需要继续同时执行另外的程序,则会出现内存不足,这时只能将内存中现有的数据换出到磁盘,磁盘、内存之间的大容量的换出换入必会导致效率低下

如何解决直接分配的弊端

解决地址空间不隔离和程序运行时地址不确定

从程序执行的角度看,我们不希望它介入到复杂得内存分配过程中,我们希望一个程序在执行的时候只需要一个简单得执行环境(独立单一的地址空间、单一的CPU,不用关心其他程序)。

可以把地址空间想象成一个很大的数组,数组大小取决于地址空间的地址长度,如64位的地址空间为2^64 = 18446744073709551616,一般用十六进制表示0x0000000000000000~0xFFFFFFFFFFFFFFFF

阅读全文 »

前言

本文纯属是根据前人对autoreleasepool的分析学习和苹果文档、源码的一次学习笔记,内容大部分来自引用。本人所做的工作仅是按照前人的笔记手动实践一遍梳理原理并记录供日后方便回顾。

通常来说,研究Objective-C必备的源码

objc4

引出autoreleasepool

iOS应用程序在默认创建时,main函数的内容都有一个autorelease块包裹函数体

1
2
3
4
5
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

从iOS内存管理的内容可以得知,这个自动释放池块对应着主线程,伴随着整个应用程序生命周期,当我们手动退出应用程序,整个自动释放池内的对象都将被释放,因此不会出现内存泄漏。

另外,从objc4源码对autorelease pool实现中的注释中可以获得一些相关信息

1
2
3
4
5
6
7
8
9
10
11
12
13
/***********************************************************************
Autorelease pool implementation

A thread's autorelease pool is a stack of pointers.
Each pointer is either an object to release, or POOL_BOUNDARY which is
an autorelease pool boundary.
A pool token is a pointer to the POOL_BOUNDARY for that pool. When
the pool is popped, every object hotter than the sentinel is released.
The stack is divided into a doubly-linked list of pages. Pages are added
and deleted as necessary.
Thread-local storage points to the hot page, where newly autoreleased
objects are stored.
**********************************************************************/
阅读全文 »

前言

一般而言,iOS设备上的固件恢复需要配合Apple服务器进行校验,Apple停止公开验证某个固件版本时,iOS设备就不能从高版本恢复到停止验证的版本。

前置条件

  1. 具备解锁nvram,写入generator的可能
  2. 根据设备的唯一码备份了对应的SHSH2文件
  3. 当前最新固件SEP兼容需要降级的目标版本固件

generator&nonce

generator是记录在shsh2文件中的一串值,这串值对应着一个nonce。nonce是一个只能使用一次的随机数。它在认证协议中用于阻止重放攻击。

原理

iOS/iTunes 在更新设备固件的过程中,会将设备的 ECID,系统版本等信息,以及一个一次使用的 Nonce 发送给 Apple 的验证服务器,服务器在校验通过后,会返回校验结果给 iOS/iTunes,结果使用非对称算法加密,在没有私钥的情况下无法解密,也无法伪造。

但是,我们可以将校验结果保存下来,之后 Apple 不再提供此版本校验的时候(假设不考虑 SEP 兼容性),在越狱后通过 nvram 固定 nonce 为此校验结果使用的,来重放校验过程,实现 iOS 系统降级/更新到不提供验证的版本。

简单理解,备份的shsh2文件对应着一个nonce,在恢复到低版本的时候,先固定shsh2对应的nonce,然后绕过校验进行固件恢复

阅读全文 »

内存使用

应用的内存消耗主要分为两部分:栈大小和堆大小

栈大小

应用中的每个线程都有专用的栈空间,栈可以在线程存在期间自由使用。线程的最大栈空间很小,因此有很多限制

  • 限制递归调用的最大方法数
  • 方法中使用的参数个数和内部变量个数
  • 视图层级的最大深度

堆大小

每个进程的所有线程共享一个堆。OC对象如NSString、UIImage、视图等都会消耗堆内存。大多数情况下,OC对象存放在堆中,与通过类创建的对象相关的所有数据也都存放在堆中

内存管理模型

苹果LLVM官方文档使用术语持有关系引用计数来描述OC对象的内存管理。

如果一个对象处于被持有状态,那么它占用的内存就不能被回收。

阅读全文 »

逆向获取block对应函数入口和函数签名

在逆向分析App的时候,有时会遇到某个关键方法中传入一个block参数来做回调,class-dump无法解析出block的类型以及函数签名。了解过block本质及其内存模型后,就可以通过lldb动态调试来获取目标信息

block的内存结构

在LLVM文档中,找到block的实现规范Block Implementation Specification,找到block内存结构的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Block_literal_1 {
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor_1 {
unsigned long int reserved; // NULL
unsigned long int size; // sizeof(struct Block_literal_1)
// optional helper functions
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
void (*dispose_helper)(void *src); // IFF (1<<25)
// required ABI.2010.3.16
const char *signature; // IFF (1<<30)
} *descriptor;
// imported variables
};

其中block对应的实现函数地址入口和函数签名分别在void (*invoke)(void *, ...);descriptor中的const char *signature;中保存

实例

某app中的某个带block参数的方法

- (void)parseRequest:(id)arg1 result:(id)arg2 completion:(id)arg3;

祭出debugserver和lldb

阅读全文 »

block

block是OC对闭包的实现,闭包的定义如下:

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

block用来实现匿名函数的特性,是一种特殊数据类型,可以定义为变量、作为参数、作为返回值,一般用来保存一段代码,在需要的时候回调,在iOS中广泛使用,如GCD、动画变换、网络回调等等

block基础

表达式

returnType (^blockName)(parameterTypes)

block变量声明

1
2
3
4
5
6
7
8
//声明无返回值,参数为空,名为aBlock的block变量
void(^aBlock)(void);

//声明无返回值,参数为一个NSString对象,名为aBlock的block变量
void(^aBlock)(NSString *aString);

//其中形参名可省略
void(^aBlock)(NSString *);

block变量赋值

阅读全文 »

简介

Lookin是一个免费的iOS UI层次分析App,相比Reveal,它不仅能获取视图控件对应的成员变量名称,而且可以做一些简单的UI之外的动态调试

安装

Mac端直接从官网下载App即可

iPhone端

配合越狱设备使用更快捷,类似于Reveal,使用越狱插件为目标App注入LookinServer.framework

LookinLoader


Lookin
阅读全文 »