iOS-逆向11-代码注入

《iOS底层原理文章汇总》

1.运行上节课的WeChat程序,ViewController中的代码不会执行,因为MachO文件中的整个都被替换了,Product目录下WeChat.app中显示包内容,提取出WeChat.app中的可执行文件WeChat

iOS-逆向11-代码注入_第1张图片
image

I.通过MachOViewer分析WeChat可执行文件,由Mach64 Header、Load Commmands、sections、Function Starts、Data in Code Entries、Symbol Table、Dynamic Symbol Table、String Table、Code Signature这几部分组成

iOS-逆向11-代码注入_第2张图片
图片.png
iOS-逆向11-代码注入_第3张图片
图片.png

可执行文件由dyld进行加载,会读取Header查看是什么样的文件,Load Command中是一些细节信息,分为四大块_PAGEZERO、_TEXT、_DATA、_LINKEDIT,告诉dyld代码段和data段分别在哪


iOS-逆向11-代码注入_第4张图片
图片.png

LCMAIN指示main函数在哪儿,LOAD COMMAND知道几乎应用程序的所有信息


iOS-逆向11-代码注入_第5张图片
image

II.代码注入思路:WeChat执行WeChat可执行文件,还需要执行WeChat.app包中的frameworks和系统的UIKit,Foundation库等,WeChat.app中依赖包含的Framework andromeda也是一个MachO文件

iOS-逆向11-代码注入_第6张图片
image

andromeda通过MachOViewer查看,没有PAGEZERO


iOS-逆向11-代码注入_第7张图片
image

微信如何告诉dyld,要加载依赖的Framework的呢?andromeda等。Load COMMANDS中有指示dyld加载andromeda的路径


iOS-逆向11-代码注入_第8张图片
image

@rpath是工程根目录Frameworks目录,dyld根据Load Commands中的相应字段去指定的目录去加载framework,
程序启动dyld会根据Load Commands中的LC_LOAD_DYLIB列表去加载frameworks,一个一个去加载动态库

III.要注入代码,可以将要注入的代码包装在动态库中,即若LC_LOAD_DYLIB列表中有要注入的代码的动态库,则动态库就会被执行

A.Framework手动注入

1.在工程中新建动态库,build后显示WeChat.app中的包内容发现CloudHook动态库已经存在于WeChat.app包中

iOS-逆向11-代码注入_第9张图片
image

iOS-逆向11-代码注入_第10张图片
image

iOS-逆向11-代码注入_第11张图片
image

iOS-逆向11-代码注入_第12张图片
image
iOS-逆向11-代码注入_第13张图片
image

此时查看MachO可执行文件中发现LC_LOAD_DYLIB列表中并没有CloudHook,也就无法执行load方法中的内容


image

可以通过Xcode查看到CloudHook是一个动态库


iOS-逆向11-代码注入_第14张图片
image

要想注入代码生效,需要修改MachO可执行文件

2.通过工具yololib修改MachO可执行文件,再次查看LC_LOAD_DYLIB列表中已经存在CloudHook了

iOS-逆向11-代码注入_第15张图片
图片.png

图片.png

将修改后的MachO可执行文件放入Wechat.ipa中替换掉原来的,重新打包
⓵.将原始的微信8.0.2.ipa包解压到当前目录下生成Payload文件夹中包含WeChat.app包


iOS-逆向11-代码注入_第16张图片
image

⓶.用修改好的MachO文件替换原始解压后的WeChat中的MachO文件


iOS-逆向11-代码注入_第17张图片
图片.png

⓷.重新打包执行zip -ry WeChata.ipa Payload/生成WeChata.ipa


iOS-逆向11-代码注入_第18张图片
image

⓸.用生成的WeChata.ipa替换到原来工程中的微信8.0.2.ipa包,重新运行,注入成功

iOS-逆向11-代码注入_第19张图片
图片.png
图片.png
iOS-逆向11-代码注入_第20张图片
image

!!!注意:若运行时程序崩溃报错如下,libsystem_c.dylib strlen,则是Framework的版本号高于手机的系统,调低Framework Target中的版本号就好了


