逆向

本人逆向学习之路总结,涉及到iOS和macOS平台。

工具

逆向步骤

iOS

  1. 安装MonkeyDev
  2. 脱壳(AppStore下载的ipa文件是被加密过的):PP助手下载越狱版本的ipa文件或者IPADownload(最简便的方式)
  3. 瘦身、符号表恢复:我们下载的包可以用于ARMv7、ARM64平台,可以使用lipo macho -thin arm64 -output macho_arm64_thin进行瘦身,再使用./restore-symbol macho_arm64_thin -o macho_arm64_thin_restore恢复符号表
  4. 运行:MonkeyDev

macOS

  1. 可以直接使用MonkeyDev进行调试
  2. 或者我写的Reverse_xctemplatesMac调试模板

知识必备

ASLR(Address space layout randomization)(随机地址偏移)

我们在Hopper中看到的地址为模块偏移前基地址,需要在加上随机地址偏移(在macOS调试的时候发现,在Debug模式下,始终为0),才是我们得到当前运行程序在内存中实际的地址。获取地址方式有两种,但是有一个细微的差别(自己也曾在这里停留了半天,就是一个细微的差别导致的):

获取的地址可以直接在Hopper中修改基地址,这样在查找跳转中直接使用运行过程中内存地址就可以找到了。

这里获取的地址需要自己手动加,然后在从Hopper进行查找。

寄存器

iOS

ARM64 汇编——寄存器和指令

macOS

OC下的寄存器:

rax:返回值
rdi:self
rsi:_cmd
rdx、rcx、r8~r9:参数
rbp:基址指针
rsp:栈指针

iOS高级调试&逆向技术-汇编寄存器调用

工具详解

insert_dylib

原理就是在load command中添加要加载的库,这样我们程序在启动的时候就可以加载我们编写的动态库实现方法交换。

在hook过程中,使用了libsubstitute.dylib,由于编写的动态库需要引用到,这就涉及到该库应该放到什么地方的问题。

先看看生成的动态库是怎么引用这个库的

可执行文件下面?这里指的可执行的文件并不是动态库的mach-o文件,而是要hook的app的mach-o文件下面。这个问题困扰了好久才发现。(猜测项目中引用的话会修改这个加载库为@rpath/libsubstitute.dylib并将其放入Frameworks中)

class-dump

dump出头文件(但是nygard/class-dump的对swift支持并不是很好,很多时候还是会出问题,所以用了BlueCocoa/class-dump的版本,至少不崩溃了)

fishhook

我们可能不仅仅需要hook相关的OC消息发送,可能还会涉及到静态方法的hook。

fishhook无法hook自己写的c方法

动态修改 C 语言函数的实现仔细的分析了fishhook

比如我们在hopper中看到的sub_开头的方法没有名字,lldb中显示___lldb_unnamed_symbol不是通过objc_msgSend进行派发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// Hook c/c++/swift等编译期方法
#import "substrate.h"
#import <mach-o/dyld.h>

static int (*ori_sub_1000f5ff84)(int, int);
static int hook_sub_1000f5ff84(int arg0, int arg1) {
NSLog(@"XWJACK Hook sign check.");
return 1;
}

static void __attribute__((constructor)) initialize(void) {
NSLog(@"XWJACK Begin hook c function");
/// 00000001000f5f84 如下图
/// _dyld_get_image_vmaddr_slide(0) 获取镜像偏移地址(Xcode在Debug模式下,偏移地址总是为0)
MSHookFunction((void*)(0x00000001000f5f84 + _dyld_get_image_vmaddr_slide(0)), (void *)&hook_sub_1000f5ff84, (void *)&ori_sub_1000f5ff84);
}

ptrace

程序使用ptrace来进行动态调试保护,使得执行lldb的时候出现Process xxxx exited with status = 45 (0x0000002d)错误。

使用Hopper修改指令

  • 使用Hopper直接修改ptrace,如Surge(2.4.6)
    入口就是:EntryPoint

直接暴力修改成如下形式:

使用代码进行修改,使用fishhook替换ptrace函数。

关于反调试&反反调试那些事

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#import "fishhook.h"
#import <Foundation/Foundation.h>
#import <sys/sysctl.h>

