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

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

  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,希望对大家理解逆向开发的代码注入有所帮助!!!,欢迎大家继续关注!!!

 

 

posted @ 2019-11-17 13:30  国孩  阅读(1064)  评论(0编辑  收藏  举报