iOS-逆向11-代码注入_第21张图片
image

iOS-逆向11-代码注入_第22张图片
图片.png

B.Dylib注入

⓵.创建空工程,将微信8.0.2.ipa包放入工程目录下的新建的APP文件夹中

iOS-逆向11-代码注入_第23张图片
image

⓶.将shell脚本文件appSign.sh放入工程根目录下
iOS-逆向11-代码注入_第24张图片
image

⓷.将appSign.sh文件放入工程Build Phases目录Run Script下
iOS-逆向11-代码注入_第25张图片
image

iOS-逆向11-代码注入_第26张图片
image

⓸.build后,工程根目录下会生成temp文件夹里面有WeChat.app包
iOS-逆向11-代码注入_第27张图片
图片.png

⓹.添加Dylib
iOS-逆向11-代码注入_第28张图片
image

iOS-逆向11-代码注入_第29张图片
图片.png

iOS-逆向11-代码注入_第30张图片
image

⓺.在CloudHook的Build settings中修改Base SDK为iOS,签名Code Signing Identity改为iOS Developer
iOS-逆向11-代码注入_第31张图片
图片.png

iOS-逆向11-代码注入_第32张图片
image

⓻.拷贝文件到Frameworks中
iOS-逆向11-代码注入_第33张图片
image

iOS-逆向11-代码注入_第34张图片
image

iOS-逆向11-代码注入_第35张图片
image

iOS-逆向11-代码注入_第36张图片
image

iOS-逆向11-代码注入_第37张图片
图片.png

⓼.通过脚本修改MachO文件,将yololib拷贝到工程根目录下
iOS-逆向11-代码注入_第38张图片
image

⓽.修改appSign.sh,build Cloud Target和Dylib注入 Target两个Target,查看libCloudHook.dylib已经拷贝到Frameworks目录中,找到libCloudHook.dylib的路径拷贝到脚本中,通过yololib修改MachO可执行文件的路径
./yololib " APP_BINARY" "Frameworks/libCloudHook.dylib",保存修改后的appSign.sh
iOS-逆向11-代码注入_第39张图片
图片.png

iOS-逆向11-代码注入_第40张图片
image

图片.png

iOS-逆向11-代码注入_第41张图片
图片.png

iOS-逆向11-代码注入_第42张图片
image

⓾.运行,查看类CloudHook中的load方法中的NSLog打印注入成功咯是否显示,通过MachOViewer查看Dylib注入.app显示包内容中的WeChat可执行文件的LC_LOAD_DYLIB列表中是否新增libCloudHook.dylib路径
图片.png

iOS-逆向11-代码注入_第43张图片
image

iOS-逆向11-代码注入_第44张图片
image

以上用脚本注入dylib成功,同理,Framework也可以通过脚本注入,只需修改脚本,指明Frameworks的路径为Frameworks/CloudHook.framework/CloudHook
./yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/CloudHook.framework/CloudHook"
图片.png

iOS-逆向11-代码注入_第45张图片
图片.png

图片.png

2.Method Swizzle方法交换

利用OC的Runtime特性,动态改变SEL(方法编号)和IMP(方法实现)的对应关系,达到OC方法调用流程改变的目的。主要用于OC方法。
在OC中,SEL 和 IMP 之间的关系,就好像一本书的“目录”。
SEL 是方法编号,就像“标题”一样。
IMP是方法实现的真实地址,就像“页码”一样。
他们是一一对应的关系


iOS-逆向11-代码注入_第46张图片
image

3.多种Hook方式

1.class_addMethod方式:
利用AddMethod方式,让原始方法可以被调用,不至于因为找不到SEL而崩溃

2.class_replaceMethod方式:
利用class_replaceMethod,直接给原始的方法替换IMP

3.method_setImplementation方式:
利用method_setImplementation,直接重新赋值原始的新的IMP

4.破坏微信注册功能

I.通过lldb调试Debug View Hierarchy,发现微信的注册的控制器WCAccountLoginControlLogic和方法onFirstViewRegister