typedef int (*ptrace_ptr_t)(int _request,pid_t _pid, caddr_t _addr,int _data);
static ptrace_ptr_t orig_ptrace = NULL;
int my_ptrace(int _request, pid_t _pid, caddr_t _addr, int _data);
int my_ptrace(int _request, pid_t _pid, caddr_t _addr, int _data){
if(_request != 31){
return orig_ptrace(_request,_pid,_addr,_data);
}

NSLog(@"🍒 ptrace request is PT_DENY_ATTACH");

return 0;
}
__attribute__((constructor)) static void entry(){
rebind_symbols((struct rebinding[1]){{"ptrace", my_ptrace, (void*)&orig_ptrace}},1);
}

Swift && Objective-C混编

如果改项目是混编项目,难度要高很多,比如Reveal,Swift是类似于C++,在很多东西在编译期就知道,而且Swift在生成的代码中还进行的“混淆”,这使得在hook方法的时候变得不那么容易。

1
2
3
4
5
6
@interface _TtC6Reveal20LicenseBindingsModel : NSObject
{
...
}
...
@end

如果直接hook _TtC6Reveal20LicenseBindingsModel类,是找不到这个类对应的方法的,我们需要找到这类正确的名字。

我的做法是先下断点b 0x00000001001ff200

停止后打印值,看到真正的类名其实是Reveal.LicenseBindingsModel,之后我们就可以正常的hook了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@implementation NSObject (XWJACK_LicenseBindingsModel)
- (BOOL)hook_isCommercialLicense {
return YES;
}
- (BOOL)hook_licensePeriodExpired {
return NO;
}
- (BOOL)hook_upgradeLicenseMenuItemVisible {
return NO;
}
- (NSString *)hook_licenseEmail {
return @"xwjack@xwjack.com";
}
- (NSString *)hook_licenseName {
return @"xwjack";
}
+ (void)load {
NSLog(@"XWJACK Begin hook LicenseBindingsModel");
Class hookClass = objc_getClass("Reveal.LicenseBindingsModel");
xwjack_hookMethod(hookClass, @selector(isCommercialLicense), self.class, @selector(hook_isCommercialLicense));
xwjack_hookMethod(hookClass, @selector(licensePeriodExpired), self.class, @selector(hook_licensePeriodExpired));
xwjack_hookMethod(hookClass, @selector(upgradeLicenseMenuItemVisible), self.class, @selector(hook_upgradeLicenseMenuItemVisible));
xwjack_hookMethod(hookClass, @selector(licenseEmail), self.class, @selector(hook_licenseEmail));
xwjack_hookMethod(hookClass, @selector(licenseName), self.class, @selector(hook_licenseName));
}

@end

该类由于需要被OC使用,所以继承为NSObject,如果是纯Swift写hook可以参考Refrences的内容。

macOS调试配置


这里指定了当前生成的库会Attach到哪个程序。

实例

macOS

Surge

