新的博客
学习笔记
前言
code
软件环境:Xcode
硬件环境:iPhone5越狱手机、Mac
开发工具: Cycript、LLDB、logos Tweak、hopper、MonkeyDev、AFLEXLoader、dumpdecrypted、debugserver、ssh、class_dump、hook
本文采用tweak 的方式进行MSHookFunction
ptrace这个函数很容易修改,几种破解方式
- 运行时期,断点ptrace,直接返回 :
初始化应用程序,而不是运行中附着
iPhone:~ root# debugserver -x posix *:12345 /var/mobile/Containers/Bundle/Application/A612F542-81EF-456A-A6A0-B23046EF57BA/AlipayWallet.app/AlipayWallet
初始化程序,目的是从程序入口就开始进行附着,这样我们就可以在一些安全防护代码执行之前,进行破解。 最常用的就是跳过ptrace:
过命令thread return直接返回,以跳过函数的逻辑。
(lldb) br set -n ptrace
Breakpoint 2: where = libsystem_kernel.dylib`__ptrace, address = 0x00000001966af2d4
(lldb) br command add 2
Enter your debugger command(s). Type 'DONE' to end.
> thread return
> c
> DONE
- 通过tweak,替换disable_gdb函数
- 在二进制文件中 ,修改 PT_DENY_ATTACH的31,改成 任意一个值,如PT_ATTACH 0.
iOS逆向 砸壳
iPhone:~ root# ps -e |grep AlipayWallet
714 ?? 0:26.44 /var/mobile/Containers/Bundle/Application/A612F542-81EF-456A-A6A0-B23046EF57BA/AlipayWallet.app/AlipayWallet
736 ttys000 0:00.01 grep AlipayWallet
iPhone:~ root# cycript -p AlipayWallet
cy# [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask][0]
#"file:///var/mobile/Containers/Data/Application/89313E1C-76C2-41E3-8ECD-F4BDC1A78524/Documents/"
devzkndeMacBook-Pro:decrypted devzkn$ scp /Users/devzkn/Downloads/kevin-software/ios-Reverse_Engineering/dumpdecrypted-master/dumpdecrypted.dylib iphone150:/var/mobile/Containers/Data/Application/89313E1C-76C2-41E3-8ECD-F4BDC1A78524/Documents/
devzkndeMacBook-Pro:decrypted devzkn$ scp iphone150:/var/mobile/Containers/Data/Application/89313E1C-76C2-41E3-8ECD-F4BDC1A78524/Documents/AlipayWallet.decrypted /Users/devzkn/decrypted/AlipayWallet
devzkndeMacBook-Pro:bin devzkn$ class-dump --arch armv7 /Users/devzkn/decrypted/AlipayWallet10.1.8/AlipayWallet.decrypted -H -o /Users/devzkn/decrypted/AlipayWallet10.1.8/head
定位目标app
TBSDKMTOPServer
bundleIdentifier
修改com.apple.springboard为iphoneclient
iPhone:~ root# cycript -p AlipayWallet
cy# [[NSBundle mainBundle] bundleIdentifier]
@"com.alipay.iphoneclient"
tweak
%hook DFClientDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
%log();
// 打印某个类的所有方法的,查看所有方法的执行顺序
[KNHook hookClass:@"H5WebViewController"];//aluLoginViewController
[KNHook hookClass:@"TBSDKServer"];//getUaPageName aluMTopService _tokenLoginInvoker
[KNHook hookClass:@"TBSDKMTOPServer"];//getUaPageName aluMTopService _tokenLoginInvoker
return %orig;
}
%end
Nov 28 18:36:20 iPhone AlipayWallet[1246] : KNHooklog :-(void)loadJs:(have 1 value)
return:(null)
value1:__NSCFString-->(function(){if(window.NEBULAHASADDEDTOUCHEVENT){return;};function onDOMReady(callback){var readyRE=/complete|loaded|interactive/;if(readyRE.test(document.readyState)){setTimeout(function(){callback();},1);}else{document.defaultView.addEventListener('DOMContentLoaded',function(){callback();},false);}}
onDOMReady(function(){document.addEventListener("touchstart",function(event){AlipayJSBridge.call("reportClickTime");},false);});window.NEBULAHASADDEDTOUCHEVENT=true;})();
object:
reportClickTime
通讯录朋友
Nov 28 18:46:07 iPhone AlipayWallet[1246] : KNHooklog :-(void)resetWebviewDomainLabelText:(have 1 value)
return:(null)
value1:NSURL-->https://render.alipay.com/p/f/fd-j6lzqrgm/addressbook.html?__webview_options__=canPullDown%3DNO%26showOptionMenu%3DNO%26transparent%3DNO%26networkIndicator%3DYES
object:
##########################################
https://render.alipay.com/p/f/fd-j6lzqrgm/addressbook.html?__webview_options__=canPullDown=NO&showOptionMenu=NO&transparent=NO&networkIndicator=YES
NSURLRequest *_originalHttpsRequest;
webView:shouldStartLoadWithRequest:navigationType
char -[AliH5WebViewController webView:shouldStartLoadWithRequest:navigationType:](void * self, void * _cmd, void * arg2, void * arg3, int arg4) {
else {
sub_e0afcc(*ivar_offset(_originalHttpsRequest) + r8, stack[2051]);
loc_e0afa4(@class(NSURLConnection), @selector(alloc), 0x39e724c);
r8->_urlConnection = loc_e0afa4();
loc_e0afa0(r8->_urlConnection, r8->_urlConnection);
loc_e0afa4(r8->_urlConnection, @selector(start));
r5 = 0x0;
}
goto loc_d14f42;
void -[H5WebViewController callExternNativeApi:](void * self, void * _cmd, void * arg2) {
r6 = sub_2a0e18c();
sub_2a0e184();
r5 = sub_2a0e188();
sub_2a0e184();
sub_2a0e180();
loc_2a0e1a4(r5, @selector(callExternNativeApi:webviewcontroller:), r6);
return;
}
总结
总结下去掉ptrace的思路:
- 当程序运行后,使用 debugserver *:1234 -a BinaryName 附加进程出现 segmentfault 11
时,一般说明程序内部调用了ptrace 。 - 为验证是否调用了ptrace 可以 debugserver -x backboard *:1234 /BinaryPath
(这里是完整路径),然后下符号断点 b ptrace,c 之后看ptrace第一行代码的位置,然后 p $lr 找到函数返回地址,再根据
image list -o -f 的ASLR偏移,计算出原始地址。最后在 IDA
中找到调用ptrace的代码,分析如何调用的ptrace。 - 开始hook ptrace。
步骤一:debugserver
iPhone:~ root# debugserver *:12345 -a AlipayWallet
debugserver-@(#)PROGRAM:debugserver PROJECT:debugserver-320.2.89
for armv7.
Attaching to process AlipayWallet...
Segmentation fault: 11
当程序运行后,使用 debugserver *:1234 -a BinaryName 附加进程出现 segmentfault 11 时,一般说明程序内部调用了ptrace 。
http://everettjf.com/2015/12/...
iPhone:~ root# debugserver *:12345 -x backboard /var/mobile/Containers/Bundle/Application/A612F542-81EF-456A-A6A0-B23046EF57BA/AlipayWallet.app/AlipayWallet
debugserver-@(#)PROGRAM:debugserver PROJECT:debugserver-320.2.89
for armv7.
Segmentation fault: 11
步骤二:分析如何调用的ptrace
debugserver -x
iPhone:~ root# debugserver -x *:12345 /var/mobile/Containers/Bundle/Application/A612F542-81EF-456A-A6A0-B23046EF57BA/AlipayWallet.app/AlipayWallet
error: invalid TYPE for the --launch=TYPE (-x TYPE) option: '*:12345'
Valid values TYPE are:
auto Auto-detect the best launch method to use.
posix Launch the executable using posix_spawn.
fork Launch the executable using fork and exec.
backboard Launch the executable through BackBoard Services.
总共有四种类型
debugserver -x backboard *:1234 /var/mobile/......
把这个backboard改成posix试试
iPhone:~ root# debugserver -x posix *:12345 /var/mobile/Containers/Bundle/Application/A612F542-81EF-456A-A6A0-B23046EF57BA/AlipayWallet.app/AlipayWallet
debugserver-@(#)PROGRAM:debugserver PROJECT:debugserver-320.2.89
for armv7.
Listening to port 12345 for a connection from *...
(lldb) process connect connect://127.0.0.1:12345
(lldb)
error: Process 1657 is currently being debugged, kill the process before connecting.
Process 1657 stopped
* thread #1, stop reason = signal SIGSTOP
frame #0: 0x1fe9b000 dyld`_dyld_start
dyld`_dyld_start:
-> 0x1fe9b000 <+0>: mov r8, sp
0x1fe9b004 <+4>: sub sp, sp, #16
0x1fe9b008 <+8>: bic sp, sp, #7
0x1fe9b00c <+12>: ldr r3, [pc, #0x70] ; <+132>
Target 0: (dyld) stopped.
- 在ptrace上下断点,找到调用ptrace的地方
(lldb) b ptrace
Breakpoint 1: no locations (pending).
WARNING: Unable to resolve breakpoint to any actual locations.
(lldb) c
Process 1657 resuming
关闭Target,重新启动Target
1 location added to breakpoint 1
Process 1657 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x37a13e64 libsystem_kernel.dylib`__ptrace
libsystem_kernel.dylib`__ptrace:
-> 0x37a13e64 <+0>: ldr r12, [pc, #0x4] ; <+12>
0x37a13e68 <+4>: ldr r12, [pc, r12]
0x37a13e6c <+8>: b 0x37a13e74 ; <+16>
0x37a13e70 <+12>: rsbeq r9, r11, #192, #2
Target 0: (AlipayWallet) stopped.
(lldb) p/x $lr
(unsigned int) $0 = 0x0000bfbb
由此可见ptrace函数在libsystem_kernel.dylib这个动态库中,使用时才进行加载,不是静态放在本地的,所以我们不能简单地去tweak ptrace函数。
(lldb) image list -o -f |grep AlipayWallet
[ 0] 0x00000000 /private/var/mobile/Containers/Bundle/Application/A612F542-81EF-456A-A6A0-B23046EF57BA/AlipayWallet.app/AlipayWallet(0x0000000000004000)
所以ptrace的调用者位于0x0000bfbb - 0x00000000 = 0x0000bfbb处,如图所示:
在hopper使用go to add ,快捷键G
0x0000bfba
这段代码的含义很明显了,动态调用ptrace,来达到反动态调试的目的。这段代码位于 sub_bf92:内部,我们来看看 sub_bf92:的显式调用者:
只有一个EnteryPoint+212,看看内部实现
高德地图通过在main函数中动态调用ptrace函数来达到反动态调试目的,可以参考学习。
支付宝现在做到的是通过inline函数调用ptrace
- 干掉sub_bf92(动态调用ptrace的地方)
基础知识
其中MSHookMessageEx负责用来hook Objective-C函数,
MSHookFunction负责用来hook C/C++函数。
- MSHookFunction实现Hook采用的是在内存中写入汇编指令来达到目的:
简单说来就是首先修改要Hook函数的前N个字节的内存,使其跳转到替换后的函数头,这样就能执行自己的代码;同时会保存原函数的前N个字节的内容以便执行完自己的逻辑后能正确执行原函数的逻辑。具体的汇编实现可以参考这篇分析文章。
void MSHookFunction(void *symbol, void *hook, void **old);
- 其中第一个参数为所要Hook的函数地址,值得注意的是该地址不一定限于函数头,也可以是函数内部的任一代码地址;
- 第二个参数为Hook后要替换的函数地址;
- 第三个参数为指向Hook地址的指针,用来保存被Hook函数替换掉的汇编指令方便执行完自己的代码逻辑后能够继续执行原函数的逻辑,若不需要调用原函数,则此处可以设为“NULL”
- 模版MSHookFunction
void *(*oldConnect)(int, const sockaddr *, socklen_t);
void *newConnect(
int socket, const sockaddr *address, socklen_t length
) {
if (address->sa_family == AF_INET) {
sockaddr_in *address_in = address;
if (address_in->sin_port == htons(6667)) {
sockaddr_in copy = *address_in;
address_in->sin_port = htons(7001);
return oldConnect(socket, ©, length);
}
}
return oldConnect(socket, address, length);
}
MSHookFunction(&connect, &newConnect, &oldConnect);
首先定义了一个名为oldConnect的指针用于保存要Hook函数被替换的指令;
然后实现了新的newConnect方法,即Hook后实际想要执行的代码逻辑;
最后使用MSHookFunction来对目标函数进行Hook。
第一个参数为原方法connect的地址,第二个参数为实现的新方法地址,第三个参数为用于保存connect被替换的汇编指令的地址。
其中“&connect”是MSHookFunction内部调用了CydiaSubstrate的另一个MSFindSymbol函数来实现根据函数名查找其函数地址,更多的MSHookFunction实现原理
此案例的hook prace 的代码
// See http://iphonedevwiki.net/index.php/Logos
//http://everettjf.com/2015/12/20/amap-ios-client-kill-anti-debugging-protect/
//https://segmentfault.com/a/1190000006104602
//http://dev.qq.com/topic/5791da152168f2690e72daa4
//https://bbs.pediy.com/thread-185014.htm
//http://www.rainyx.com/archives/category/ios/reverse
#import
#import
#import
void (*old_sub_bf92)(void);
void new_sub_bf92(void)
{
// old_sub_bf92();
NSLog(@"KNiOSRE: anti-anti-debugging");
}
//sub_bf92
%ctor
{
@autoreleasepool
{
unsigned long _sub_bf92 = (_dyld_get_image_vmaddr_slide(0) + 0xbf92) | 0x1;
if (_sub_bf92) NSLog(@"KNiOSRE: Found sub_bf92!");
MSHookFunction((void *)_sub_bf92, (void *)&new_sub_bf92, (void **)&old_sub_bf92);
}
}
编译打包安装,我们看看有了这个tweak的加持,动态调试的效果:
iPhone:~ root# debugserver *:12345 -a AlipayWallet
debugserver-@(#)PROGRAM:debugserver PROJECT:debugserver-320.2.89
for armv7.
Attaching to process AlipayWallet...
Listening to port 12345 for a connection from *...
成功
(lldb) process connect connect://127.0.0.1:12345
Process 1721 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x37a004f0 libsystem_kernel.dylib`mach_msg_trap + 20
libsystem_kernel.dylib`mach_msg_trap:
-> 0x37a004f0 <+20>: pop {r4, r5, r6, r8}
0x37a004f4 <+24>: bx lr
libsystem_kernel.dylib`mach_msg_overwrite_trap:
0x37a004f8 <+0>: mov r12, sp
0x37a004fc <+4>: push {r4, r5, r6, r8}
Target 0: (AlipayWallet) stopped.
Nov 28 21:53:46 iPhone kernel[0] : xpcproxy[1815] Container: /private/var/mobile/Containers/Data/Application/89313E1C-76C2-41E3-8ECD-F4BDC1A78524 (sandbox)
Nov 28 21:53:46 iPhone AlipayWallet[1815] : MS:Notice: Injecting: com.alipay.iphoneclient [AlipayWallet] (1141.14)
Nov 28 21:53:46 iPhone AlipayWallet[1815] : MS:Notice: Loading: /Library/MobileSubstrate/DynamicLibraries/AFlexLoader.dylib
Nov 28 21:53:46 iPhone AlipayWallet[1815] : AFlexLoader: injected successfully
Nov 28 21:53:46 iPhone AlipayWallet[1815] : MS:Notice: Loading: /Library/MobileSubstrate/DynamicLibraries/AlipayWalletTweakF.dylib
Nov 28 21:53:46 iPhone AlipayWallet[1815] : KNiOSRE: Found sub_bf92!
Nov 28 21:53:46 iPhone locationd[103] : Gesture EnabledForTopCLient: 0, EnabledInDaemonSettings: 0
Nov 28 21:53:46 iPhone AlipayWallet[1815] : MS:Notice: Loading: /Library/MobileSubstrate/DynamicLibraries/TSTweakEx.dylib
Nov 28 21:53:46 iPhone AlipayWallet[1815] : MS:Warning: nil class argument for selector applicationDidFinishLaunching:
Nov 28 21:53:46 iPhone AlipayWallet[1815] : KNiOSRE: anti-anti-debugging
H5WebViewController
cy# [#0xfc4e730 nextResponder]
#""
cy# [#0x537e600 h5WebView]
#">"
cy# [#0xfc4e240 delegate]
#""
cy# [#0x13019ea0 ]
[#""]
@property(retain, nonatomic) NSMutableDictionary *responseCallbacks; // @synthesize responseCallbacks=_responseCallbacks;
cy# [#0x13019ea0 webViewDelegate]
#""
通过tweak,替换disable_gdb函数 的第二种方式(更简单)
ptrace函数在libsystem_kernel.dylib这个动态库中,使用时才进行加载,不是静态放在本地的,所以我们不能简单地去tweak ptrace函数。所以我们分析一下其代码逻辑,一般这种反调试,代码是这么写的 :
void disable_gdb() {
void* handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);
ptrace_ptr_t ptrace_ptr = dlsym(handle, "ptrace");
ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);
dlclose(handle);
}
可以通过hook dlsym,当其调用时,如果是获取ptrace函数,则我们返回一个假的ptrace函数回去 , tweak如此编写 :
#import
#import
#import
int fake_ptrace(int request, pid_t pid, caddr_t addr, int data){
return 0;
}
void *(*old_dlsym)(void *handle, const char *symbol);
void *my_dlsym(void *handle, const char *symbol){
if(strcmp(symbol,"ptrace") == 0){
return (void*)fake_ptrace;
}
return old_dlsym(handle,symbol);
}
%ctor{
MSHookFunction((void*)dlsym,(void*)my_dlsym,(void**)&old_dlsym);
}
基本知识
操作系统提供了一种标准的服务来让程序员实现对底层硬件和服务的控制(比如文件系统),叫做系统调用(system calls)。
当一个程序需要作系统调用的时候,它将相关参数放进系统调用相关的寄存器,然后调用软中断0x80,这个中断就像一个让程序得以接触到内核模式的窗口,程序将参数和系统调用号交给内核,内核来完成系统调用的执行。