iOS-逆向11-代码注入_第47张图片
图片.png
iOS-逆向11-代码注入_第48张图片
image

II.通过runtime修改注册方法,注册被拦截到自己的方法中

+(void)load{
    //改变微信的注册
    Method oldMethod = class_getInstanceMethod(objc_getClass("WCAccountLoginControlLogic"), @selector(onFirstViewRegister));
    Method newMethod = class_getInstanceMethod(self, @selector(my_method));
    method_exchangeImplementations(oldMethod, newMethod);
}
-(void)my_method{
    NSLog(@"哥么注册不了了!");
}
图片.png
iOS-逆向11-代码注入_第49张图片
image

由此得知谨慎使用第三方插件,会有安全隐患,尤其是破解包
当点击登录时,若要截图密码,该怎么获取密码?


iOS-逆向11-代码注入_第50张图片
image

5.窃取登录密码调试分析

A.响应链条

通过lldb调试Debug View Hierarchy发现登录的控制器名字WCAccountMainLoginViewController和响应事件方法名字onNext,通过响应者链一层一层找到textField


iOS-逆向11-代码注入_第51张图片
image

iOS-逆向11-代码注入_第52张图片
image
(lldb) po [(WCAccountMainLoginViewController *)0x129001e00 view]
>

(lldb) po [(UIView *)0x127e06580 subviews]
<__NSArrayM 0x1d4e56890>(
; layer = ; contentOffset: {0, -64}; contentSize: {414, 393}; adjustedContentInset: {64, 0, 226, 0}>,
>,
>
)

(lldb) po [(UIView *)0x12800a200 subviews]
<__NSArrayM 0x1c40479e0>(
>,
>,
>,
>
)

(lldb) po [(UIView *)0x12c2a75c0 subviews]
<__NSArrayM 0x1d0252b70>(
>
)

(lldb) po [(UIView *)0x12c2a73e0 subviews]
<__NSArrayM 0x1d0252b40>(

B.静态分析

代码注入hook登录按钮

+(void)load{
    //改变微信的注册
    Method oldMethod = class_getInstanceMethod(objc_getClass("WCAccountLoginControlLogic"), @selector(onFirstViewRegister));
    Method newMethod = class_getInstanceMethod(self, @selector(my_method));
    method_exchangeImplementations(oldMethod, newMethod);
    //改变微信的登录
    Method oldMethod0 = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
    Method newMethod0 = class_getInstanceMethod(self, @selector(my_loginmethod));
    method_exchangeImplementations(oldMethod0, newMethod0);
}
-(void)my_method{
    NSLog(@"哥么注册不了了!");
}
-(void)my_loginmethod{
    NSLog(@"哥么登录不了了!");
}

1.通过class-dump导出WeChat的头文件,class-dump工具能dump出OC所有的类,对象,对象方法,类列表,方法列表,包括成员变量,执行./class-dump -H WeChat -o ./headers/,生成headers文件夹并不是源码,是通过读取MachO文件得到的头文件,头文件并没有打包到可执行文件中

iOS-逆向11-代码注入_第53张图片
图片.png

iOS-逆向11-代码注入_第54张图片
image

!!!此时注意,官网下载的class-dump不能导出OC和Swift混编的可执行文件,需要从https://github.com/AloneMonkey/MonkeyDev/blob/master/bin/class-dump下载,否则会报错Error:Cannot find offset for address 0xd8xxxxx in stringAtAddress

2.Headers文件夹拖入Sublime Text,快速找到WCAccountMainLoginViewController.h文件,找到密码输入框WCAccountTextFieldItem *_textFieldUserPwdItem,往下找WCAccountTextFieldItem,找到WCBaseTextFieldItem中WCUITextField *m_textField

图片.png

iOS-逆向11-代码注入_第55张图片
image
iOS-逆向11-代码注入_第56张图片
image

3.onNext方法没有参数,只有self和_cmd,self表示WCAccountMainLoginViewController


图片.png

4.lldb调试通过valueForKey获取m_textField的值

(lldb) po [(WCAccountMainLoginViewController *)0x118807a00 valueForKey:@"_textFieldUserPwdItem"]


(lldb) po [(WCAccountTextFieldItem *)0x1d42b0c20 valueForKey:@"m_textField"]
; layer = >
图片.png

5.代码实现,在onNext的hook方法中通过valueForKey的方式获取密码[(UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"] text]

+(void)load{
    //改变微信的注册
    Method oldMethod = class_getInstanceMethod(objc_getClass("WCAccountLoginControlLogic"), @selector(onFirstViewRegister));
    Method newMethod = class_getInstanceMethod(self, @selector(my_method));
    method_exchangeImplementations(oldMethod, newMethod);
    //改变微信的登录
    Method oldMethod0 = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
    Method newMethod0 = class_getInstanceMethod(self, @selector(my_loginmethod));
    method_exchangeImplementations(oldMethod0, newMethod0);
}
-(void)my_method{
    NSLog(@"哥么注册不了了!");
}
-(void)my_loginmethod{
    NSLog(@"密码是:%@",[(UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"] text]);
}
image

5.调用回原来的逻辑,调用原来的方法,发现程序崩溃,WCAccountMainLoginViewController中找不到my_loginmethod方法,'-[WCAccountMainLoginViewController my_loginmethod]: unrecognized selector sent to instance 0x11c805400'

-(void)my_loginmethod{
    NSLog(@"密码是:%@",[(UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"] text]);
    //调用回原来的逻辑
    //调用原来的方法
    [self my_loginmethod];
    //objc_msgSend()
}
图片.png

方法断点后,进入汇编分析进入方法objc_msgSend方法,查看x0和x1寄存器的值,此时WCAccountMainLoginViewController类中并没有my_loginmethod方法,方法交换后,只是改变了方法的指向,类中并没有新增方法,故报错,有三种解决办法


iOS-逆向11-代码注入_第57张图片
image

让被hook的类中包含有交换的selector,最简单的方式是新建分类,不用分类的情况下,可以通过runtime添加方法

  • I.通过给类添加方法后交换,程序能正常运行,调用原来的方法
+(void)load{
    //改变微信的登录
    //原始的Method
    Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
    //WC添加新方法
    class_addMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(new_onNext), new_onNext,"v@:");
    //交换
    method_exchangeImplementations(onNext, class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(new_onNext)));
}
void new_onNext(id self,SEL _cmd){
    NSLog(@"密码是:%@",[(UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"] text]);
    //调用回原来的逻辑
    //调用原来的方法
    [self performSelector:@selector(new_onNext)];
    //objc_msgSend()
}
iOS-逆向11-代码注入_第58张图片
image

iOS-逆向11-代码注入_第59张图片
图片.png
iOS-逆向11-代码注入_第60张图片
图片.png
iOS-逆向11-代码注入_第61张图片
图片.png
  • 2.通过替换原来的方法class_replaceMethod
+(void)load{
    //改变微信的登录
    //原始的Method
    Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
    IMP old_onNext = class_replaceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext), new_onNext, "v@:");
}
//原来的IMP
IMP (*old_onNext)(id self,SEL _cmd);

void new_onNext(id self,SEL _cmd){
    NSLog(@"密码是:%@",[(UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"] text]);
    //调用回原来的逻辑
    //调用原来的方法
    old_onNext(self,_cmd);
    //objc_msgSend()
}
图片.png

3.通过Method的method_getImplementation和method_setImplementation方法将原始的onNext存起来后替换为新的new_onNext,后执行原始的

+(void)load{
    //改变微信的登录
    //原始的Method
    old_onNext = method_getImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)));
    method_setImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)), new_onNext);
}
//原来的IMP
IMP (*old_onNext)(id self,SEL _cmd);

void new_onNext(id self,SEL _cmd){
    NSLog(@"密码是:%@",[(UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"] text]);
    //调用回原来的逻辑
    //调用原来的方法
    old_onNext(self,_cmd);
    //objc_msgSend()
}
图片.png

你可能感兴趣的:(iOS-逆向11-代码注入)