Quantcast
Channel: 睿论坛 - 最新话题
Viewing all 5702 articles
Browse latest View live

简书APP Hook 绕过 HTTPS 证书验证 失败

$
0
0

@huakucha wrote:

本来目的是开一下 简书APP的快速加载HTML的逻辑,就用抓包软件抓取请求 发现简书做了HTTPS证书校验,本身自己公司的证书效验就是用的AF进行配置的,觉得可以通过砸壳APP Hook相应的代码进行证书校验绕过,但是重签名之后所有的请求就无法进行网络请求了

2017-08-28 12:27:39.715135+0800 Hugo[7648:6494881] [SensorsAnalytics] <SensorsAnalyticsSDK: 0x1701def00> initialized the instance of Sensors Analytics SDK with server url 'https://events.jianshu.io:4006/sa?project=production', configure url 'https://events.jianshu.io:4006/config/iOS.conf', debugMode: 'DebugOff'
2017-08-28 12:27:40.268270+0800 Hugo[7648:6494881] Reachability Flag Status: -R ------- networkStatusForFlags
2017-08-28 12:27:40.300266+0800 Hugo[7648:6494881] [Crashlytics] Version 3.8.4 (121)
2017-08-28 12:27:40.343419+0800 Hugo[7648:6494881] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
2017-08-28 12:27:40.347608+0800 Hugo[7648:6494881] [MC] Reading from public effective user settings.
2017-08-28 12:27:40.748017+0800 Hugo[7648:6494881] Could not signal service com.apple.WebKit.WebContent: 113: Could not find specified service
2017-08-28 12:27:40.750392+0800 Hugo[7648:6494881] Could not signal service com.apple.WebKit.Networking: 113: Could not find specified service
2017-08-28 12:27:40.793662+0800 Hugo[7648:6494971] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:40.814129+0800 Hugo[7648:6494881] [WC] WCSession is not paired
2017-08-28 12:27:40.814227+0800 Hugo[7648:6494881] [WC] -[WCSession updateApplicationContext:error:]_block_invoke failed due to WCErrorCodeDeviceNotPaired
2017-08-28 12:27:40.828619+0800 Hugo[7648:6494984] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:40.829150+0800 Hugo[7648:6494971] [WC] -[WCSession onqueue_handleUpdateSessionState:]_block_invoke dropping as pairingIDs no longer match. pairingID (null), client pairingID: (null)
2017-08-28 12:27:40.830653+0800 Hugo[7648:6494971] [WC] WCSession is not paired
2017-08-28 12:27:40.835768+0800 Hugo[7648:6494984] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:40.838184+0800 Hugo[7648:6494984] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:40.840565+0800 Hugo[7648:6494984] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:40.842618+0800 Hugo[7648:6494984] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:40.846693+0800 Hugo[7648:6494996] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:40.850174+0800 Hugo[7648:6494971] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:40.855026+0800 Hugo[7648:6494996] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:40:863 Hugo[7648:6494996] Error: -1004
Status Code:0
Request URL: https://s0.jianshuapi.com/v2/admin/splash_screens.json
2017-08-28 12:27:40:872 Hugo[7648:6494996] Error: -1004
Status Code:0
Request URL: https://s0.jianshuapi.com/v1/admin/notice.json
2017-08-28 12:27:40:873 Hugo[7648:6494996] Error: -1004
Status Code:0
Request URL: https://s0.jianshuapi.com/v2/admin/ab_settings.json
2017-08-28 12:27:40:874 Hugo[7648:6494996] Error: -1004
Status Code:0
Request URL: https://s0.jianshuapi.com/v2/device_infos.json
2017-08-28 12:27:40:875 Hugo[7648:6494996] Error: -1004
Status Code:0
Request URL: https://s0.jianshuapi.com/v2/admin/app_release.json
2017-08-28 12:27:40:876 Hugo[7648:6494996] Error: -1004
Status Code:0
Request URL: https://s0.jianshuapi.com/v2/admin/idfa_records.json
2017-08-28 12:27:40:878 Hugo[7648:6494996] Error: -1004
Status Code:0
Request URL: https://s0.jianshuapi.com/v3/mobile_app/templates/note.json
2017-08-28 12:27:40:879 Hugo[7648:6494996] Error: -1004
Status Code:0
Request URL: https://s0.jianshuapi.com/v2/pts/setting.json
2017-08-28 12:27:41.004690+0800 Hugo[7648:6494881]  INFO: Reveal Server started (Protocol Version 40).
2017-08-28 12:27:41.020173+0800 Hugo[7648:6494881] retryHandleOpenURL
2017-08-28 12:27:41.106114+0800 Hugo[7648:6494881] Presenting view controllers on detached view controllers is discouraged <HGHomepageHotNoteTableViewController: 0x15be51dd0>.
2017-08-28 12:27:41.138963+0800 Hugo[7648:6495015] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.142021+0800 Hugo[7648:6494984] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41:174 Hugo[7648:6495015] Error: -1004
Status Code:0
Request URL: https://s0.jianshuapi.com/v3/trending/daily.json?count=15&limit=15&page=1
2017-08-28 12:27:41.179269+0800 Hugo[7648:6495015] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.183670+0800 Hugo[7648:6494972] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41:187 Hugo[7648:6494970] Error: -1004
Status Code:0
Request URL: https://s0.jianshuapi.com/v2/admin_banners.json?count=15&limit=15
2017-08-28 12:27:41:190 Hugo[7648:6494984] Error: -1004
Status Code:0
Request URL: https://s0.jianshuapi.com/v2/collection_categories.json?count=15&limit=15&type=for_app
2017-08-28 12:27:41:191 Hugo[7648:6494984] Error: -1004
Status Code:0
Request URL: https://s0.jianshuapi.com/v2/subscriptions/recommended_collections.json
2017-08-28 12:27:41:480 Hugo[7648:6494881] Fail to register for remote notifications: Error Domain=NSCocoaErrorDomain Code=3000 "no valid 'aps-environment' entitlement string found for application" UserInfo={NSLocalizedDescription=no valid 'aps-environment' entitlement string found for application}
2017-08-28 12:27:41:482 Hugo[7648:6494881] Fail to register for remote notifications: Error Domain=NSCocoaErrorDomain Code=3000 "no valid 'aps-environment' entitlement string found for application" UserInfo={NSLocalizedDescription=no valid 'aps-environment' entitlement string found for application}
2017-08-28 12:27:41.486867+0800 Hugo[7648:6494996] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.500956+0800 Hugo[7648:6494996] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.513817+0800 Hugo[7648:6494997] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.521216+0800 Hugo[7648:6495019] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.525152+0800 Hugo[7648:6495019] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.529527+0800 Hugo[7648:6495014] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.599619+0800 Hugo[7648:6495019] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.603129+0800 Hugo[7648:6494970] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.608536+0800 Hugo[7648:6494997] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.616146+0800 Hugo[7648:6494997] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.621170+0800 Hugo[7648:6494997] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.626511+0800 Hugo[7648:6494997] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.646125+0800 Hugo[7648:6494996] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.652063+0800 Hugo[7648:6495014] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.655427+0800 Hugo[7648:6494984] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.658990+0800 Hugo[7648:6494978] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.661573+0800 Hugo[7648:6494978] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:41.666314+0800 Hugo[7648:6494970] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:42.573423+0800 Hugo[7648:6494881] Unbalanced calls to begin/end appearance transitions for <HGTabBarController: 0x15c883a00>.
2017-08-28 12:27:52.474860+0800 Hugo[7648:6494971] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:52.482274+0800 Hugo[7648:6494971] [Fabric] failed to download settings Error Domain=NSURLErrorDomain Code=-1004 "Could not connect to the server." UserInfo={NSUnderlyingError=0x170256380 {Error Domain=kCFErrorDomainCFNetwork Code=-1004 "(null)" UserInfo={_kCFStreamErrorCodeKey=64, _kCFStreamErrorDomainKey=1}}, NSErrorFailingURLStringKey=https://settings.crashlytics.com/spi/v2/platforms/ios/apps/com.jianshu.Hugo/settings?build_version=1707211539&display_version=3.7.0&instance=08e5a5b9b535d80d232d03df1dc7b2af04e92aaa&icon_hash=f3cc9ece6be0c8f2d56f2ebc047763c4b4d84c78&source=1, NSErrorFailingURLKey=https://settings.crashlytics.com/spi/v2/platforms/ios/apps/com.jianshu.Hugo/settings?build_version=1707211539&display_version=3.7.0&instance=08e5a5b9b535d80d232d03df1dc7b2af04e92aaa&icon_hash=f3cc9ece6be0c8f2d56f2ebc047763c4b4d84c78&source=1, _kCFStreamErrorDomainKey=1, _kCFStreamErrorCodeKey=64, NSLocalizedDescription=Could not connect to the server.}
2017-08-28 12:27:56.573944+0800 Hugo[7648:6494978] [] nw_socket_connect connectx failed: [64] Host is down
2017-08-28 12:27:56.578483+0800 Hugo[7648:6494978] [SensorsAnalytics] <SensorsAnalyticsSDK: 0x1701def00> network failure: Error Domain=NSURLErrorDomain Code=-1004 "Could not connect to the server." UserInfo={NSUnderlyingError=0x1702561d0 {Error Domain=kCFErrorDomainCFNetwork Code=-1004 "(null)" UserInfo={_kCFStreamErrorCodeKey=64, _kCFStreamErrorDomainKey=1}}, NSErrorFailingURLStringKey=https://events.jianshu.io:4006/sa?project=production, NSErrorFailingURLKey=https://events.jianshu.io:4006/sa?project=production, _kCFStreamErrorDomainKey=1, _kCFStreamErrorCodeKey=64, NSLocalizedDescription=Could not connect to the server.}

本人觉得这个防护挺新颖的 所以贴出来一起讨论

Posts: 2

Participants: 2

Read full topic


Usbmuxd时 执行ssh root@localhost -p 2222 指令时出现 报错。ssh_exchange_identification: Connection closed by remote host

Theos_device_ip一定要月月手机的ip地址吗,没越狱行不行

Failed to get reply to handshake packet

$
0
0

@tengge wrote:

第一步 打开监听:
iPhone:~ root# debugserver -x backboard 192.168.1.101:1234 /var/mobile/Containers/Bundle/Application/2302E216-267D-4E78-806F-6DB264E3C7FE/WeChat.app/WeChat
debugserver-@(#)PROGRAM:debugserver PROJECT:debugserver-340.3.51.1
for arm64.
Listening to port 1234 for a connection from 192.168.1.101...
第二步 打开lldb:
/Applications/Xcode.app/Contents/Developer/usr/bin/lldbb
第三步 lldb连接 报了2个错
(lldb) process connect connect://192.168.1.101:1234
error: failed to get reply to handshake packet
error: rejecting incoming connection from 192.168.1.102 (expecting 192.168.1.101)

之前我按照这3步是可以成功连接的,今天不知道为啥这样了

Posts: 5

Participants: 2

Read full topic

Jailbreak ios 10.2.1 update to 10.3.1 - 10.3.2 - 10.3.3 (Download)

书上210修改寄存器的值后运行,程序退出了,但是并没有打印相应的结果

$
0
0

@fun wrote:

鉴于论坛大量涌入新人且提问缺乏必要信息导致问题无法解决,目前暂行提问模版机制。试运行期间内新帖没有按照这个模版发帖将导致 锁帖/删帖/封号
(除非有原因,某些问题无法分类进下面的分支。这一点完全靠管理员唯一指定,大多数普通问题请老实按照下面的分类)

标**的为可选项

需求: (请勿使用 “我的需求是a但是我认为b可以解决a所以我来问问b” 这种提问模式,会造成误导)
**日志: (例如iOS系统的日志,OS X上相关操作的日志,etc)
**代码: (如果项目本身代码不方便发布请提供最小问题重现代码)
操作步骤: (请详细描述自己每步做了什么操作)
** 任何其他描述: (描述问题的现象,等等)
** 环境: (系统版本,安装的相关工具,等操作)
需求:书上210页,修改寄存器的值后,打印出修改后的相应结果
日志:
Process 18398 stopped
* thread #1: tid = 0x13a170, 0x000000010007bf10 MainBinarymain + 36, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x000000010007bf10 MainBinary
main + 36
MainBinary`main:
-> 0x10007bf10 <+36>: tbz w8, #0, 0x10007bf1c ; <+48>
0x10007bf14 <+40>: bl 0x10007bed0 ; ImportantAndComplidatedFunction
0x10007bf18 <+44>: b 0x10007bf28 ; <+60>
0x10007bf1c <+48>: adrp x0, 1
(lldb) p $w8
(unsigned int) $1 = 1
(lldb) register write $w8 0
(lldb) p $w8
(unsigned int) $2 = 0
(lldb) c
Process 18398 resuming
Process 18398 exited with status = 0 (0x00000000)
(lldb)

代码:
#include stdio.h>
#include dlfcn.h>
#import Foundation/Foundation.h>
#import UIKit/UIKit.h>

extern void ImportantAndComplidatedFunction()
{
NSLog(@"iOSRE: Suppose I'm a very important and complicated function");
}

int main(int argc, char **argv)
{
BOOL isIOS10 = YES;
if (isIOS10) {
ImportantAndComplidatedFunction();
}
else
{
NSLog(@"不是iOS10");
}
return 0;
}
描述:修改w8的值为0后,再c,并没有打印文字"不是iOS10"
环境:iOS10.0.2 iPhone5s

Posts: 2

Participants: 2

Read full topic

按照书上的教程 lldb+debugserver 调试 SpringBoard 遇到点问题,求指点.谢谢

按照书上的在 LLDB+debugserver 调试这里点击 HOME 无法触发断点.请求指点


微信如何追踪经纬度参数是从哪里传递过来的

$
0
0

@CCbird wrote:

找到了[QUserLocation setLocation]这个函数,也能看到传进来的经纬度参数,如何追踪这个包含经纬度参数是从哪里传递过来的

Posts: 1

Participants: 1

Read full topic

开发一个root权限的App

$
0
0

@Alan_xz wrote:

需求:我要开发一个拥有root 权限的app 我在论坛浏览了好多,也有很多介绍 但不是很理解,在网上我找到一个demo 但是不太理解它的执行文件的操作意义,能不能解释下。。。 我安装后返回的UID GID 是 0 是成功了么??

main 代码
int main(int argc, char * argv[]) {
@autoreleasepool {
setgid(0);
setuid(0);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

然后在打包deb的时候 在DEBIAN下 添加了两个可执行文件
1.postinst代码如下:(注:我的工程名为ReverseTest)
#!/bin/bash
cd "/Applications/ReverseTest.app/"

process origin binary

mv ReverseTest ReverseTest_
chown root.wheel ReverseTest_
chmod +s ReverseTest_

cont=`cat <<"EOF"

!/bin/bash

dir=$(dirname "$0")
exec "${dir}"/ReverseTest_ "$@"
EOF
`

create new fake binary

echo -e "$cont" > ReverseTest
chown root.wheel ReverseTest
chmod +x ReverseTest

The RESPRING script after Install

declare -a cydia
cydia=($CYDIA)

if [[ $1 == install || $1 == upgrade ]]; then
if [[ ${CYDIA+@} ]]; then
eval "echo 'finish:restart' >&${cydia[0]}"
fi
fi

exit
2.prerm代码如下:

!/bin/bash

rm -f "/Applications/rootApp.app/ReverseTest_"

附demo地址:https://github.com/QbsuranAlang/rootApp

Posts: 1

Participants: 1

Read full topic

刚入手一个mac和本站书籍 想付费请教动态调试

$
0
0

@sjsjnimei wrote:

刚入手一个mac 下午5-6点到货 。 想付500元 手把手教我安装相应的软件.进行对应的ipa动态调试

Posts: 2

Participants: 2

Read full topic

注入 com.apple.corefoundation,com.apple.foundation和com.apple.Security

依葫芦画瓢hook一个Mac App最后失败的全过程

$
0
0

@wjk930726 wrote:

引言

昨天看完论坛里的五分钟搞不定新版Mac迅雷后成功依葫芦画瓢hook掉了一个Mac程序,今天很有成就感得准备再把Interface Inspector的15天测试干掉,结果屡屡受挫。就自己先写了个小demo测试一下,结果就有了下面这篇帖子……

小demo

一个非常简单的demo,大概就是软件正中一个按钮,点击之后alert("hi!")。核心代码如下:

#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // Do any additional setup after loading the view.
}

- (IBAction)sayHi:(NSButton *)sender {
    NSAlert *alert = NSAlert.new;
    alert.messageText = @"hi!";
    alert.alertStyle = NSAlertStyleInformational;
    [alert runModal];
}


- (void)setRepresentedObject:(id)representedObject {
    [super setRepresentedObject:representedObject];

    // Update the view, if already loaded.
}


@end

hook过程再现

根据多年想像的编程经验 写了下面这个Logos:

%config(generator=internal)

// You don't need to #include <substrate.h>, it will be done automatically, as will
// the generation of a class list and an automatic constructor.
#import <Foundation/Foundation.h>

%hook ViewController


// Hooking an instance method with an argument.
- (void)sayHi:(id)argument {
    NSAlert *r15 = [[NSAlert alloc] init];
    [r15 setMessageText:@"hello world!"];
    [r15 setAlertStyle:0x1];
    [r15 runModal];
}



// Always make sure you clean up after yourself; Not doing so could have grave consequences!
%end

%ctor {
    NSLog(@"!!!!!!inject success!!!!!!!");
}

想要实现的效果是点击了按钮之后say的不再是“hi!”而是“hello world!”
然后我就凭借我多年的编程经验在终端中输入了如下几个我根本不知道是在干什么的命名:

$THEOS/bin/logos.pl ./Tweak.xm > ~/desktop/target/abc
clang -shared -undefined dynamic_lookup -o ~/Desktop/SayHi/SayHi.app/Contents/MacOS/lib.dylib ~/desktop/target/abc
./optool install -c load -p @executable_path/lib.dylib -t ~/Desktop/SayHi/SayHi.app/Contents/MacOS/SayHi

各位千万不要问我我在干什么,因为我也不知道这是在干什么,总之我就执行了上面一波操作,结果自然是:


当然这就很奇怪是不是,毕竟我注入Interface Inspector至少是直接闪退吧,这个什么都没有发生就很奇怪。所以我认为是我的Logos写错了,于是我修改了demo的代码,如下:

#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // Do any additional setup after loading the view.
}

- (NSString *)hi {
    return @"hi!";
}

- (IBAction)sayHi:(NSButton *)sender {
    NSAlert *alert = [NSAlert.alloc init];
    alert.messageText = [self hi];
    alert.alertStyle = NSAlertStyleInformational;
    [alert runModal];
}


- (void)setRepresentedObject:(id)representedObject {
    [super setRepresentedObject:representedObject];

    // Update the view, if already loaded.
}


@end

然后再次编写了一个我觉得再错我的智商估计要告别逆向界的Logos,如下:

%config(generator=internal)

// You don't need to #include <substrate.h>, it will be done automatically, as will
// the generation of a class list and an automatic constructor.
#import <Foundation/Foundation.h>

%hook ViewController


// Hooking an instance method with an argument.
- (NSString *)hi {
	return @"hello world!";
}



// Always make sure you clean up after yourself; Not doing so could have grave consequences!
%end

%ctor {
    NSLog(@"!!!!!!inject success!!!!!!!");
}

然后再次achieve了一个demo程序,再次执行下面的天书命令:

$THEOS/bin/logos.pl ./Tweak.xm > ~/desktop/target/abc
clang -shared -undefined dynamic_lookup -o ~/Desktop/SayHi/SayHi.app/Contents/MacOS/lib.dylib ~/desktop/target/abc
./optool install -c load -p @executable_path/lib.dylib -t ~/Desktop/SayHi/SayHi.app/Contents/MacOS/SayHi

结果当然是什么都没有发生,于是诞生了这个帖子,不知道给位大大能否告诉在下我到底是哪里错了,还有我是否应该就此退出 ~~逆向界~~

Posts: 3

Participants: 2

Read full topic

[[UIApp keyWindow] recursiveDescription].toString() 打印显示不全是什么原因?

Class-dump提取头文件失败,求解谢谢


iOS11上通过LSApplicationWorkspace获取应用列表失败

$
0
0

@Quakerfang wrote:

(非越狱)通过LSApplicationWorkspace的实例方法allInstalledApplications返回空数组,iOS10上返回的是已安装app列表,据说iOS11上对LSApplicationWorkspace这个类做了权限控制,请问有没有方法解决

Posts: 1

Participants: 1

Read full topic

HookZz & HookZzModules Release

$
0
0

@spiderzz wrote:

前言

花了有些时间写这个 hookzz 框架. 白话文说下, 单指令的 hook, 无惧短函数和不定参数函数, 可以 hook 指令地址, 可以 RuntimeCodePatch, 还有很多其他玩法. Move to HookZz

下面直接复制粘贴 README.md 了.

HookZzModules 是基于 HookZz 搞得一些模块. 可以在更方便的在 反调试 / hook_objc_msgSend / hook_MGCopyAnswer 做一些工作.

如果希望了解原理请 Move to HookFrameworkDesign

What is HookZz ?

a cute hook framwork.

still developing, for arm64/IOS now!

ref to: frida-gum and minhook and substrate.

special thanks to frida-gum's perfect code and modular architecture, frida is aircraft carrier, HookZz is boat.

Features

  • HookZz-Modules help you to hook.

  • the power to access registers directly

  • hook function with replace_call

  • hook function with pre_call and post_call

  • hook address(a piece of code) with pre_call and half_call

  • (almost)only one instruction to hook(i.e.hook short funciton, even only one instruction)

  • runtime code patch, without codesign limit

  • it's cute

Getting Started

Move to HookZz Getting Started

How it works ?

Move to HookFrameworkDesign.md

Docs

Move to HookZz docs

Example

Move to HookZz example

Modules

Move to HookZzModules

Quick Example No.1

Read It Carefully!

#include "hookzz.h"
#include <string.h>
#include <stdarg.h>
#include <stdio.h>

int (*orig_printf)(const char * restrict format, ...);
int fake_printf(const char * restrict format, ...) {
    puts("call printf");

    char *stack[16];
    va_list args;
    va_start(args, format);
    memcpy(stack, args, 8 * 16);
    va_end(args);

    // how to hook variadic function? fake a original copy stack.
    // [move to detail-1](http://jmpews.github.io/2017/08/29/pwn/%E7%9F%AD%E5%87%BD%E6%95%B0%E5%92%8C%E4%B8%8D%E5%AE%9A%E5%8F%82%E6%95%B0%E7%9A%84hook/)
    // [move to detail-2](https://github.com/jmpews/HookZzModules/tree/master/AntiDebugBypass)
    int x = orig_printf(format, stack[0], stack[1], stack[2], stack[3], stack[4], stack[5], stack[6], stack[7], stack[8], stack[9], stack[10], stack[11], stack[12], stack[13], stack[14], stack[15]);
    return x;
}

void printf_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {
    puts((char *)rs->general.regs.x0);
    STACK_SET(callstack, "format", rs->general.regs.x0, char *);
    puts("printf-pre-call");
}

void printf_post_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {
    if(STACK_CHECK_KEY(callstack, "format")) {
        char *format = STACK_GET(callstack, "format", char *);
        puts(format);
    }
    puts("printf-post-call");
}

__attribute__((constructor)) void test_hook_printf()
{
    void *printf_ptr = (void *)printf;

    ZzBuildHook((void *)printf_ptr, (void *)fake_printf, (void **)&orig_printf, printf_pre_call, printf_post_call);
    ZzEnableHook((void *)printf_ptr);
    printf("HookZzzzzzz, %d, %p, %d, %d, %d, %d, %d, %d, %d\n",1, (void *)2, 3, (char)4, (char)5, (char)6 , 7, 8 , 9);
}

breakpoint with lldb. Read It Carefully!

(lldb) disass -s 0x1815f61d8 -c 3
libsystem_c.dylib`printf:
    0x1815f61d8 <+0>: sub    sp, sp, #0x30             ; =0x30 
    0x1815f61dc <+4>: stp    x20, x19, [sp, #0x10]
    0x1815f61e0 <+8>: stp    x29, x30, [sp, #0x20]
(lldb) c
Process 41408 resuming
HookZzzzzzz, %d, %p, %d, %d, %d, %d, %d, %d, %d

printf-pre-call
call printf
HookZzzzzzz, 1, 0x2, 3, 4, 5, 6, 7, 8, 9
HookZzzzzzz, %d, %p, %d, %d, %d, %d, %d, %d, %d

printf-post-call
(lldb) disass -s 0x1815f61d8 -c 3
libsystem_c.dylib`printf:
    0x1815f61d8 <+0>: b      0x1795f61d8
    0x1815f61dc <+4>: stp    x20, x19, [sp, #0x10]
    0x1815f61e0 <+8>: stp    x29, x30, [sp, #0x20]

Quick Example No.2

Read It Carefully!

#include "hookzz.h"
#include <stdio.h>
#include <unistd.h>

static void hack_this_function()
{
#ifdef __arm64__
    __asm__("mov X0, #0\n"
            "mov w16, #20\n"
            "svc #0x80");
#endif
}

static void sorry_to_exit()
{
#ifdef __arm64__
    __asm__("mov X0, #0\n"
            "mov w16, #1\n"
            "svc #0x80");
#endif
}

void getpid_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {
    unsigned long request = *(unsigned long *)(&rs->general.regs.x16);
    printf("request(x16) is: %ld\n", request);
    printf("x0 is: %ld\n", (long)rs->general.regs.x0);
}

void getpid_half_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {
    pid_t x0 = (pid_t)(rs->general.regs.x0);
    printf("getpid() return at x0 is: %d\n", x0);
}

__attribute__((constructor)) void test_hook_address()
{
    void *hack_this_function_ptr = (void *)hack_this_function;
    ZzBuildHookAddress(hack_this_function_ptr + 8, hack_this_function_ptr + 12, getpid_pre_call, getpid_half_call);
    ZzEnableHook((void *)hack_this_function_ptr + 8);

    void *sorry_to_exit_ptr = (void *)sorry_to_exit;
    unsigned long nop_bytes = 0xD503201F;
    ZzRuntimeCodePatch((unsigned long)sorry_to_exit_ptr + 8, (zpointer)&nop_bytes, 4);

    hack_this_function();
    sorry_to_exit();

    printf("hack success -.0\n");
}

breakpoint with lldb. Read It Carefully!

(lldb) disass -n hack_this_function
test_hook_address.dylib`hack_this_function:
    0x1000b0280 <+0>:  mov    x0, #0x0
    0x1000b0284 <+4>:  mov    w16, #0x14
    0x1000b0288 <+8>:  svc    #0x80
    0x1000b028c <+12>: ret    

(lldb) disass -n sorry_to_exit
test_hook_address.dylib`sorry_to_exit:
    0x1000b0290 <+0>:  mov    x0, #0x0
    0x1000b0294 <+4>:  mov    w16, #0x1
    0x1000b0298 <+8>:  svc    #0x80
    0x1000b029c <+12>: ret    

(lldb) c
Process 41414 resuming
request(x16) is: 20
x0 is: 0
getpid() return at x0 is: 41414
hack success -.0
(lldb) disass -n hack_this_function
test_hook_address.dylib`hack_this_function:
    0x1000b0280 <+0>:  mov    x0, #0x0
    0x1000b0284 <+4>:  mov    w16, #0x14
    0x1000b0288 <+8>:  b      0x1001202cc
    0x1000b028c <+12>: ret    

(lldb) disass -n sorry_to_exit
test_hook_address.dylib`sorry_to_exit:
    0x1000b0290 <+0>:  mov    x0, #0x0
    0x1000b0294 <+4>:  mov    w16, #0x1
    0x1000b0298 <+8>:  nop    
    0x1000b029c <+12>: ret

Posts: 1

Participants: 1

Read full topic

HookFramework 架构设计

$
0
0

@spiderzz wrote:

一般来说可以分为以下几个模块

  1. 内存分配 模块
  2. 指令写 模块
  3. 指令读 模块
  4. 指令修复 模块
  5. 跳板 模块
  6. 调度器 模块
  7. 栈 模块

1. 内存分配 模块

需要分配部分内存用于写入指令, 这里需要关注两个函数都是关于内存属性相关的. 1. 如何使内存 可写 2. 如何使内存 可执行 3. 如何分配相近的内存来达到 near jump

这一部分与具体的操作系统有关. 比如 darwin 下分配内存使用 mmap 实际使用的是 mach_vm_allocate. move to detail.

在 lldb 中可以通过 memory region address 查看地址的内存属性.

当然这里也存在一个巨大的坑, IOS 下无法分配 rwx 属性的内存页. 这导致 inlinehook 无法在非越狱系统上使用, 并且只有 MobileSafari 才有 VM_FLAGS_MAP_JIT 权限. 具体解释请参下方 [坑 - rwx 与 codesigning].

另一个坑就是如何在 hook 目标周围分配内存, 如果可以分配到周围的内存, 可以直接使用 b 指令进行相对地址跳(near jump), 从而可以可以实现单指令的 hook.

举个例子比如 b label, 在 armv8 中的可以想在 +-128MB 范围内进行 near jump, 具体可以参考 ARM Architecture Reference Manual ARMv8, for ARMv8-A architecture profile Page: C6-550.

这里可以有三个尝试.

  1. 使用 mmapMAP_FIXED 尝试在周围地址分配内存页, 成功几率小.

  2. 尝试使用 vm_region_recurse_64 搜索 protectionPROT_EXEC & PROT_READcode cave. (通常用来暴力查找 dyld 的地址)

  3. 尝试搜索内存空洞(code cave), 搜索 __text 这个 section 其实更准确来说是搜索 __TEXT 这个 segment. 由于内存页对齐的原因以及其他原因很容易出现 code cave. 所以只需要搜索这个区间内的 00 即可, 00 本身就是无效指令, 所以可以判断该位置无指令使用.

当然还可以有强制相对跳(double jump), 直接对 +-128MB 内选一个地址强制 code patch 并修复.

__asm__ {
	// 第一次绝对地址跳, 跳转到修复模块, 执行正常流程
	"ldr x17, #0x8\n"
	"b #0xc\n"
	".long\n"
	".long\n"
	"br x17"

	// double jump, 跳转到 on_enter_trampoline
	"ldr x17, #0x8\n"
	"b #0xc\n"
	".long\n"
	".long\n"
	"br x17"
}

2. 指令写 模块

先说坑, 非越狱状态下不允许设置 rw-r-x, 或者 设置 r-xrx-. 具体解释请参考下方坑 [坑-rwx 与 codesigning].

其实这里的指令写有种简单的方法, 就是在本地生成指令的16进制串, 之后直接写即可. 但这种应该是属于 hardcode.

这里使用 frida-gumCydiaSubstrace 都用的方法, 把需要用到的指令都写成一个小函数.

例如:

// frida-gum/gum/arch-arm64/gumarm64writer.c
void
gum_arm64_writer_put_ldr_reg_address (GumArm64Writer * self,
                                      arm64_reg reg,
                                      GumAddress address)
{
  gum_arm64_writer_put_ldr_reg_u64 (self, reg, (guint64) address);
}

void
gum_arm64_writer_put_ldr_reg_u64 (GumArm64Writer * self,
                                  arm64_reg reg,
                                  guint64 val)
{
  GumArm64RegInfo ri;

  gum_arm64_writer_describe_reg (self, reg, &ri);

  g_assert_cmpuint (ri.width, ==, 64);

  gum_arm64_writer_add_literal_reference_here (self, val);
  gum_arm64_writer_put_instruction (self,
      (ri.is_integer ? 0x58000000 : 0x5c000000) | ri.index);
}

其实有另外一个小思路, 有一点小不足, 就是确定指令片段的长度, 但其实也有解决方法, 可以放几条特殊指令作为结尾标记.

先使用内联汇编写一个函数.

__attribute__((__naked__)) static void ctx_save() {
  __asm__ volatile(

      /* reserve space for next_hop */
      "sub sp, sp, #(2*8)\n"

      /* save {q0-q7} */
      "sub sp, sp, #(8*16)\n"
      "stp q6, q7, [sp, #(6*16)]\n"
      "stp q4, q5, [sp, #(4*16)]\n"
      "stp q2, q3, [sp, #(2*16)]\n"
      "stp q0, q1, [sp, #(0*16)]\n"

      /* save {x1-x30} */
      "sub sp, sp, #(30*8)\n"
      "stp fp, lr, [sp, #(28*8)]\n"
      "stp x27, x28, [sp, #(26*8)]\n"
      "stp x25, x26, [sp, #(24*8)]\n"
      "stp x23, x24, [sp, #(22*8)]\n"
      "stp x21, x22, [sp, #(20*8)]\n"
      "stp x19, x20, [sp, #(18*8)]\n"
      "stp x17, x18, [sp, #(16*8)]\n"
      "stp x15, x16, [sp, #(14*8)]\n"
      "stp x13, x14, [sp, #(12*8)]\n"
      "stp x11, x12, [sp, #(10*8)]\n"
      "stp x9, x10, [sp, #(8*8)]\n"
      "stp x7, x8, [sp, #(6*8)]\n"
      "stp x5, x6, [sp, #(4*8)]\n"
      "stp x3, x4, [sp, #(2*8)]\n"
      "stp x1, x2, [sp, #(0*8)]\n"

      /* save sp, x0 */
      "sub sp, sp, #(2*8)\n"
      "add x1, sp, #(2*8 + 8*16 + 30*8 + 2*8)\n"
      "stp x1, x0, [sp, #(0*8)]\n"

      /* alignment padding + dummy PC */
      "sub sp, sp, #(2*8)\n");
}

之后直接复制这块函数内存数据即可, 这一般适合那种指令片段堆.

void ZzThunkerBuildEnterThunk(ZzWriter *writer)
{

    // pop x17
    writer_put_ldr_reg_reg_offset(writer, ARM64_REG_X17, ARM64_REG_SP, 0);
    writer_put_add_reg_reg_imm(writer, ARM64_REG_SP, ARM64_REG_SP, 16);

    writer_put_bytes(writer, (void *)ctx_save, 26 * 4);

    // call `function_context_begin_invocation`
    writer_put_bytes(writer, (void *)pass_enter_func_args, 4 * 4);
    writer_put_ldr_reg_address(
        writer, ARM64_REG_X17,
        (zaddr)(zpointer)function_context_begin_invocation);
    writer_put_blr_reg(writer, ARM64_REG_X17);

    writer_put_bytes(writer, (void *)ctx_restore, 23 * 4);
}

3. 指令读 模块

这一部分实际上就是 disassembler, 这一部分可以直接使用 capstone, 这里需要把 capstone 编译成多种架构.

4. 指令修复 模块

这里的指令修复主要是发生在 hook 函数头几条指令, 由于备份指令到另一个地址, 这就需要对所有 PC(IP) 相关指令进行修复. 对于确定的哪些指令需要修复可以参考 Move to <解析ARM和x86_x64指令格式>.

大致的思路就是: 判断 capstone 读取到的指令 ID, 针对特定指令写一个小函数进行修复.

例如在 frida-gum 中:

frida-gum/gum/arch-arm64/gumarm64relocator.c
static gboolean
gum_arm64_relocator_rewrite_b (GumArm64Relocator * self,
                               GumCodeGenCtx * ctx)
{
  const cs_arm64_op * target = &ctx->detail->operands[0];

  (void) self;

  gum_arm64_writer_put_ldr_reg_address (ctx->output, ARM64_REG_X16,
      target->imm);
  gum_arm64_writer_put_br_reg (ctx->output, ARM64_REG_X16);

  return TRUE;
}

5. 跳板 模块

跳板模块的设计是希望各个模块的实现更浅的耦合, 跳板函数主要作用就是进行跳转, 并准备 跳转目标 需要的参数. 举个例子, 被 hook 的函数经过入口跳板(enter_trampoline), 跳转到调度函数(enter_chunk), 需要被 hook 的函数相关信息等, 这个就需要在构造跳板时完成.

6. 调度 模块

可以理解为所有被 hook 的函数都必须经过的函数, 类似于 objc_msgSend, 在这里通过栈返回值来控制函数(replace_call, pre_call, half_call, post_call)调用顺序.

本质有些类似于 objc_msgSend 所有的被 hook 的函数都在经过 enter_trampoline 跳板后, 跳转到 enter_thunk, 在此进行下一步的跳转判断决定, 并不是直接跳转到 replace_call.

7. 栈模块

如果希望在 pre_callpost_call 使用同一个局部变量, 就想在同一个函数内一样. 在 frida-js 中也就是 this 这个关键字. 这就需要自建函数栈, 模拟栈的行为. 同时还要避免线程冲突, 所以需要使用 thread local variable, 为每一个线程中的每一个 hook-entry 添加线程栈, 同时为每一次调用添加函数栈. 所以这里存在两种栈. 1. 线程栈(保存了该 hook-entry 的所有当前函数调用栈) 2. 函数调用栈(本次函数调用时的栈)

ldr 指令

在进行指令修复时, 需要需要将 PC 相关的地址转换为绝对地址, 其中涉及到保存地址到寄存器. 一般来说是使用指令 ldr. 也就是说如何完成该函数 writer_put_ldr_reg_address(relocate_writer, ARM64_REG_X17, target_addr);

frida-gum 的实现原理是, 有一个相对地址表, 在整体一段写完后进行修复.

void
gum_arm64_writer_put_ldr_reg_u64 (GumArm64Writer * self,
                                  arm64_reg reg,
                                  guint64 val)
{
  GumArm64RegInfo ri;

  gum_arm64_writer_describe_reg (self, reg, &ri);

  g_assert_cmpuint (ri.width, ==, 64);

  gum_arm64_writer_add_literal_reference_here (self, val);
  gum_arm64_writer_put_instruction (self,
      (ri.is_integer ? 0x58000000 : 0x5c000000) | ri.index);
}

在 HookZz 中的实现, 直接将地址写在指令后, 之后使用 b 到正常的下一条指令, 从而实现将地址保存到寄存器.

void writer_put_ldr_reg_address(ZzWriter *self, arm64_reg reg, zaddr address)
{
    writer_put_ldr_reg_imm(self, reg, (zuint)0x8);
    writer_put_b_imm(self, (zaddr)0xc);
    writer_put_bytes(self, (zpointer)&address, sizeof(address));
}

也就是下面的样子.

__asm__ {
	"ldr x17, #0x8\n"
	"b #0xc\n"
	".long\n"
	".long\n"
	"br x17"
}

寄存器污染

在进行 inlinehook 需要进行各种跳转, 通常会以以下模板进行跳转.

0:  ldr x16, 8;
4:  br x16;
8:  0x12345678
12: 0x00000000

问题在于这会造成 x16 寄存器被污染(arm64 中 svc #0x80 使用 x16 传递系统调用号) 所以这里有两种思路解决这个问题.

思路一:

在使用寄存器之前进行 push, 跳转后 pop, 这里存在一个问题就是在原地址的几条指令进行 patch code 时一定会污染一个寄存器(也不能说一定, 如果这时进行压栈, 在之后的 invoke_trampline 会导致函数栈发生改变, 此时有个解决方法可以 pop 出来, 由 hook-entry 或者其他变量暂时保存, 但这时需要处理锁的问题. )

思路二:

挑选合适的寄存器, 不考虑污染问题. 这时可以参考, 下面的资料, 选择 x16 or x17, 或者自己做一个实验 otool -tv ~/Downloads/DiSpecialDriver64 > ~/Downloads/DiSpecialDriver64.txt 通过 dump 一个 arm64 程序的指令, 来判断哪个寄存器用的最少, 但是不要使用 x18 寄存器, 你对该寄存器的修改是无效的.

Tips: 之前还想过为对每一个寄存器都做适配, 用户可以选择当前的 hook-entry 选择哪一个寄存器作为临时寄存器.

参考资料:

PAGE: 9-3
Programmer’s Guide for ARMv8-A
9.1 Register use in the AArch64 Procedure Call Standard 
9.1.1 Parameters in general-purpose registers

这里也有一个问题, 这也是 frida-gum 中遇到一个问题, 就是对于 svc #0x80 类系统调用, 系统调用号(syscall number)的传递是利用 x16 寄存器进行传递的, 所以本框架使用 x17 寄存器, 并且在传递参数时使用 push & pop, 在跳转后恢复 x17, 避免了一个寄存器的使用.

rwxcodesigning

对于非越狱, 不能分配可执行内存, 不能进行 code patch.

两篇原理讲解 codesign 的原理

https://papers.put.as/papers/ios/2011/syscan11_breaking_ios_code_signing.pdf
http://www.newosxbook.com/articles/CodeSigning.pdf

以及源码分析如下:

crash 异常如下, 其中 0x0000000100714000 是 mmap 分配的页.

Exception Type:  EXC_BAD_ACCESS (SIGKILL - CODESIGNING)
Exception Subtype: unknown at 0x0000000100714000
Termination Reason: Namespace CODESIGNING, Code 0x2
Triggered by Thread:  0

寻找对应的错误码

xnu-3789.41.3/bsd/sys/reason.h
/*
 * codesigning exit reasons
 */
#define CODESIGNING_EXIT_REASON_TASKGATED_INVALID_SIG 1
#define CODESIGNING_EXIT_REASON_INVALID_PAGE          2
#define CODESIGNING_EXIT_REASON_TASK_ACCESS_PORT      3

找到对应处理函数, 请仔细阅读注释里内容, 不做解释了.

# xnu-3789.41.3/osfmk/vm/vm_fault.c:2632

	/* If the map is switched, and is switch-protected, we must protect
	 * some pages from being write-faulted: immutable pages because by 
	 * definition they may not be written, and executable pages because that
	 * would provide a way to inject unsigned code.
	 * If the page is immutable, we can simply return. However, we can't
	 * immediately determine whether a page is executable anywhere. But,
	 * we can disconnect it everywhere and remove the executable protection
	 * from the current map. We do that below right before we do the 
	 * PMAP_ENTER.
	 */
	cs_enforcement_enabled = cs_enforcement(NULL);

	if(cs_enforcement_enabled && map_is_switched && 
	   map_is_switch_protected && page_immutable(m, prot) && 
	   (prot & VM_PROT_WRITE))
	{
		return KERN_CODESIGN_ERROR;
	}

	if (cs_enforcement_enabled && page_nx(m) && (prot & VM_PROT_EXECUTE)) {
		if (cs_debug)
			printf("page marked to be NX, not letting it be mapped EXEC\n");
		return KERN_CODESIGN_ERROR;
	}

	if (cs_enforcement_enabled &&
	    !m->cs_validated &&
	    (prot & VM_PROT_EXECUTE) &&
	    !(caller_prot & VM_PROT_EXECUTE)) {
		/*
		 * FOURK PAGER:
		 * This page has not been validated and will not be
		 * allowed to be mapped for "execute".
		 * But the caller did not request "execute" access for this
		 * fault, so we should not raise a code-signing violation
		 * (and possibly kill the process) below.
		 * Instead, let's just remove the "execute" access request.
		 * 
		 * This can happen on devices with a 4K page size if a 16K
		 * page contains a mix of signed&executable and
		 * unsigned&non-executable 4K pages, making the whole 16K
		 * mapping "executable".
		 */
		prot &= ~VM_PROT_EXECUTE;
	}

	/* A page could be tainted, or pose a risk of being tainted later.
	 * Check whether the receiving process wants it, and make it feel
	 * the consequences (that hapens in cs_invalid_page()).
	 * For CS Enforcement, two other conditions will 
	 * cause that page to be tainted as well: 
	 * - pmapping an unsigned page executable - this means unsigned code;
	 * - writeable mapping of a validated page - the content of that page
	 *   can be changed without the kernel noticing, therefore unsigned
	 *   code can be created
	 */
	if (!cs_bypass &&
	    (m->cs_tainted ||
	     (cs_enforcement_enabled &&
	      (/* The page is unsigned and wants to be executable */
	       (!m->cs_validated && (prot & VM_PROT_EXECUTE))  ||
	       /* The page should be immutable, but is in danger of being modified
		* This is the case where we want policy from the code directory -
		* is the page immutable or not? For now we have to assume that 
		* code pages will be immutable, data pages not.
		* We'll assume a page is a code page if it has a code directory 
		* and we fault for execution.
		* That is good enough since if we faulted the code page for
		* writing in another map before, it is wpmapped; if we fault
		* it for writing in this map later it will also be faulted for executing 
		* at the same time; and if we fault for writing in another map
		* later, we will disconnect it from this pmap so we'll notice
		* the change.
		*/
	      (page_immutable(m, prot) && ((prot & VM_PROT_WRITE) || m->wpmapped))
	      ))
		    )) 
	{

其他文章:

Later on, whenever a page fault occurs the vm_fault function in vm_fault.c is called. During the page fault the signature is validated if necessary. The signature will need to be validated if the page is mapped in user space, if the page belongs to a code-signed object, if the page will be writable or simply if it has not previously been validated. Validation happens in the vm_page_validate_cs function inside vm_fault.c (the validation process and how it is enforced continually and not only at load time is interesting, see Charlie Miller’s book for more details).

If for some reason the page cannot be validated, the kernel checks whether the CS_KILL flag has been set and kills the process if necessary. There is a major distinction between iOS and OS X regarding this flag. All iOS processes have this flag set whereas on OS X, although code signing is checked it is not set and thus not enforced.

In our case we can safely assume that the (missing) code signature couldn’t be verified leading to the kernel killing the process.


Posts: 1

Participants: 1

Read full topic

反调试与绕过的奇淫技巧

$
0
0

@spiderzz wrote:

任何带特征的检测都是不安全的 & 隐而不发(@Ouroboros)

Move to AntiDebugBypass on github

代码依赖于 HookZz, 一个 hook 框架

前言

对于应用安全甲方一般会在这三个方面做防御.

按逻辑分类的话应该应该分为这几类, 但如果从实现原理的话, 应该分为两类, 用API实现的不用API实现的(这说的不用 API 实现, 不是指换成 inine 函数就行) . 首先使用 API 实现基本统统沦陷. 直接通过指令实现的机制还有一丝存活的可能. 逻辑的话应该分为, 反调试, 反注入, 越狱检测, hook 检测.

本文所有相关仅仅针对 aarch64.

假设读者对下知识有了解

  1. arm64 相关知识
  2. macho 文件结构以及加载相关知识
  3. dyld 链接 dylib 相关函数等知识

如何 hook 不定参数函数?

技巧在于伪造原栈的副本. 具体参考下文.

通常来说必备手册

// 指令格式等细节
ARM Architecture Reference Manual(ARMv8, for ARMv8-A architecture profile)
https://static.docs.arm.com/ddi0487/b/DDI0487B_a_armv8_arm.pdf

ARM Cortex -A Series Programmer’s Guide for ARMv8-A
http://infocenter.arm.com/help/topic/com.arm.doc.den0024a/DEN0024A_v8_architecture_PG.pdf

Calling conventions for different C++ compilers and operating systems
http://www.agner.org/optimize/calling_conventions.pdf

Procedure Call Standard for the ARM 64-bit Architecture (AArch64)
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf

通常来说必备源码

// dyld
https://opensource.apple.com/tarballs/dyld/

// xnu
https://opensource.apple.com/tarballs/xnu/

// objc
https://opensource.apple.com/tarballs/objc4/
https://github.com/RetVal/objc-runtime (可编译)

// cctools
https://opensource.apple.com/tarballs/cctools (很全的头文件)

反调试

反调试从逻辑上分大概分为, 一种是直接屏蔽调试器挂载, 另一种就是根据特征手动检测调试器挂载. 当然也分为使用函数实现 和 直接使用内联 asm 实现.

ptrace 反调试

ptrace 反调试可以使用四种方法实现.

1. 直接使用 ptrace 函数

这里使用的是 dlopen + dysym.

typedef int (*PTRACE_T)(int request, pid_t pid, caddr_t addr, int data);
static void AntiDebug_001() {
    void *handle = dlopen(NULL, RTLD_GLOBAL | RTLD_NOW);
    PTRACE_T ptrace_ptr = dlsym(handle, "ptrace");
    ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);
}

当然也可以基于 runtime 符号查找.

// runtime to get symbol address, but must link with `
// -Wl,-undefined,dynamic_lookup` or you can use `dlopen` and `dlsym`
extern int ptrace(int request, pid_t pid, caddr_t addr, int data);
static void AntiDebug_002() { ptrace(PT_DENY_ATTACH, 0, 0, 0); }

2. 使用 syscall 实现

void AntiDebug_005() { syscall(SYS_ptrace, PT_DENY_ATTACH, 0, 0, 0); }

3. 内联 svc + ptrace 实现

其实这种方法等同于直接使用 ptrace, 此时系统调用号是 SYS_ptrace

static __attribute__((always_inline)) void AntiDebug_003() {
#ifdef __arm64__
    __asm__("mov X0, #31\n"
            "mov X1, #0\n"
            "mov X2, #0\n"
            "mov X3, #0\n"
            "mov w16, #26\n"
            "svc #0x80");
#endif
}

4. 内联 svc + syscall + ptrace 实现

其实这种方法等同于使用 syscall(SYS_ptrace, PT_DENY_ATTACH, 0, 0, 0), 这里需要注意, 此时的系统调用号是 0, 也就是 SYS_syscall

static __attribute__((always_inline)) void AntiDebug_004() {
#ifdef __arm64__
    __asm__("mov X0, #26\n"
            "mov X1, #31\n"
            "mov X2, #0\n"
            "mov X3, #0\n"
            "mov X4, #0\n"
            "mov w16, #0\n"
            "svc #0x80");
#endif
}

简单整理下系统调用流程, 只能以 xnu-3789.41.3 源码举例.

Supervisor Call causes a Supervisor Call exception. svc 切换 Exception LevelsEL0(Unprivileged)EL1(Privileged)

上面说的是指令层相关, 再说系统层相关, 使用 svc 进行系统中断调用需要明确 3 个点: 中断号, 系统调用号, 以及参数. 下面以 x86-64 举例.

中断向量表

// xnu-3789.41.3/osfmk/x86_64/idt_table.h
USER_TRAP_SPC(0x80,idt64_unix_scall)
USER_TRAP_SPC(0x81,idt64_mach_scall)
USER_TRAP_SPC(0x82,idt64_mdep_scall)

中断处理函数

// xnu-3789.41.3/osfmk/x86_64/idt64.s
/*
 * System call handlers.
 * These are entered via a syscall interrupt. The system call number in %rax
 * is saved to the error code slot in the stack frame. We then branch to the
 * common state saving code.
 */
		
#ifndef UNIX_INT
#error NO UNIX INT!!!
#endif
Entry(idt64_unix_scall)
	swapgs				/* switch to kernel gs (cpu_data) */
	pushq	%rax			/* save system call number */
	PUSH_FUNCTION(HNDL_UNIX_SCALL)
	pushq	$(UNIX_INT)
	jmp	L_32bit_entry_check

// xnu-3789.41.3/bsd/dev/i386/systemcalls.c
__attribute__((noreturn))
void
unix_syscall64(x86_saved_state_t *state)
{
	thread_t	thread;
	void			*vt;
	unsigned int	code;
	struct sysent	*callp;
	int		args_in_regs;
	boolean_t	args_start_at_rdi;
	int		error;
	struct proc	*p;
	struct uthread	*uthread;
	x86_saved_state64_t *regs;
	pid_t		pid;

	assert(is_saved_state64(state));
	regs = saved_state64(state);
#if	DEBUG
	if (regs->rax == 0x2000800)
		thread_exception_return();
#endif
	thread = current_thread();
	uthread = get_bsdthread_info(thread);

#if PROC_REF_DEBUG
	uthread_reset_proc_refcount(uthread);
#endif

	/* Get the approriate proc; may be different from task's for vfork() */
	if (__probable(!(uthread->uu_flag & UT_VFORK)))
		p = (struct proc *)get_bsdtask_info(current_task());
	else 
		p = current_proc();

	/* Verify that we are not being called from a task without a proc */
	if (__improbable(p == NULL)) {
		regs->rax = EPERM;
		regs->isf.rflags |= EFL_CF;
		task_terminate_internal(current_task());
		thread_exception_return();
		/* NOTREACHED */
	}

	code = regs->rax & SYSCALL_NUMBER_MASK;
	DEBUG_KPRINT_SYSCALL_UNIX(
		"unix_syscall64: code=%d(%s) rip=%llx\n",
		code, syscallnames[code >= nsysent ? SYS_invalid : code], regs->isf.rip);
	callp = (code >= nsysent) ? &sysent[SYS_invalid] : &sysent[code];

系统调用表

xnu-3789.41.3/bsd/kern/syscall.h
#define	SYS_setuid         23
#define	SYS_getuid         24
#define	SYS_geteuid        25
#define	SYS_ptrace         26
#define	SYS_recvmsg        27
#define	SYS_sendmsg        28

反调试检测

这里主要是调试器的检测手段, 很多检测到调试器后使用 exit(-1) 退出程序. 这里很容易让 cracker 断点到 exit 函数上. 其实有一个 trick 就是利用利用系统异常造成 crash. 比如: 覆盖/重写 __TEXT 内容(debugmode 模式下可以对 rx- 内存进行操作).

或者利用内联汇编实现退出, 并清除堆栈(防止暴力 svc patch with nop).

static __attribute__((always_inline)) void asm_exit() {
#ifdef __arm64__
    __asm__("mov X0, #0\n"
            "mov w16, #1\n"
            "svc #0x80\n"

            "mov x1, #0\n"
            "mov sp, x1\n"
            "mov x29, x1\n"
            "mov x30, x1\n"
            "ret");
#endif
}

使用 sysctl 检测

这里在检测时也可以通过 svc 实现.

static int DetectDebug_sysctl() __attribute__((always_inline));
int DetectDebug_sysctl() {
    size_t size = sizeof(struct kinfo_proc);
    struct kinfo_proc info;
    int ret, name[4];
    
    memset(&info, 0, sizeof(struct kinfo_proc));
    
    name[0] = CTL_KERN;
    name[1] = KERN_PROC;
    name[2] = KERN_PROC_PID;
    name[3] = getpid();
    
#if 0
    if ((ret = (sysctl(name, 4, &info, &size, NULL, 0)))) {
        return ret; // sysctl() failed for some reason
    }
#else
    // or change as `AntiDebug_003` and `AntiDebug_004`
    // https://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
    __asm__ volatile("mov x0, %[name_ptr]\n"
                     "mov x1, #4\n"
                     "mov x2, %[info_ptr]\n"
                     "mov x3, %[size_ptr]\n"
                     "mov x4, #0\n"
                     "mov x5, #0\n"
                     "mov w16, #202\n"
                     "svc #0x80"
                     :
                     : [name_ptr] "r"(name), [info_ptr] "r"(&info),
                     [size_ptr] "r"(&size));
#endif
    
    return (info.kp_proc.p_flag & P_TRACED) ? 1 : 0;
}

void AntiDebug_006() {
    if (DetectDebug_sysctl()) {
        asm_exit();
    }
}

使用 isatty 检测

#include <unistd.h>
void AntiDebug_isatty() {
  if (isatty(1)) {
    exit(1);
  } else {
  }
}

使用 ioctl 检测

#include <sys/ioctl.h>
void AntiDebug_ioctl() {
  if (!ioctl(1, TIOCGWINSZ)) {
    exit(1);
  } else {
  }
}

svc 完整性检测

上述的 svc 反调试手段, 可以通过 patch svc #0x80 with nop 轻松绕过. 所以需要校验 svc #0x80 是否被 patch, 一个想当然的方法是在正常的代码中使用 svc 进行 coding, 仔细想想并不合适.

所以另一个想法就是, 使用 svc 实现一个小功能, 之后检测 x0 返回值. 这里使用的是 getpid().

tips: longjmp 本来是用在异常时恢复状态, 这里由于未保存状态. 所以可以让攻击者不能对退出进行断点.

这里使用, 下面一小段内联汇编可以达到相同的目的.

"mov x1, #0\n"
"mov sp, x1\n"
"mov x29, x1\n"
"mov x30, x1\n"
"ret\n"

整体的 svc 完整检测原型如下, 仅做抛砖引玉.

static __attribute__((always_inline)) void check_svc_integrity() {
    int pid;
    static jmp_buf protectionJMP;
#ifdef __arm64__
    __asm__("mov x0, #0\n"
            "mov w16, #20\n"
            "svc #0x80\n"
            "cmp x0, #0\n"
            "b.ne #24\n"
            
            "mov x1, #0\n"
            "mov sp, x1\n"
            "mov x29, x1\n"
            "mov x30, x1\n"
            "ret\n"
            
            "mov %[result], x0\n"
            : [result] "=r" (pid)
            :
            :
            );
    
    if(pid == 0) {
        longjmp(protectionJMP, 1);
    }
#endif
}

绕过

对于使用函数进行反调试可以使用 hook 轻松绕过, 具体的实现, 直接看代码.

syscall 反调试绕过

因为 syscall 反调试有些特殊, 这里需要介绍下如何绕过 syscall 反调试, 使用的是 va_list 进行传递参数. http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf 参考阅读 va_list 相关.

借助 HookZz 有两种方法可以进行绕过

1. 使用 replace_call 绕过

这里的 syscall 使用的是 va_list 传递参数. 所以这里问题在于如何 hook 不定参数函数. 因为在 hook 之后不确定原函数的参数个数. 所以没有办法调用原函数.

所以这里有一个 trick, 在 orig_syscall(number, stack[0], stack[1], stack[2], stack[3], stack[4], stack[5], stack[6], stack[7]); 时伪造了一个栈, 这个栈的内容和原栈相同(应该是大于等于原栈的参数内容). 虽然传递了很多参数, 如果理解 function call 的原理的话, 即使传递了很多参数, 但是只要栈的内容不变, 准确的说的是从低地址到高地址的栈里的内容不变(这里可能多压了很多无用的内容到栈里), 函数调用就不会变.

这里不要使用 large structure, 编译时会使用隐含的 memcpy 最终传入的其实是地址. 大部分注释请参考下文.

int (*orig_syscall)(int number, ...);
int fake_syscall(int number, ...) {
    int request;
    pid_t pid;
    caddr_t addr;
    int data;
    
    // fake stack, why use `char *` ? hah
    char *stack[8];
    
    va_list args;
    va_start(args, number);
    
    // get the origin stack args copy.(must >= origin stack args)
    memcpy(stack, args, 8 * 8);
    
    if (number == SYS_ptrace) {
        request = va_arg(args, int);
        pid = va_arg(args, pid_t);
        addr = va_arg(args, caddr_t);
        data = va_arg(args, int);
        va_end(args);
        if (request == PT_DENY_ATTACH) {
            NSLog(@"[AntiDebugBypass] catch 'syscall(SYS_ptrace, PT_DENY_ATTACH, 0, "
                  @"0, 0)' and bypass.");
            return 0;
        }
    } else {
        va_end(args);
    }
    
    // must understand the principle of `function call`. `parameter pass` is
    // before `switch to target` so, pass the whole `stack`, it just actually
    // faked an original stack. Do not pass a large structure,  will be replace with
    // a `hidden memcpy`.
    int x = orig_syscall(number, stack[0], stack[1], stack[2], stack[3], stack[4],
                         stack[5], stack[6], stack[7]);
    return x;
}

2. 使用 pre_call 绕过

这种方法需要查看 syscall 的汇编实现, 来确定 PT_DENY_ATTACH 放在哪一个寄存器.

libsystem_kernel.dylib`__syscall:
    0x1815c0900 <+0>:  ldp    x1, x2, [sp]
    0x1815c0904 <+4>:  ldp    x3, x4, [sp, #0x10]
    0x1815c0908 <+8>:  ldp    x5, x6, [sp, #0x20]
    0x1815c090c <+12>: ldr    x7, [sp, #0x30]
    0x1815c0910 <+16>: mov    x16, #0x0
    0x1815c0914 <+20>: svc    #0x80
    0x1815c0918 <+24>: b.lo   0x1815c0930               ; <+48>
    0x1815c091c <+28>: stp    x29, x30, [sp, #-0x10]!
    0x1815c0920 <+32>: mov    x29, sp
    0x1815c0924 <+36>: bl     0x1815a6dc0               ; cerror
    0x1815c0928 <+40>: mov    sp, x29
    0x1815c092c <+44>: ldp    x29, x30, [sp], #0x10
    0x1815c0930 <+48>: ret

可以看到调用如果 x0SYS_ptrace, 那么 PT_DENY_ATTACH 存放在 [sp].

void syscall_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {
    int num_syscall;
    int request;
    zpointer sp;
    num_syscall = (int)(uint64_t)(rs->general.regs.x0);
    if (num_syscall == SYS_ptrace) {
        sp = (zpointer)(rs->sp);
        request = *(int *)sp;
        if (request == PT_DENY_ATTACH) {
            *(long *)sp = 10;
            NSLog(@"[AntiDebugBypass] catch 'syscall(SYS_ptrace, PT_DENY_ATTACH, 0, "
                  @"0, 0)' and bypass.");
        }
    }
}
__attribute__((constructor)) void patch_syscall_by_pre_call() {
    zpointer syscall_ptr = (void *)syscall;
    #if 0
    ZzBuildHook((void *)syscall_ptr, NULL, NULL, syscall_pre_call, NULL);
    ZzEnableHook((void *)syscall_ptr);
    #endif
}
// --- end ---

svc #0x80 反调试绕过

这里介绍关键是介绍如何对 svc 反调试的绕过.

上面已经对 svc 进行了简单的介绍. 所以理所当然想到的是希望通过 syscall hook, 劫持 system call table(sysent) . 这里相当于实现 syscall hook. 但是难点之一是需要找到 system call table(sysent), 这一步可以通过 joker, 对于 IOS 10.x 可以参考 http://ioshackerwiki.com/syscalls/, 难点之二是作为 kext 加载. 可以参考 附录, 对于具体的 kernel patch 没有做过深入研究, 应该可以参考 comex 的 datautils0

ok, 接下来使用另一种思路对绕过, 其实也就是 code patch + hook address. 对 __TEXT 扫描 svc #0x80 指令, 对于 cracker 来说, 在 __TEXT 段使用 svc #0x80 具有一定的反调试可能, 所以需要对 svc #0x80 进行 hook addres, 这里并不直接对 svc #0x80 进行覆盖操作.

以下代码依赖于 HookZz).

大致原理就是先搜索到 svc #0x80 指令后, 对该指令地址进行 hook, 之后使用 pre_call 修改寄存器的值.

void hook_svc_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {
    int num_syscall;
    int request;
    num_syscall = (int)(uint64_t)(rs->general.regs.x16);
    request = (int)(uint64_t)(rs->general.regs.x0);
    
    if (num_syscall == SYS_syscall) {
        int arg1 = (int)(uint64_t)(rs->general.regs.x1);
        if (request == SYS_ptrace && arg1 == PT_DENY_ATTACH) {
            *(unsigned long *)(&rs->general.regs.x1) = 10;
            NSLog(@"[AntiDebugBypass] catch 'SVC #0x80; syscall(ptrace)' and bypass");
        }
        
    } else if (num_syscall == SYS_ptrace) {
        request = (int)(uint64_t)(rs->general.regs.x0);
        if (request == PT_DENY_ATTACH) {
            *(unsigned long *)(&rs->general.regs.x0) = 10;
            NSLog(@"[AntiDebugBypass] catch 'SVC-0x80; ptrace' and bypass");
        }
    } else if(num_syscall == SYS_sysctl) {
        STACK_SET(callstack, (char *)"num_syscall", num_syscall, int);
        STACK_SET(callstack, (char *)"info_ptr", rs->general.regs.x2, zpointer);
    }
}

void hook_svc_half_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {
    // emmm... little long...
    if(STACK_CHECK_KEY(callstack, (char *)"num_syscall")) {
        int num_syscall = STACK_GET(callstack, (char *)"num_syscall", int);
        struct kinfo_proc *info = STACK_GET(callstack, (char *)"info_ptr", struct kinfo_proc *);
        if (num_syscall == SYS_sysctl)
        {
            NSLog(@"[AntiDebugBypass] catch 'SVC-0x80; sysctl' and bypass");
            info->kp_proc.p_flag &= ~(P_TRACED);
        }
    }
}

__attribute__((constructor)) void hook_svc_x80() {
    zaddr svc_x80_addr;
    zaddr curr_addr, text_start_addr, text_end_addr;
    uint32_t svc_x80_byte = 0xd4001001;
    
    const struct mach_header *header = _dyld_get_image_header(0);
    struct segment_command_64 *seg_cmd_64 = zz_macho_get_segment_64_via_name((struct mach_header_64 *)header, (char *)"__TEXT");
    zsize slide = (zaddr)header - (zaddr)seg_cmd_64->vmaddr;
    
    struct section_64 *sect_64 = zz_macho_get_section_64_via_name((struct mach_header_64 *)header, (char *)"__text");
    
    text_start_addr = slide + (zaddr)sect_64->addr;
    text_end_addr = text_start_addr + sect_64->size;
    curr_addr = text_start_addr;
    
    while (curr_addr < text_end_addr) {
        svc_x80_addr = (zaddr)zz_vm_search_data((zpointer)curr_addr, (zpointer)text_end_addr, (zbyte *)&svc_x80_byte, 4);
        if (svc_x80_addr) {
            NSLog(@"hook svc #0x80 at %p with aslr (%p without aslr)",
                  (void *)svc_x80_addr, (void *)(svc_x80_addr - slide));
            ZzBuildHookAddress((void *)svc_x80_addr, (void *)(svc_x80_addr + 4),
                               hook_svc_pre_call, hook_svc_half_call);
            ZzEnableHook((void *)svc_x80_addr);
            curr_addr = svc_x80_addr + 4;
        } else {
            break;
        }
    }
}

Move to AntiDebugBypass

总结

上文对很多的反调试原理做了总结, 也有一些没有讲到原理. 读者可以自行研究.

附录

// syscall hook
http://siliconblade.blogspot.jp/2013/07/offensive-volatility-messing-with-os-x.html
https://www.defcon.org/images/defcon-17/dc-17-presentations/defcon-17-bosse_eriksson-kernel_patching_on_osx.pdf
http://d.hatena.ne.jp/hon53/20100926/1285476759
https://papers.put.as/papers/ios/2011/SysScan-Singapore-Targeting_The_IOS_Kernel.pdf
https://www.blackhat.com/docs/us-15/materials/us-15-Diquet-TrustKit-Code-Injection-On-iOS-8-For-The-Greater-Good.pdf

ios kext load

https://github.com/LinusHenze/anyKextLoader
https://github.com/Jailbreaks/trident-kloader
https://github.com/saelo/ios-kern-utils
https://github.com/xerub/kexty

Posts: 1

Participants: 1

Read full topic

Ssl的问题

$
0
0

@ysfox wrote:

苹果6,系统10.0.2,yalu102越狱,越狱之后修改了127.0.0.1:22为22,卸载了自带的ssh,ssh现在能连接上,但是SSL失败,提示问题如下
提示问题:! sh: /usr/libexec/sftp-server: No such file or directory

Posts: 4

Participants: 2

Read full topic

Viewing all 5702 articles
Browse latest View live