近期實戰了一次 IDA + Hopper 逆向破解。講真,第一次體驗了一回把別人“衣服”扒光了的快感~簡直 High 翻~所以,特此,利用 AlipayWallet 總結分享一下 IDA 和 Hopper 的基本使用。希望對大家有幫助。
先回顧一下,之前兩篇文章已經學習的內容:
- 獲得一臺越獄設備
- 利用 SSH 連接訪問越獄設備
- 利用 Clutch 解密砸殼
- 利用 class-dump 導出應用頭文件
- 利用 Cycript 進行應用運行時的動態分析與修改
這麽一看確實學了不少技能,而接下來這篇文章,會簡單介紹兩個反編譯的利器 IDA 和 Hopper 的使用。
註,本次實驗利用的並不是最新版本的支付寶 Mach-O 文件,因此可能本次實驗中涉及的函數方法在最新版本中無法找到,但由於本文章只作為學習,所以,只提供具體的方法。如果有需要,可自行砸殼獲得。
動態分析與靜態分析
動態分析
我們前兩篇學習的 Cycript ,就是典型的動態分析工具。Cycript 可以在應用進行運行時方法分析,視圖層級分析等操作。而動態分析除了 Cycript 以外,常用的工具還有 lldb & debugserver 遠程斷點調試,logify 追蹤等,以後一起慢慢學習這些技能方法。
靜態分析
Static program analysis is the analysis of computer software that is performed without actually executing programs (analysis performed on executing programs is known as dynamic analysis).[1] In most cases the analysis is performed on some version of the source code, and in the other cases, some form of the object code.
以上是 Wikipedia 對靜態分析的英文介紹,中文翻譯如下。
靜態程序分析是指,沒有實際執行程序的電腦軟件分析方法(與之相對的是,被人們所知的實際執行軟件的動態分析)。在絕大部分的例子中,分析是運行在源代碼的某些版本,而其余則是運行在 目標代碼 中。
之前學習的 class-dump 工具導出 Mach-O 頭文件,就是對軟件應用進行靜態分析。而今天我們要學習的兩個工具,IDA 和 Hopper 反匯編二進制文件同樣也是靜態分析的方法。
IDA 的基本使用
什麽是 IDA
IDA is the Interactive DisAssembler: the world’s smartest and most feature-full disassembler, which many software security specialists are familiar with.
IDA 是世界上最敏捷和多功能的反編譯工具,被眾多軟件安全專家所熟知的交互的反匯編工具。
安裝 IDA
IDA 的官方網站是 https://www.hex-rays.com/ 。在網頁上可以看到 IDA 的插件、SDK 等內容。IDA 會在官網提供 Demo 版的下載 https://www.hex-rays.com/products/ida/support/download_demo.shtml 。然而, Demo 版沒有 IDA 最強大的反匯編功能,神器 F5~!如果自己學習使用正版,很難承擔巨額的證書費用,所以一般情況是在網上找破解版(囧。。並不推薦,土豪還是建議買證書)。很不幸的是 IDA Pro 的 Mac 版非常難找(我是沒找到)。所以,本文是在 Windows 下找破解 IDA Pro 來學習。
IDA Pro 的使用
打開 IDA 會出現 About 和 Support message 等提示對話框,點擊 OK 繼續後彈出 Quick Start 對話框,在這個對話框裏可以看到之前打開過的 IDA 反匯編二進制文件,也可以新建一個新的反匯編文件。這裏我們點擊 New 新建一個反匯編,這次以支付寶為例。
準備 Mach-O
回顧之前的內容,先進行 Clutch 砸殼,然後 scp AlipayWallet 到 Mac 目錄下,再利用 class-dump 導出頭文件。
導入 IDA
這裏可以直接將 Mach-O 文件拖拽進 IDA 的工作區,也可以點擊打開文件夾的圖標將 Mach-O 導入。檢測到 Objective-C 2.0 代碼時會提示,點擊 OK 繼續即可。
之後就是漫長的等待~~~(Hopper 相對於 IDA 等待的時間會短一些,但是反編譯結果沒有 IDA 更接近真實的 C 語言代碼,會包含很多寄存器變量)
認識 IDA 工作區
當 IDA 對 Mach-O 解析完成後,會默認出現 6 個選項卡視圖,分別是 匯編視圖,16 進制字節視圖,結構體視圖,枚舉類型視圖,導入的函數視圖,導出的函數視圖 。
另外,紅框標註的是工具欄,藍框標註的是二進制文件解析的進度,在藍框下面,用不同顏色區分庫函數、數據、常規函數等不同類型的解析數據。
靜態分析:還原源代碼
使用 IDA 反匯編二進制文件的目的是,利用工具得到反匯編之後的偽代碼,還原出真正的程序源碼。比如,我們如果要看一下登錄方法是如何寫的,可以在已經導出的 AlipayWallet.h 中查詢 login 關鍵字,並查找相關代碼,我們發現會有 LoginProtocol 協議和 LoginAdapter 類的相關代碼,那我們不妨就拿 LoginAdapter 這個類作為實戰研究對象。
@protocolLoginProtocol<NSObject>
+ (id)sharedInstantce;
- (NSDictionary *)currentSession;
- (void)loginWithLoginOption:(int)arg1 extraInfo:(NSDictionary *)arg2 completionHandler:(void (^)(BOOL, NSDictionary *))arg3 cancelationHandler:(void (^)(void))arg4;
- (void)loginWithLoginOption:(int)arg1 completionHandler:(void (^)(BOOL, NSDictionary *))arg2 cancelationHandler:(void (^)(void))arg3;
- (BOOL)isValidLogin;
@optional
- (void)logout;
- (void)markInvalidLogin;
- (BOOL)isProcessingLogin;
@end
@interfaceLoginAdapter:NSObject<LoginProtocol>
{
int _is_login_doing;
int _is_processing_pending;
id <LoginProtocol> _Network_config;
id <LoginProtocol> _login_service;
NSMutableArray *_loginPendingRequests;
NSRecursiveLock *_pending_lock;
}
+ (id)sharedInstantce;
@property(retain, nonatomic) NSRecursiveLock *pending_lock; // @synthesize pending_lock=_pending_lock;
@property(retain, nonatomic) NSMutableArray *loginPendingRequests; // @synthesize loginPendingRequests=_loginPendingRequests;
@property(retain, nonatomic) id <LoginProtocol> login_service; // @synthesize login_service=_login_service;
@property(retain, nonatomic) id <LoginProtocol> network_config; // @synthesize network_config=_network_config;
- (void).cxx_destruct;
- (void)storeSessionWithLoginResult:(id)arg1;
- (id)currentUserId;
- (void)notifyNetworkSDK:(id)arg1;
- (void)logout:(id)arg1;
- (void)logined:(id)arg1;
- (void)loadAlu:(Class)arg1;
- (void)loadLoginModule;
- (void)loadNetworSDKConfig;
- (void)failedPendingLoginRequests;
- (void)redoPendingLoginRequests;
- (void)pendingLoginRequest:(id)arg1;
- (void)releasePendingLock;
- (void)accquirePendingLock;
- (void)releaseLoginLock;
- (BOOL)accquireLoginLock;
- (int)tryLogin:(id)arg1 isForce:(BOOL)arg2;
- (int)tryLogin:(id)arg1;
- (void)logout;
- (id)currentSession;
- (int)loginWithLoginOption:(int)arg1 isForce:(BOOL)arg2 extraInfo:(id)arg3 completionHandler:(CDUnknownBlockType)arg4 cancelationHandler:(CDUnknownBlockType)arg5 request:(id)arg6;
- (void)loginWithLoginOption:(int)arg1 extraInfo:(id)arg2 completionHandler:(CDUnknownBlockType)arg3 cancelationHandler:(CDUnknownBlockType)arg4;
- (void)loginWithLoginOption:(int)arg1 completionHandler:(CDUnknownBlockType)arg2 cancelationHandler:(CDUnknownBlockType)arg3;
- (BOOL)isValidLogin;
- (BOOL)isProcessingLogin;
- (void)markInvalidLogin;
- (id)setCustomLoginModule:(id)arg1;
- (void)dealloc;
- (id)init;
// Remaining properties
@property(readonly, copy) NSString *debugDescription;
@property(readonly, copy) NSString *description;
@property(readonly) unsigned int hash;
@property(readonly) Class superclass;
@end
在頭文件裏查找可以得到上面結果,以 LoginAdapter 為例。我們可以在 function 窗口中按住 Ctrl+F 鍵查找 LoginAdapter 的相關函數。
雙擊 [LoginAdapter sharedInstan] 到達這個函數在二進制文件中的內存地址,按 F5 可以就查看這個反編譯的偽碼。
這裏可以看一下登錄方法 Alipay 是如何寫的,雙擊 [LoginAdapter loginWithLoginOption:isForce:extraInfo:completionHandler:cancelationHandler:request:] ,按 F5 鍵查看這個方法的反編譯偽代碼。偽代碼如下。
// LoginAdapter - (int)loginWithLoginOption:(int) isForce:(char) extraInfo:(id) completionHandler:(id) cancelationHandler:(id) request:(id)
int __cdecl -[LoginAdapter loginWithLoginOption:isForce:extraInfo:completionHandler:cancelationHandler:request:](struct LoginAdapter *self, SEL a2, int a3, char a4, id a5, id a6, id a7, id a8)
{
struct LoginAdapter *v8; // r8@1
// some variarble
v8 = self;
v9 = a4;
v37 = a3;
v39 = objc_retain(a5, a2);
v11 = objc_retain(a6, v10);
v13 = objc_retain(a7, v12);
v15 = (void *)objc_retain(a8, v14);
if ( !v8->_login_service )
{
v18 = 0;
v19 = v39;
goto LABEL_27;
}
v40 = v11;
if ( v15 )
objc_msgSend(v8, "accquirePendingLock");
if ( (unsigned int)objc_msgSend(v8, "accquireLoginLock") & 0xFF )
{
if ( v9 || !((unsigned int)objc_msgSend(v8, "isValidLogin") & 0xFF) )
{
v36 = v13;
if ( v15 )
objc_msgSend(v8, "pendingLoginRequest:", v15);
v20 = objc_msgSend(&OBJC_CLASS___LogAdapter, "getInstance");
v21 = (void *)objc_retainAutoreleasedReturnValue(v20);
v38 = v8;
v22 = objc_msgSend(v15, "getApiName");
v23 = objc_retainAutoreleasedReturnValue(v22);
v24 = objc_msgSend(v15, "getApiVersion");
v25 = objc_retainAutoreleasedReturnValue(v24);
v26 = v25;
v27 = objc_msgSend(
&OBJC_CLASS___NSString,
"stringWithFormat:",
CFSTR("[LoginAdapter] apiName: %@, apiVersion: %@ pull login module"),
v23,
v25);
v28 = objc_retainAutoreleasedReturnValue(v27);
objc_msgSend(v21, "warn:", v28);
objc_release(v28);
objc_release(v26);
objc_release(v23);
objc_release(v21);
v29 = v38->_login_service;
v48 = &_NSConcreteStackBlock;
v49 = -1040187392;
v50 = 0;
v51 = sub_2AAF082;
v52 = &unk_3164640;
v30 = objc_retain(v38, sub_2AAF082);
v53 = v30;
v54 = objc_retain(v40, v31);
v41 = &_NSConcreteStackBlock;
v42 = -1040187392;
v43 = 0;
v44 = sub_2AAF1AC;
v45 = &unk_3164660;
v13 = v36;
v46 = objc_retain(v30, &unk_3164660);
v19 = v39;
v47 = objc_retain(v36, v32);
objc_msgSend(v29, "loginWithLoginOption:extraInfo:completionHandler:cancelationHandler:", v37, v39, &v48, &v41);
objc_release(v47);
objc_release(v46);
objc_release(v54);
objc_release(v53);
v18 = 2;
goto LABEL_24;
}
objc_msgSend(v8, "releaseLoginLock");
if ( v11 )
{
v38 = v8;
v16 = objc_msgSend(v8->_login_service, "currentSession");
v17 = objc_retainAutoreleasedReturnValue(v16);
(*(void (__fastcall **)(int, signed int, int))(v11 + 12))(v11, 1, v17);
objc_release(v17);
}
else
{
v38 = v8;
}
v18 = 3;
}
else
{
if ( v15 )
{
if ( (unsigned int)objc_msgSend((void *)v8->_loginPendingRequests, "count") > 0xFF )
{
v38 = v8;
v18 = 0;
}
else
{
v38 = v8;
objc_msgSend(v8, "pendingLoginRequest:", v15);
v18 = 1;
}
}
else
{
v38 = v8;
v18 = 0;
}
if ( v11 )
{
v33 = objc_msgSend(&OBJC_CLASS___NSDictionary, "dictionary");
v34 = objc_retainAutoreleasedReturnValue(v33);
v40 = v11;
(*(void (__fastcall **)(int, _DWORD, int))(v11 + 12))(v11, 0, v34);
objc_release(v34);
}
else
{
v40 = 0;
}
}
v19 = v39;
LABEL_24:
if ( v15 )
objc_msgSend(v38, "releasePendingLock");
v11 = v40;
LABEL_27:
objc_release(v15);
objc_release(v13);
objc_release(v11);
objc_release(v19);
return v18;
}
反編譯後的偽代碼會保持原有函數的邏輯和函數的符號名,接下來,我們就可以嘗試還原這段代碼的 Objective-C 源代碼。
創建工程
創建一個空項目,並創建 LoginAdapter 類的頭文件和實現文件,然後把 class-dump 導出的頭文件的 property 和共有的方法復制在新創建的頭文件中,然後根據反匯編碼還原原代碼。
註意,在這個例子中, CDUnknownBlockType 是一個 block 類型,但是具體的 block 類型,class-dump 不能導出,如果想得到這個 block 可參考
- Hook https://github.com/iosre/SMSNinja/blob/master/libsmsninja/Hook.xm#L754
- Deprecated https://github.com/iosre/SMSNinja/blob/master/libsmsninja/Deprecated.xm#L139
方法名和參數
我們知道, Objective-C 中的 objc_msgSend 方法是向消息接受者(實例對象)發送一條消息,蘋果文檔如下。
**objc_msgSend**
Sends a message with a simple return value to an instance of a class.
id objc_msgSend(id self, SEL op, ...);
Parameters
self
A pointer that points to the instance of the class that is to receive the message.
op
The selector of the method that handles the message.
...
A variable argument list containing the arguments to the method.
從文檔可以得出,第一個參數是該接受消息類型實例的指針,第二個參數是方法選擇器(selector),之後是實例方法傳遞的參數。
而在反編譯得到的偽碼中,第一行就是 Objective-C 方法和實際 C 函數的參數對應,如下所示。
int __cdecl -[LoginAdapter loginWithLoginOption:isForce:extraInfo:completionHandler:cancelationHandler:request:](struct LoginAdapter *self, SEL a2, int a3, char a4, id a5, id a6, id a7, id a8)
因此,我們可以得出:
self 是當前實例指針,a2 對應方法選擇器,a3 對應方法參數 option; a4 對應方法參數 isForce; a5 對應方法參數 extraInfo; a6 對應方法參數 completionHandler; a7 對應方法參數 cancelationHandler; a8 對應方法參數 request。明確方法名和參數對應關系之後,我們接下來就開始還原函數體。
方法函數體
還原函數體的基本思路很簡單,就是利用偽代碼的方法名和變量,根據應用正向開發的經驗方法,還原函數體的具體邏輯。
對於函數體中涉及的其他類及相關協議協議,如, MtopExtRequest TBSDKRequest LogAdapter 等也可以利用 IDA 和頭文件獲得,具體還原過程這裏就不再贅述,還原函數體代碼如下。
#import "LoginAdapter.h"
#import "LogAdapter.h"
#import "MtopExtRequest.h"
@implementationLoginAdapter
- (NSInteger)loginWithLoginOption:(int)option
isForce:(BOOL)isForce
extraInfo:(NSDictionary *)extraInfo
completionHandler:(AlipayCompletionHandler)completionHandler
cancelationHandler:(AlipayCancelationHandler)cancelationHandler
request:(MtopExtRequest *)request {
// self 是當前實例指針,a2 對應方法選擇器,a3 對應方法參數 option; a4 對應方法參數 isForce; a5 對應方法參數 extraInfo;
// a6 對應方法參數 completionHandler; a7 對應方法參數 cancelationHandler; a8 對應方法參數 request
NSInteger returnValue = http://www.open-open.com/lib/view/0; // v18
if (!self.login_service) {
returnValue = 0;
}
if (request) {
[self accquirePendingLock];
}
if ([self accquireLoginLock]) {
if (extraInfo || ![self isValidLogin]) {
if (request) {
[self pendingLoginRequest:request];
}
LogAdapter *logAdapter = [LogAdapter getInstance];
NSString *apiName = [request getApiName];
NSString *apiVersion = [request getApiVersion];
NSString *logString = [NSString stringWithFormat:@"[LoginAdapter] apiName: %@, apiVersion: %@ pull login module", apiName, apiVersion];
[logAdapter warn:logString];
[self.login_service loginWithLoginOption:option
extraInfo:extraInfo
completionHandler:completionHandler
cancelationHandler:cancelationHandler];
} // extraInfo || ![self isValidLogin] end
[self releaseLoginLock];
if (completionHandler) {
NSDictionary *currentSession = [self.login_service currentSession];
completionHandler(1, currentSession);
} else {
// v38 = v8 即,對 self 進行 retain 操作,無需翻譯成 oc 代碼
}
returnValue = http://www.open-open.com/lib/view/3;
} else {
if (request) {
if (self.loginPendingRequests.count) {
returnValue = 0;
} else {
[self pendingLoginRequest:request];
returnValue = 1;
}
} else {
returnValue = 0;
}
if (completionHandler) {
completionHandler(0, [NSDictionary dictionary]);
} else {
completionHandler = nil;
}
}
if (request) {
[self releaseLoginLock];
}
return returnValue;
}
@end
如果想查看具體代碼,可到 GitHub 查看:
倉庫地址 https://github.com/niyaoyao/reverse-learning
Hopper 的基本使用
對於 Hopper 的學習,依然使用 AlipayWallet 這個文件進行分析,與 IDA 做比較,類比進行。
什麽是 Hopper
Hopper Disassembler, the reverse engineering tool that lets you disassemble, decompile and debug your applications.
Hopper 反匯編工具,是逆向工程的工具,能夠讓你進行反匯編、反編譯並且調試你的應用。
安裝 Hopper
Hopper 的官網是 https://www.hopperapp.com/ ,與 IDA 相同,官網也提供了 Demo 的試用版。試用版也可以進行反匯編,但是不能保存 *.hop 文件。所以,用於學習的話 Hopper 的 Demo 版就足夠了。
Hopper 的使用
導入 Hopper
與 IDA 不同, Hopper 不能直接將 Mach-O 文件導入工作區,需要下載應用的 *.ipa 文件,並將文件後綴改為 *.zip,解壓該 zip 文件後就可看到 Payload 文件目錄,在該目錄下就存有對應應用的 package 包文件。右擊該 package 出現菜單,選擇 Show Package Contents 選項,就可看到包內容。
認識 Hopper 工作區
與 IDA 類似, Hopper 也有二進制文件的解析進度條,左邊是符號 Label 等區域,中間是 ARM 的匯編代碼,右邊是相關信息欄。
靜態分析:Hopper 反匯編的偽代碼
如圖,在最左列的搜索欄中輸入 loginWithLoginOption 關鍵字,雙擊選擇 [LoginAdapter loginWithLoginOption:isForce:extraInfo:completionHandler:cancelationHandler:request:] 方法,在中間的匯編代碼區域,光標就會跳到該方法的內存地址處。
當 Hopper 分析完全部二進制後,按住 Option + Enter 鍵,就可看到 Hopper 反編譯後的偽代碼,如下所示。
int -[LoginAdapter loginWithLoginOption:isForce:extraInfo:completionHandler:cancelationHandler:request:](void * self, void * _cmd, int arg2, char arg3, void * arg4, void * arg5, void * arg6, void * arg7) {
stack[2048] = arg4;
r7 = (sp - 0x14) + 0xc;
sp = sp - 0x74;
r8 = self;
r5 = arg3;
stack[2051] = arg2;
stack[2053] = [arg4 retain];
r6 = [arg5 retain];
r11 = [arg6 retain];
r10 = [arg7 retain];
if (r8->_login_service == 0x0) goto loc_2aaee0a;
loc_2aaed6c:
stack[2054] = r6;
if (r10 != 0x0) {
[r8 accquirePendingLock];
}
if (([r8 accquireLoginLock] & 0xff) == 0x0) goto loc_2aaefa8;
loc_2aaeda0:
if (((r5 & 0xff) != 0x0) || (([r8 isValidLogin] & 0xff) == 0x0)) goto loc_2aaee10;
loc_2aaedbe:
[r8 releaseLoginLock];
r6 = stack[2054];
if (r6 != 0x0) {
stack[2052] = r8;
r5 = [[r8->_login_service currentSession] retain];
(*(r6 + 0xc))(r6, 0x1, r5, *(r6 + 0xc));
[r5 release];
}
else {
stack[2052] = r8;
}
r5 = 0x3;
goto loc_2aaf044;
loc_2aaf044:
r4 = stack[2053];
goto loc_2aaf046;
loc_2aaf046:
if (r10 != 0x0) {
[stack[2052] releasePendingLock];
}
r6 = stack[2054];
goto loc_2aaf060;
loc_2aaf060:
[r10 release];
[r11 release];
[r6 release];
[r4 release];
r0 = r5;
return r0;
loc_2aaee10:
stack[2050] = r11;
if (r10 != 0x0) {
[r8 pendingLoginRequest:r10];
}
r6 = [[LogAdapter getInstance] retain];
stack[2052] = r8;
r8 = [[r10 getApiName] retain];
r11 = [[r10 getApiVersion] retain];
r5 = [[NSString stringWithFormat:@"[LoginAdapter] apiName: %@, apiVersion: %@ pull login module", r8, r11] retain];
[r6 warn:r5];
[r5 release];
[r11 release];
[r8 release];
[r6 release];
r5 = [stack[2052] retain];
stack[2068] = [stack[2054] retain];
r11 = stack[2050];
stack[2060] = [r5 retain];
r4 = stack[2053];
stack[2061] = [r11 retain];
[stack[2052]->_login_service loginWithLoginOption:stack[2051] extraInfo:r4 completionHandler:sp + 0x38 cancelationHandler:sp + 0x1c, stack[2050], stack[2051], stack[2052], stack[2053], stack[2054], __NSConcreteStackBlock, 0xc2000000, 0x0];
[stack[2061] release];
[stack[2060] release];
[stack[2068] release];
[r5 release];
r5 = 0x2;
goto loc_2aaf046;
loc_2aaefa8:
if (r10 != 0x0) {
r6 = stack[2054];
if ([r8->_loginPendingRequests count] <= 0xff) {
stack[2052] = r8;
[r8 pendingLoginRequest:r10];
r5 = 0x1;
}
else {
stack[2052] = r8;
r5 = 0x0;
}
}
else {
stack[2052] = r8;
r5 = 0x0;
r6 = stack[2054];
}
if (r6 != 0x0) {
r4 = [[NSDictionary dictionary] retain];
stack[2054] = r6;
(*(r6 + 0xc))(r6, 0x0, r4, *(r6 + 0xc), stack[2048]);
[r4 release];
}
else {
stack[2054] = r6;
}
goto loc_2aaf044;
loc_2aaee0a:
r5 = 0x0;
r4 = stack[2053];
goto loc_2aaf060;
}
與 IDA 相比, Hopper 反編譯後的偽代碼的邏輯與 IDA 反編譯得到的偽代碼 邏輯類似 ,但多了 r0~r8 等寄存器,閱讀性相較而言差一些,但是,仍然可以根據偽代碼還原出源代碼。
其實我們可以用這個偽代碼來檢查我們之前還原的源代碼,保證還原代碼的正確性。
以上,便是 Hopper 的簡單使用。 Hopper 除了可以查看匯編代碼以外,還可以直接對 Mach-O 文件進行修改,然後重新生成二進制文件(Demo 版沒有這個功能)。
感興趣的同學可以閱讀這篇文章學習, 《如何讓 Mac 版微信客戶端防撤回》 http://t.cn/RxzeMIx
小結
本文主要學習了一下內容:
- IDA 的安裝及使用
- Hopper 的安裝及使用
- 靜態分析,根據偽碼還原源代碼
關於 iOS 安全領域還有很多技能要學習,以後還會繼續學習研究。比如,TheOS、 tweak、加密解密、越獄應用開發等,但是不著急慢慢學~
Where there’s a will, there’s a way!
Never forget your dream!
Fighting!
參考
- 如何讓 Mac 版微信客戶端防撤回 http://t.cn/RxzeMIx
- iOS 安全攻防 (十五) 使用 Hopper 修改字符串 http://www.cnblogs.com/jailbreaker/p/4183954.html
- iOS 逆向工程之 Hopper 中的 ARM 指令 http://www.cnblogs.com/ludashi/p/5740696.html
- IDA 反匯編/反編譯靜態分析 iOS 模擬器程序 http://blog.csdn.net/column/details/ios-ida.html
- 今天開始學逆向:用 Cycript 進行運行時分析及應用操作 https://niyaoyao.github.io/2016/11/01/Learning-Reverse-From-Today-D2/
- 今天開始學逆向:SSH 訪問越獄機與導出二進制文件的頭文件 https://niyaoyao.github.io/2016/11/01/Learning-Reverse-From-Today-D1/
來自:https://niyaoyao.github.io/2017/01/18/Learning-Reverse-From-Today-D3/
Tags: performed software computer actually programs
文章來源: