代码注入及其拓展--逆向开发

Posted guohai-stronger

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代码注入及其拓展--逆向开发相关的知识,希望对你有一定的参考价值。

今天继续讲述逆向开发中另一个比较重要的课程是代码注入内容,本篇篇幅比较长,但还是有很多干货的,希望大家通过此篇文章更加了解逆向开发中的要点和知识点.我们将分解几个内容,进行讲解:

  1. Framework注入
  2. Dylib注入
  3. MethodSwizzle
  4. 微信示例讲解
  5. 总结

让代码执行自己的代码,整体方案如下:

技术图片

如何让别人的app来执行自己的代码呢? 这就要通过代码注入的方式来达到,而代码注入的方式有两种: 一种是通过framework, 一种是dylib方式,另种方案,可以通过Runtime机制

代码注入思路:

DYLD会动态加载动态库Framework中所有动态库,在frameworks中加入自己的一个动态库,然后在动态库中hook和注入代码.

一、FrameWork注入

 1.准备工作

  • 微信6.6.5(越狱应用)
  • MachOView软件

  MachOView的下载地址:http://sourceforge.net/projects/machoview/

  如果想看源码如下:MachOView源码:https://github.com/gdbinit/MachOView

  • yololib工具(给MachOView注入framework)

  yololib工具下载地址:https://github.com/KJCracks/yololib?spm=a2c4e.11153940.blogcont63256.9.5126420eAJpqBD

  • 签名文件appsign文件

2.流程

2.1 加入准备工作,导入微信6.6.5版本以及脚本appSign.sh重签名文件

技术图片

2.2 将appSign导入到项目脚本中

技术图片

 

 

 2.3 有了上面的两个步骤后,然后编译一下工程,会出现一个temp工程,里面包含了payload文件

技术图片

2.4 显示包内容,查看可执行文件

技术图片

 2.5 我们通过MachOView软件查看WeChat

技术图片

我们看到有很多的DYLIB,代表的是加载动态库

2.6  我们在项目中新建framework

技术图片

 

2.7 新建文件用于验证

技术图片

2.8 想要达到刚加载就运行,代码要写在load方法

技术图片

 2.9 编译一下,查看app包位置会多出一个framework

技术图片

2.10 显示包内容,在framework查看

技术图片

由上可知,WJHookFrameWork已经加入成功。

2.11 但是运行并没有成功,没有执行load里的代码

原因:用MachOView打开可执行的WeChat,没有找到WJHookFrameWork

下面我们讲述怎么将WJHookFramework写入到MachoView文件中?

3. WJHookFramework写入到MachOView文件中

需要使用yololib工具,建议将yololib放到 /usr/local/bin

技术图片

3.1 解压越狱包

技术图片

3.2 将WeChat.app显示包内容,找到WeChat可执行的文件

需要增加执行权限: chmod +x WeChat

3.3 写入WeChat可执行文件

yololib WeChat Frameworks/WJHookFrameWork.framework/WJHookFrameWork

技术图片

通过上面的过程,查看MachOView文件Load commands中是否有WJHookFrameWork

技术图片

上面图显示已经加入成功。

3.4 删除原有的ipa,打包payload

zip -ry WeChat.ipa Payload

将WeChat.ipa放入App目录中,删除其他的文件夹。

技术图片

 

3.5 再次运行,发现成功!!!

技术图片

上面就是framework方式代码注入。大家可以私信我,如有不懂!!!

 二、Dylib注入

2.1 新建工程,添加脚本到build phases 

技术图片


加入脚本文件

技术图片

2.2添加第三方库dylib(mac os的,非ios

技术图片

2.3 添加依赖

技术图片

技术图片

2.4 修改第三方类库仅限mac使用,修改Base SDK

技术图片

2.5 修改signing 

技术图片

2.6 脚本中注入动态库的代码

# ${SRCROOT} 它是工程文件所在的目录
TEMP_PATH="${SRCROOT}/Temp"
#资源文件夹,我们提前在工程目录下新建一个APP文件夹,里面放ipa包
ASSETS_PATH="${SRCROOT}/APP"
#目标ipa包路径
TARGET_IPA_PATH="${ASSETS_PATH}/*.ipa"
#清空Temp文件夹
rm -rf "${SRCROOT}/Temp"
mkdir -p "${SRCROOT}/Temp"

#----------------------------------------
# 1. 解压IPA到Temp下
unzip -oqq "$TARGET_IPA_PATH" -d "$TEMP_PATH"
# 拿到解压的临时的APP的路径
TEMP_APP_PATH=$(set -- "$TEMP_PATH/Payload/"*.app;echo "$1")
# echo "路径是:$TEMP_APP_PATH"

#----------------------------------------
# 2. 将解压出来的.app拷贝进入工程下
# BUILT_PRODUCTS_DIR 工程生成的APP包的路径
# TARGET_NAME target名称
TARGET_APP_PATH="$BUILT_PRODUCTS_DIR/$TARGET_NAME.app"
echo "app路径:$TARGET_APP_PATH"

rm -rf "$TARGET_APP_PATH"
mkdir -p "$TARGET_APP_PATH"
cp -rf "$TEMP_APP_PATH/" "$TARGET_APP_PATH"

#----------------------------------------
# 3. 删除extension和WatchAPP.个人证书没法签名Extention
rm -rf "$TARGET_APP_PATH/PlugIns"
rm -rf "$TARGET_APP_PATH/Watch"

#----------------------------------------
# 4. 更新info.plist文件 CFBundleIdentifier
#  设置:"Set : KEY Value" "目标文件路径"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $PRODUCT_BUNDLE_IDENTIFIER" "$TARGET_APP_PATH/Info.plist"

#----------------------------------------
# 5. 给MachO文件上执行权限
# 拿到MachO文件的路径
APP_BINARY=`plutil -convert xml1 -o - $TARGET_APP_PATH/Info.plist|grep -A1 Exec|tail -n1|cut -f2 -d>|cut -f1 -d<`
#上可执行权限
chmod +x "$TARGET_APP_PATH/$APP_BINARY"

#----------------------------------------
# 6. 重签名第三方 FrameWorks
TARGET_APP_FRAMEWORKS_PATH="$TARGET_APP_PATH/Frameworks"
if [ -d "$TARGET_APP_FRAMEWORKS_PATH" ];
then
for FRAMEWORK in "$TARGET_APP_FRAMEWORKS_PATH/"*
do

#签名
/usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" "$FRAMEWORK"
done
fi

#注入
yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/libHankHook.dylib"

2.7 编译运行成功(也和上面一样在类中加入load代码)

技术图片

 

上面就是dylib方式代码注入,希望对大家有所帮助!!!

 通过上面的两种方式实现代码注入,让别人的app运行自己的app,下面总结如下:

技术图片

 三、MethodSwizzle

3.1 概念

iOS 中实现AOP编程思想的方式其中之一是Method Swizzling,而 Method Swizzling 是利用Runtime特性把一个方法和另个方法的实现做替换,程序运行时修改Dispatch Table里SEL和IMP之间的映射关系.

通过swizzling method改变目标函数selector所指向实现,在新的实现中来实现所要改的内容即可.

技术图片

3.2 特点

  • 继承: 修改较多,无法敢保证他人一定继承基类
  • 类别: 类别中重写方法会覆盖到原有的实现,其实,在真实的开发中,重写方法并不是为了取代它,而是为了添加一些实现; 如果几个类别实现了同样的方法, 但只有一个类别的方法会被调用.
  • AOP优势: 减少了重复代码

3.3 代码

@implementation NSURL (HKURL)

+(void)load
{
    Method URLWithStr = class_getClassMethod(self, @selector(URLWithString:));
    
    Method HKURL = class_getClassMethod(self, @selector(HKURLWithStr:));
    
    //交换
    method_exchangeImplementations(URLWithStr, HKURL);
}

+(instancetype)HKURLWithStr:(NSString *)str{
    //调用系统原来的方法
    NSURL * url = [NSURL HKURLWithStr:str];
    if (url == nil) {
        str = @"https://www.blog.com";
    }
    url = [NSURL HKURLWithStr:str];
    
    return url;
}

在上面的代码中,利用method swizzling的交换方法.其他Runtime的使用方法,以及为什么写在load方法中,请参考本人另篇博客https://www.cnblogs.com/guohai-stronger/p/9184356.html

拓展: 为什么写在load中?

  • load方法在源文件被装载到程序中会被自动调用,不需要手动调用,也不需要该类使用不使用无关,在main()前被执行.
  • 当子类重写了load,假如子类的类别重写了load,load的调用顺序会这样: 父类、子类、子类类别
  • 但是如果有多个子类category都重写了load,每个子类category中load都会调用一次
  • 假如子类没有重写load,子类的默认load也不会去调用父类的load.此与正常继承不太一样.
  • 在正常的开发中, 除了method swizzle ,其他的逻辑代码尽量不放在load,load方法中的代码逻辑要尽量简单

 

四、微信示例Demo

4.1 微信--破坏注册

4.1.1 将微信程序运行出来,如下图所示

技术图片

4.1.2 根据上面红色找出类名,方法名

技术图片

4.1.3 通过插件class-dump导出微信的.h文件

class-dump是将OC运行时声明的信息导出来的工具, 其实可以导出.h文件. 用此工具将未经过加密的app的头文件导出来.

使用它同样也要讲此工具拷贝到MAC的目录下/usr/local/bin下.

技术图片

4.1.4 经过sublime text来找出对应的文件

技术图片

4.1.5 通过第三部分讲解,利用runtime交换方法

技术图片

4.1.6 结果

技术图片

 

4.2 窃取微信密码

4.2.1 找到输入密码框的内容

技术图片

从上面看出,登录按钮为一个FixTitleColorButton对象,Target名字存放的地址为0x280afaa40,Action名字存放地址是0x280afac00。

4.2.2 查看账号密码的输入框

技术图片

发现账号密码输入框对象属于都一个对象,叫做WCUITextField

4.2.3 利用LLDB查看登录具体的Target和Action

技术图片

从上面卡出,登录按钮在WCAccountMainLoginViewController页面中;

登录点击方法叫做onNext

4.2.4 利用Sublime查看WeChat文件

发现确实有onNext()方法,并从中看出账号输入框和密码输入框都是WCAccountTextFieldItem中,但是并没有发现textFileld,但是可以看到WCAccountTextFieldItem是继承于WCBaseTextFieldItem,我们再看看WCBaseTextFieldItem文件内容

技术图片

看出一个m_textField对象,通过tex字段取出string。

4.2.5 在账号栏中输入账号和密码

po [(WCAccountMainLoginViewController *)0x1128bbc00 valueForKey:@"_textFieldUserPwdItem"]
po [(WCAccountTextFieldItem *)0x28328e880 valueForKey:@"m_textField"]
po [(WCUITextField *)0x112163a00 text]

技术图片

通过LLDB调试输入的密码是123456。

4.2.6 Hook登录,获取密码

+ (void)load {
    NSLog(@"来了,老弟");
    Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), sel_registerName("onNext"));
    //1.保存原始的IMP
    old_onNext = method_getImplementation(onNext);
    //2.SET
    method_setImplementation(onNext, (IMP)my_next);
}

IMP (*old_onNext)(id self,SEL _cmd);

void my_next(id self,SEL _cmd){
    // 获取密码
    NSString *pwd = [[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"] performSelector:@selector(text)];
    NSString *accountTF = [[[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"] performSelector:@selector(text)];
    NSLog(@"密码是!%@",pwd);
    // 将密码追加在账号栏的后面
    [[[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"] performSelector:@selector(setText:) withObject:[NSString stringWithFormat:@"%@+%@",accountTF,pwd]];
    //调用原来的方法
    old_onNext(self,_cmd);
}

上面用的是setIMP和getIMP的方式,对原方法进行Hook,也可以用class_replaceMethod(),method_exchangeImplementations()。

 

五、总结

首先从代码注入的方式:framework和dylib两种方式,然后讲到Method swizzling方式尝试Hook,最后又以demo的方式来阐述代码注入和Hook,希望对大家理解逆向开发的代码注入有所帮助!!!,欢迎大家继续关注!!!

 

 

以上是关于代码注入及其拓展--逆向开发的主要内容,如果未能解决你的问题,请参考以下文章

iOS开发逆向之代码注入(上)

Android 逆向Android 进程注入工具开发 ( 注入代码分析 | 注入工具的 main 函数分析 )

201555332盛照宗—网络对抗实验1—逆向与bof基础

Android 逆向Android 进程注入工具开发 ( 注入代码分析 | 调试进程 ATTACH 附着目标进程 | 读取目标函数寄存器值并存档 )

Android 逆向Android 进程注入工具开发 ( 注入代码分析 | 远程调用 目标进程中 libc.so 动态库中的 mmap 函数 二 | 准备参数 | 远程调用 mmap 函数 )(代码片

Android 逆向Android 进程注入工具开发 ( 注入代码分析 | 远程调用 目标进程中 libc.so 动态库中的 mmap 函数 三 | 等待远程函数执行完毕 | 寄存器获取返回值 )(代