+[NSA load]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void +[NSA load](void * self, void * _cmd) {
rax = sub_1000a860f();
rax = [rax retain];
rdi = *0x1003a4338;
*0x1003a4338 = rax;
[rdi release];
if (*0x1003a4338 != 0x0) {
rdi = *0x1003a4338;
sub_1000a890f();
sub_1000a89a3(rdi);
}
rax = [NSA sharedInstance];
objc_unsafeClaimAutoreleasedReturnValue(rax);
return;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void sub_1000a860f() {
rbx = [[KDStorageHelper applicationSupportDirectoryPathWithName:@"com.nssurge.surge-mac"] retain];
r15 = malloc(0x19000);
r14 = objc_retainAutorelease(rbx);
rax = [r14 UTF8String];
rbx = 0x0;
rax = getxattr(rax, "com.nssurge.surge-mac.nsa.3", r15, 0x19000, 0x0, 0x0);
if (rax > 0x0) {
rcx = rax;
r12 = [[NSData dataWithBytes:r15 length:rcx, 0x0] retain];
free(r15);
rbx = [[r12 KD_JSONObject] retain];
[r12 release];
}
[r14 release];
rdi = rbx;
[rdi autorelease];
return;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
- (id)hook_KD_JSONObject {
NSDictionary *dict = [self hook_KD_JSONObject];
NSMutableDictionary *newDic = [NSMutableDictionary dictionaryWithDictionary:dict];

NSTimeInterval newTime = 2524582861;
if (dict[@"expiresOnDate"]) {
newDic[@"expiresOnDate"] = @(newTime);
}

NSLog(@"XWJACK %@", newDic);

return [newDic copy];
}

policy:对deviceID、expiresOnDate等数据base64得到
sign:对policy的签名再base64

  • deviceID:设备ID
  • expiresOnDate:过期时间
  • issueDate:好像是用来判断下次请求的时间
  • type:类型(trial)没有trial意味着是已经激活的
  • enterprise(sub_1000a6e27)
    ….
1
2
3
4
5
6
7
int sub_1000a6e27() {
rbx = [[*0x1003a4338 objectForKeyedSubscript:@"enterprise"] retain];
r14 = [rbx boolValue];
[rbx release];
rax = sign_extend_64(r14);
return rax;
}

那怎么得到这个东西呢?

  1. sub_1000a6e7a发送网络请求https://www.surge-activation.com/mac/v3/init/得到
  2. 本地获取

真正核心部分是sub_1000f5bcc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
int sub_1000f5bcc(int arg0, int arg1) {
r12 = arg0;
rbx = [arg0 retain];
var_48 = [arg1 retain];
*(int32_t *)0x1003a4490 = 0x0;
r13 = [[rbx objectForKeyedSubscript:@"policy"] retain];
if (r13 != 0x0) {
var_60 = r12;
r14 = [_objc_msgSend(rbx, _objc_release) retain];
r12 = rbx;
rbx = _objc_release;
[r14 release];
[r13 release];
if (r14 != 0x0) {
r15 = _objc_release;
rsi = rbx;
rbx = [NSData alloc];
r13 = [_objc_msgSend(r12, rsi) retain];
var_30 = 0x0;
rbx = [rbx initWithBase64EncodedString:@"policy" options:0x0];
[r13 release];
r13 = [NSData alloc];
var_68 = r12;
rdi = r12;
r12 = rbx;
rbx = [[rdi objectForKeyedSubscript:@"sign"] retain];
rcx = 0x0;
rdi = r13;
rsi = @selector(initWithBase64EncodedString:options:);
rdx = rbx;
r14 = _objc_msgSend(rdi, rsi);
[rbx release];
if ((r12 != 0x0) && (r14 != 0x0)) {
rsi = r14;
/// 这部分是重点(校验签名)
if (sub_1000f5f84(r12, rsi) != 0x0) {
var_38 = r14;
rax = [r12 KD_JSONObject];
rax = [rax retain];
var_40 = rax;
r15 = @selector(objectForKeyedSubscript:);
r14 = [_objc_msgSend(rax, r15, @"deviceID") retain];
rbx = [sub_1000f611e() retain];
rsi = @selector(isEqualToString:);
var_58 = r14;
rdx = rbx;
r14 = _objc_msgSend(r14, rsi, rdx);
[rbx release];
if (r14 != 0x0) {
[var_48 timeIntervalSince1970];
var_30 = intrinsic_movsd(var_30, xmm0);
rbx = var_40;
r14 = [_objc_msgSend(rbx, r15, @"type") retain];
rcx = 0x1;
var_50 = r14;
/// 从这里我们知道type类型要么为trial要么没有
if ([r14 isEqualToString:@"trial"] == 0x0) {
rcx = 0x2;
if ([r14 isEqualToString:@"licensed"] == 0x0) {
rdx = @"revoked";
rsi = @selector(isEqualToString:);
rcx = 0x3;
if (_objc_msgSend(r14, rsi) != 0x0) {
r14 = intrinsic_cvttsd2si(r14, var_30);
*(int32_t *)0x1003a4490 = rcx;
rdx = @"expiresOnDate";
rbx = [_objc_msgSend(rbx, r15, rdx, rcx) retain];
*0x1003a4498 = [rbx longValue];
[rbx release];
if ((*0x1003a4498 < r14) && (*(int32_t *)0x1003a4490 != 0x3)) {
if (*0x1003a4498 != 0x0) {
*(int32_t *)0x1003a4490 = 0x4;
}
}
*(int32_t *)0x1003a6f14 = 0x2;
rsi = var_60;
objc_storeStrong(0x1003a44a0, rsi);
var_30 = 0x1;
r14 = var_38;
rbx = var_40;
}
else {
var_30 = 0x0;
r14 = var_38;
}
}
else {
r14 = intrinsic_cvttsd2si(r14, var_30);
*(int32_t *)0x1003a4490 = rcx;
rdx = @"expiresOnDate";
rbx = [_objc_msgSend(rbx, r15, rdx, rcx) retain];
*0x1003a4498 = [rbx longValue];
[rbx release];
if ((*0x1003a4498 < r14) && (*(int32_t *)0x1003a4490 != 0x3)) {
if (*0x1003a4498 != 0x0) {
*(int32_t *)0x1003a4490 = 0x4;
}
}
*(int32_t *)0x1003a6f14 = 0x2;
rsi = var_60;
objc_storeStrong(0x1003a44a0, rsi);
var_30 = 0x1;
r14 = var_38;
rbx = var_40;
}
}
else {
r14 = intrinsic_cvttsd2si(r14, var_30);
*(int32_t *)0x1003a4490 = rcx;
rdx = @"expiresOnDate";
rbx = [_objc_msgSend(rbx, r15, rdx, rcx) retain];
*0x1003a4498 = [rbx longValue];
[rbx release];
if ((*0x1003a4498 < r14) && (*(int32_t *)0x1003a4490 != 0x3)) {
if (*0x1003a4498 != 0x0) {
*(int32_t *)0x1003a4490 = 0x4;
}
}
*(int32_t *)0x1003a6f14 = 0x2;
rsi = var_60;
objc_storeStrong(0x1003a44a0, rsi);
var_30 = 0x1;
r14 = var_38;
rbx = var_40;
}
[var_50 release];
}
else {
var_30 = 0x0;
r14 = var_38;
rbx = var_40;
}
r15 = _objc_release;
[var_58 release];
[rbx release];
}
else {
var_30 = 0x0;
}
}
(r15)(r14, rsi, rdx, rcx);
(r15)(r12, rsi, rdx, rcx);
rbx = var_68;
r15 = var_30;
}
else {
r15 = 0x0;
rbx = r12;
}
}
else {
r15 = 0x0;
}
[var_48 release];
[rbx release];
rax = r15 & 0xff;
return rax;
}

由于里面签名过于复杂,就不一一hook了,直接hook这个方法进行返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int sub_1000f5f84(int arg0, int arg1) {
var_38 = [arg0 retain];
var_50 = [arg1 retain];
r13 = sub_100214220();
rbx = objc_retainAutorelease([sub_10012da6c() retain]);
var_48 = rbx;
r12 = [rbx bytes];
rax = [rbx length];
rax = sub_10020ac20(r12, rax);
var_30 = rax;
rbx = sub_10023dff0(rax, 0x0, 0x0, 0x0);
r12 = sub_10021b890();
sub_10021ba80(r12, 0x6, rbx);
rax = sub_10021af30();
sub_100221c80(r13, 0x0, rax, 0x0, r12);
rbx = objc_retainAutorelease(arg0);
r15 = [rbx bytes];
rbx = [rbx length];
[var_38 release];
sub_1002144a0(r13);
rbx = objc_retainAutorelease(arg1);
r15 = [rbx bytes];
rbx = [rbx length];
[var_50 release];
*(int32_t *)0x1003a6f1c = sub_100221e90(r13, r15, rbx);
sub_1002149a0(r13);
sub_10021be30(r12);
sub_1002099f0(var_30);
COND = *(int32_t *)0x1003a6f1c == 0x1;
[var_48 release];
rax = COND ? 0x1 : 0x0;
return rax;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#import "substrate.h"
#import <mach-o/dyld.h>

static int (*ori_sub_1000f5ff84)(int, int) = NULL;
static int hook_sub_1000f5ff84(int arg0, int arg1) {
NSLog(@"XWJACK Hook sign check.");
// int oriResult = ori_sub_1000f5ff84(arg0, arg1);
return 1;
}

static void __attribute__((constructor)) initialize(void) {
NSLog(@"XWJACK Begin hook c function");
MSHookFunction((void*)(0x00000001000f5f84 + _dyld_get_image_vmaddr_slide(0)), (void *)&hook_sub_1000f5ff84, (void *)&ori_sub_1000f5ff84);
}

其中无意间发现好像有一个公钥和私钥都在本地写死的。

Reveal

This copy of Reveal is damaged

Reveal会校验签名,如果签名不对,会弹出提示。

  1. 从资源文件Localizable.strings中我们找到:
    “IBAApplicationIsDamagedAlert.messageText” = “This copy of Reveal is damaged”;

sub_1001ff4a0()
sub_1001ff7f0()

1
2
3
4
5
6
void sub_1001ff7f0() {
r15 = sub_1001ff4a0();
/// 弹出模态框
rax = [r15 runModal];
*0x10051b730 = *0x10051b730 + 0x2;
if (rax != 0x3e8) goto loc_1001ff936;

sub_1001ff950()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int sub_1001ff950() {
rbx = r12;
*0x100522518 = *0x100522518 + 0x1;
rax = sub_1003a4c76();
rax = $S10ObjectiveC22_convertObjCBoolToBoolySbAA0cD0VF(LOBYTE(rax) & 0xff);
/// 从这里我们确定rax的值需要为0x1才不会走else方法。
if ((LOBYTE(rax) & 0x1) != 0x0) {
r12 = rbx;
}
else {
*0x100522520 = *0x100522520 + 0x1;
r14 = [[swift_getInitializedObjCClass(@class(IBAAnalytics)) sharedInstance] retain];
rbx = $SSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF(0x8000000000000000 | "Code Signing Verification Failed", 0x20);
rax = [r14 recordEvent:rbx count:0x1];
rax = [rbx release];
rax = [r14 release];
rax = sub_1001ff7f0();
if (*0x100513078 == 0x0) {
*0x100513078 = sub_10003d530();
}
LODWORD(rdx) = 0x0;
LODWORD(rcx) = 0x0;
rax = swift_allocError(0x1004779f0);
r12 = rax;
}
rbx = stack[2045];
r14 = stack[2046];
rsp = rsp + 0x18;
rbp = stack[2047];
return rax;
}

hopper修改

sub_1003a4c76()
mov rax, 0x1其它全部nop掉

代码
1
2
3
4
5
6
7
8
9
10
11
static int (*ori_sub_1003a4c76)(void);
static int hook_sub_1003a4c76() {
NSLog(@"XWJACK Hook sign check.");
return 1;
}

static void __attribute__((constructor)) initialize(void) {
NSLog(@"XWJACK Begin hook c function");

MSHookFunction((void*)(0x00000001003a4c76 + _dyld_get_image_vmaddr_slide(0)), (void *)&hook_sub_1003a4c76, (void *)&ori_sub_1003a4c76);
}

Reveal.ActivationWindowController: DMActivationController

激活控制器(_stepMap中包含了所有的弹窗)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Printing description of self->_stepsMap:
{
0 = "<DMWelcomeStepController: 0x600003705680>";
101 = (
"Reveal.AnnualSupportApplicationBuildTooNewWelcomeStepController",
AnnualSupportApplicationBuildTooNewWelcomeStepController
);
7 = (
DMEmbeddedStoreStepController
);
5 = (
DMExpirationStepController
);
3 = (
DMSuccessStepController
);
"-1" = (
DMStepController
);
1 = (
DMActivationStepController
);
102 = (
"Reveal.AnnualSupportStatusWelcomeStepController",
AnnualSupportStatusWelcomeStepController
);
100 = (
IBATrialLicenseWelcomeStepController,
IBATrialLicenseWelcomeStepView
);
6 = (
DMDeactivationStepController
);
4 = (
"Reveal.AnnualSupportFailureStepController",
AnnualSupportFailureStepController
);
2 = (
DMAdditionalActivationStepController
);
}

先打印一下DMActivationController方法(Hopper中无法看到)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
(lldb) pmethods -n DMActivationController
Class Methods:
+ (id)currentTrialController
+ (id)timeTrialControllerForArea:(long long)arg0 timeInterval:(double)arg1 customWindowNib:(id)arg2
+ (id)manualTrialControllerForArea:(long long)arg0 callbacks:({__DMTrialCallbacks=q@?@?@?@?@?^v})arg1 customWindowNib:(id)arg2
+ (id)trialControllerWithArguments:({_DMTrialArguments=qq(?=d{__DMTrialCallbacks=q@?@?@?@?@?^v})})arg0 customWindowNib:(id)arg1
+ (void)registerActivationSchemesIfNeed
+ (void)startListeningForActivationStateChanges
+ (void)performBlockAfterAppLaunch:(@?)arg0 waitUntilDone:(char)arg1
+ (id)registeredSchemes:(id)arg0
+ (id)schemeRegistrationMap
+ (char)tryToHandleAppleEvent:(id)arg0 withReplyEvent:(id)arg1
+ (void)setDelegate:(id)arg0
+ (id)sharedController
+ (void)load
+ (id)defaultController

Instance Methods:
- ({__DMTrial=} *)trialObject
- (void)startTrial
- (void)runActivationWindowInMode:(unsigned long long)arg0 initialActivationInfo:(id)arg1 withCompletionHandler:(@?)arg2
- (void)endActivationWithResult:(unsigned long long)arg0
- (void)updateActivationInfoWithFastSpringOrder:(id)arg0
- (void)performActivationStepWithStep:(long long)arg0
- (id)privateActivator
- (id)allStepsStates
- (void)registerInitialSteps
- (void)registerStepController:(id)arg0 withNibName:(id)arg1 forActivationStep:(long long)arg2
- (void)setShowReason:(unsigned long long)arg0
- (unsigned long long)showReason
- (void)updateCompletionHandler:(@?)arg0
- (long long)updatedStepForWelcomeStep
- (void)postInitPreparations
- (void)setAnimationAnchor:(unsigned long long)arg0
- (id)com_devmate_LineSeparator
- (void)setActivationMode:(unsigned long long)arg0
- (id)stepsStateContainer
- (id)stepsMap
- (void)setNextPerformStep:(long long)arg0
- (void)updateStepControllerForCurrentStep
- (void)_registerStepInfo:(id)arg0 forActivationStep:(long long)arg1
- (void)getUrl:(id)arg0 withReplyEvent:(id)arg1
- (unsigned long long)activationMode
- (void)kevlarApplicationDidChangeActivationStatus:(id)arg0
- (id)currentStepController
- (long long)nextPerformStep
- (long long)confirmedNextPerformStep:(long long)arg0
- (id)stepControllerForStep:(long long)arg0
- (void)setCurrentStep:(long long)arg0
- (void)setCurrentStepController:(id)arg0
- (unsigned long long)animationAnchor
- (void)_correctStepControllerView:(id)arg0
- (void)DMActivationController_windowWillClose:(id)arg0
- (id)activator
- (void)registerStepController:(id)arg0 forActivationStep:(long long)arg1
- (void)registerForScheme:(id)arg0
- (void)setActivator:(id)arg0
- (void)setCom_devmate_LineSeparator:(id)arg0
- (void)setStepsMap:(id)arg0
- (void)setStepsStateContainer:(id)arg0
- (void)showActivationWindowIfNeeds
- (void).cxx_destruct
- (id)delegate
- (void)setDelegate:(id)arg0
- (void)dealloc
- (id)initWithCoder:(id)arg0
- (id)initWithWindow:(id)arg0
- (char)isRunning
- (id)containerView
- (void)setContainerView:(id)arg0
- (void)setCompletionHandler:(@?)arg0
- (@?)completionHandler
- (void)windowDidLoad
- (long long)currentStep

showActivationWindowIfNeeds

1
2
3
4
5
6
7
8
9
10
11
12
/// 直接hook掉
@implementation NSObject (XWJACK_DMActivationController)
- (void)hook_showActivationWindowIfNeeds {
NSLog(@"🍇 XWJACK Prevent %s", _cmd);
// [self hook_showActivationWindowIfNeeds];

}
+ (void)load {
Class H_DMActivationController = objc_getClass("DMActivationController");
xwjack_hookMethod(H_DMActivationController, @selector(showActivationWindowIfNeeds), self.class, @selector(hook_showActivationWindowIfNeeds));
}
@end

theos基本语法

1
2
3
4
5
6
7
8
9
10
11
%hook ClassName
- (void)logEvent:(id)arg1 infos:(id)arg2 details:(id)arg3 {
/// 相当于super调用
%orig;
id result = %orig;
%orig()
%orig(arg1, arg2, arg3);
/// 打印日志
%log;
}
%end

xctemplate

为了简化步骤,所以写了一个Reverse_xctemplates

Refrences