普通视图

发现新文章,点击刷新页面。
昨天以前掘金专栏-iOS逆向

iOS小技能:【设备日志查看工具】syslog、deviceconsole和socat

2023年3月12日 07:41

本文正在参加「金石计划」

前言

本文介绍iOS设备日志查看工具syslog、deviceconsole和socat,如果上述工具都不满意,你也可以使用Mac系统自带的console控制台进行查看。

>

I syslog

1.1 安装syslog

在cydia搜索syslogd to /var/log/syslog安装即可

1.2 syslog用法

syslog是把系统日志写入到/var/log/syslog文件里,用法很简单,执行tail -f /var/log/syslog就能看到了

如果需要过滤某一应用的日志,只需加上grep即可,比如过滤微信

tail -f /var/log/syslog |grep WeChat

II socat

2.1 安装

  • 在iOS设备安装

使用 APT 0.6 Transitional 安装socat 几乎所有流行的黑客工具都可以在 BigBoss Recommendation tools这个包中找到 ( APT 0.6 Transitional, Git, GNU Debugger, less, make, unzip, wget 和 SQLite 3.x)

apt-get install socat

如果找不到安装包的时候,运行一下 apt-get update, 获得最新的包列表.

2.2 连接到系统日志的sock文件

socat - UNIX-CONNECT:/var/run/lockdown/syslog.sock

  • 进入到命令行交互界面,这时可以输入help查看帮助
iPhone:~ root# socat - UNIX-CONNECT:/var/run/lockdown/syslog.sock

========================
ASL is here to serve you
> 

2.3 日志的查看

输入watch查看,输入stop停止

2.4 清除日志文件数据

cat /dev/null >/var/log/syslog

III deviceconsole

自从iOS8之后,我就习惯使用Mac系统自带的console ,后来发现有些同事的Mac中console 版本低,没有device 选项;于是乎,就推荐他们使用deviceconsole

  • deviceconsole --help
➜  bin git:(master) ✗ deviceconsole --help
Usage: deviceconsole [options]
Options:
 -d      Include connect/disconnect messages in standard out
 -u <udid>    Show only logs from a specific device
 -p <process name>  Show only logs from a specific process

Control-C to disconnect
Mail bug reports and suggestions to <ryan.petrich@medialets.com>
  • knlog -help

    Usage: knlog [options]
    Options:
    -i | --case-insensitive     Make filters case-insensitive
    -f | --filter <string>      Filter include by single word occurrences (case-sensitive)
    -x | --exclude <string>     Filter exclude by single word occurrences (case-sensitive)
    -p | --process <string>     Filter by process name (case-sensitive)
    -u | --udid <udid>          Show only logs from a specific device
    -s | --simulator <version>  Show logs from iOS Simulator
         --debug                Include connect/disconnect messages in standard out
         --use-separators       Skip a line between each line
         --force-color          Force colored text
         --message-only          Display only level and message
    Control-C to disconnect
    
  • 编译之后的可执行文件 knlog

IV shell 脚本完成对日志文件的提取之sed 学习

sed 是一种在线编辑器,它一次处理一行内容。Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。

文件内容并没有改变,除非你使用重定向存储输出。

4.1 ## sed的参数

    sed [-nefr] [动作]
选项与参数:
-n :使用安静(silent)模式。在一般 sed 的用法中,所有来自 STDIN 的数据一般都会被列出到终端上。但如果加上 -n 参数后,则只有经过sed 特殊处理的那一行(或者动作)才会被列出来。
-e :直接在命令列模式上进行 sed 的动作编辑;
-f :直接将 sed 的动作写在一个文件内, -f filename 则可以运行 filename 内的 sed 动作;
-r :sed 的动作支持的是延伸型正规表示法的语法。(默认是基础正规表示法语法)
-i :直接修改读取的文件内容,而不是输出到终端。

动作说明: [n1[,n2]]function
n1, n2 :不见得会存在,一般代表『选择进行动作的行数』,举例来说,如果我的动作是需要在 10 到 20 行之间进行的,则『 10,20[动作行为] 』

function:
a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~
c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行!
d :删除,因为是删除啊,所以 d 后面通常不接任何咚咚;
i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
p :列印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行~
s :取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法!例如 1,20s/old/new/g 
————————————————
版权声明:本文为CSDN博主「iOS逆向」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/z929118967/article/details/49489527

4.2 数据的搜寻并替换

`sed 's/要被取代的字串/新的字串/g'`

    /etc>ifconfig -a | grep inet 
        inet 172.16.49.224 netmask 0xffffff00 broadcast 172.16.49.255
        inet 192.168.10.4 netmask 0xffffff00 broadcast 192.168.10.255
        inet 127.0.0.1 netmask 0xff000000 broadcast 127.255.255.255
        inet6 ::1%1/0
————————————————
版权声明:本文为CSDN博主「iOS逆向」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/z929118967/article/details/49489527

see also

更多服务和咨询请关注#公号:iOS逆向

iOS小技能:给debugserver添加task_for_pid权限,以便调试从AppStore中获取的App。

2022年10月27日 09:49
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第28天,点击查看活动详情 前言 在做iOS开发时,在Mac上输入LLDB的命令就可以控制iOS端的App,是因为在iOS客户端中

iOS小技能:消息发送的步骤(利用类型编码加快消息分发)

2022年6月16日 09:20

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情

前言

运行时API的应用:

  1. 路由的实现(接口控制app跳任意界面)
  2. 获取修改对象的成员属性
  3. 动态添加/交换方法的实现: blog.csdn.net/z929118967/…
  4. 属性关联: blog.csdn.net/z929118967/…
  5. iOS 间接实现多继承的方式:消息转发、类别、delegate和protocol(委托协助主体完成操作任务,将需要定制化的操作预留给委托对象来自定义实现 与block类似) 。

I runtime的作用

因为objective-c是一门动态语言,也就是说只有编译器是不够的,还需要一个运行时系统(runtime system)来执行编译后的代码。

runtime简称运行时,其中最主要的就是消息机制。

对于编译期语言,会在编译的时候决定调用哪个函数。对于OC的函数,是动态调用的,在编译的时候并不能决定真正调用哪个函数,只有在运行时才会根据函数的名称找到对应的函数来调用。

1.1 消息发送的步骤

messages aren’t bound to method implementations until Runtime。(消息直到运行时才会与方法实现进行绑定)

objc_msgSend 是当方法的实现被调用后才会返回数据。

在这里插入图片描述

  1. 当向someObject发送消息时,先在本类中的方法缓存列表中进行查找
  2. 如果找不到就在本类中的法列表中进行查找
  3. 如果没找到,就去父类中进行查找。
  4. 如果没找到,runtime system并不会立即报错使程序崩溃,而是依次执行消息转发。

这里写图片描述

检查是否有动态添加对应的方法->检查是否有其他对象实现了对应的方法(快速转发给其他对象处理)->(标准消息转发)

  1. 动态方法解析 : 向当前类发送 resolveInstanceMethod: 信号,检查是否动态向该类添加了方法。
  2. 快速消息转发: 获取转发对象, 检查该类是否实现了 forwardingTargetForSelector: 方法,若实现了则调用这个方法。若该方法返回值对象非nil或非self,那么就进行消息的常规转发。
  3. 标准消息转发:获取方法签名,runtime发送methodSignatureForSelector:消息获取Selector对应的方法签名。返回值非空则通过forwardInvocation:转发消息,返回值为空则向当前对象发送doesNotRecognizeSelector:消息,程序崩溃退出。

1.2 Runtime API

  1. 通过对 Runtime 库函数的直接调用

runtime源码:github.com/opensource-…

opensource.apple.com/source/objc… 在这里插入图片描述

其中主要使用的函数定义在message.h和runtime.h这两个文件中。

  1. 通过 Foundation 框架的 NSObject 类定义的方法

Cocoa 程序中绝大部分类都是 NSObject 类的子类,所以都继承了 NSObject 的行为。(NSProxy 类是个例外,它是个抽象超类)

  • class方法返回对象的类;
  • isKindOfClass: 和 -isMemberOfClass: 方法检查对象是否存在于指定的类的继承体系中(是否是其子类或者父类或者当前类的成员变量);
  • respondsToSelector: 检查对象能否响应指定的消息;
  • conformsToProtocol:检查对象是否实现了指定协议类的方法;
  • methodForSelector: 返回指定方法实现的地址。

1.3 理解instance、class object、metaclass的关系

在这里插入图片描述

一个实例对象struct objc_object的isa指针指向它的struct objc_class类对象,类对象的isa指针指向它的元类;super_class指针指向了父类的类对象,而元类super_class指针指向了父类的元类

II 消息转发

2.1 消息处理(动态地添加一个方法实现)

当在相应的类以及父类中找不到类方法实现时会执行+resolveInstanceMethod:这。该方法如果在类中不被重写的话,默认返回NO。如果返回NO就表明不做任何处理,走下一步。

如果返回YES的话,就说明在该方法中对这个找不到实现的方法进行了处理。

+resolveInstanceMethod:方法中,我们可以为找不到实现的SEL动态地添加一个方法实现,添加完毕后,就会执行我们添加的方法实现。这样,当一个类调用不存在的方法时,就不会崩溃了。具体做法如下所示:

//运行时方法拦截
- (void)dynamicAddMethod: (NSString *) value {
    NSLog(@"OC替换的方法:%@", value);
}

/**
 没有找到SEL的IMP实现时会执行下方的方法
 
 @param sel 当前对象调用并且找不到IMP的SEL
 @return 找到其他的执行方法,并返回yes
 */
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    return NO;    //当返回NO时,会接着执行forwordingTargetForSelector:方法,
    [KNRuntimeKit addMethod:[self class] method:sel method:@selector(dynamicAddMethod:)];//动态地添加一个方法实现
    return YES;
}

2.2 消息快速转发

如果不对上述消息进行处理的话,也就是+resolveInstanceMethod:返回NO时,会走下一步消息转发,即-forwardingTargetForSelector:

该方法会返回一个类的对象,这个类的对象有SEL对应的实现。当调用这个找不到的方法时,就会被转发到SecondClass中去进行处理。


/**
 将当前对象不存在的SEL传给其他存在该SEL的对象
 
 @param aSelector 当前类中不存在的SEL
 @return 存在该SEL的对象
 */
- (id)forwardingTargetForSelector:(SEL)aSelector {
    return self;//当该方法返回self或者nil, 说明不对相应的方法进行转发,那么就进行消息的常规转发。 

    return [SecondClass new];   //让SecondClass中相应的SEL去执行该方法
}

例子: 教师执行手术


//Returns the object to which unrecognized messages should first be directed.
- (id)forwardingTargetForSelector:(SEL)aSelector  
{  
    Doctor *doctor = [[Doctor alloc]init];  
    if ([doctor respondsToSelector:aSelector]) {  
        return doctor;  
    }  
    return nil;  
} 

这个方式的好处是隐藏了我要转发的消息,没有类目那么清晰。

2.3 消息常规转发

如果不将消息转发给其他类的对象,那么就只能自己进行处理了。

如果上述方法返回nil或者self的话,会执行-methodSignatureForSelector:方法来获取方法的参数以及返回数据类型(方法签名)。

返回值非空则通过forwardInvocation:转发消息,返回值为空则向当前对象发送doesNotRecognizeSelector:消息,程序崩溃,报出找不到相应的方法实现的崩溃信息。

//在+resolveInstanceMethod:返回NO时就会执行下方的方法,下方也是将该方法转发给SecondClass。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
    //查找父类的方法签名
    NSMethodSignature *signature = [super methodSignatureForSelector:selector];
    if(signature == nil) {
       // signature = [NSMethodSignature signatureWithObjCTypes:"@@:"];
                signature = [someObj methodSignatureForSelector:aSelector];  //返回指定方法签名

        
    }
    return signature;
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    SecondClass * forwardClass = [SecondClass new];
    SEL sel = invocation.selector;
    if ([forwardClass respondsToSelector:sel]) {
        [invocation invokeWithTarget:forwardClass];
    } else {
        [self doesNotRecognizeSelector:sel];
    }
}


2.4 两种消息转发方式的比较

  1. 快速消息转发:简单、快速、但仅能转发给一个对象。
  2. 标准消息转发:稍复杂、较慢、但转发操作实现可控,可以实现多对象转发

III 编译器指令 @encode()

苹果的 Objective-C 运行时库内部利用类型编码帮助加快消息分发。

ocrtTypeEncodings

NSValue 的 +valueWithBytes:objCType:第二个参数需要用@encode()指令来创建一种内部表示的字符串 。

@encode(int) → i


+ (NSValue *)valueWithBytes:(const void *)value objCType:(const char *)type;
+ (NSValue *)value:(const void *)value withObjCType:(const char *)type;


3.1 Objective-C type encodings

Code Meaning
c A char
i An int
s A short
l A long ,l is treated as a 32-bit quantity on 64-bit programs.
q A long long
C An unsigned char
I An unsigned int
S An unsigned short
L An unsigned long
Q An unsigned long long
f A float
d A double
B A C++ bool or a C99 _Bool
v A void
* A character string (char *)
@ An object (whether statically typed or typed id)
# A class object (Class)
: A method selector (SEL)
[array type] An array
{name=type...} A structure
(name=type...) A union
bnum A bit field of num bits
^type A pointer to type
? An unknown type (among other things, this code is used for function pointers)

- (void)testtypeEncodings{
    
    
    NSLog(@"int        : %s", @encode(int));
    NSLog(@"float      : %s", @encode(float));
    NSLog(@"float *    : %s", @encode(float*));//指针的标准编码是加一个前置的 ^
    NSLog(@"char       : %s", @encode(char));
    NSLog(@"char *     : %s", @encode(char *));//char * 拥有自己的编码 *,因为 C 的字符串被认为是一个实体,而不是指针。
    NSLog(@"BOOL       : %s", @encode(BOOL));//BOOL 是 c,而不是i。原因是 char 比 int 小。BOOL 更确切地说是 signed char 。
    NSLog(@"void       : %s", @encode(void));
    NSLog(@"void *     : %s", @encode(void *));
    NSLog(@"NSObject * : %s", @encode(NSObject *));
    NSLog(@"NSObject   : %s", @encode(NSObject));
    NSLog(@"[NSObject] : %s", @encode(typeof([NSObject class])));
    NSLog(@"NSError ** : %s", @encode(typeof(NSError **)));

    int intArray[5] = {1, 2, 3, 4, 5};
    NSLog(@"int[]      : %s", @encode(typeof(intArray)));

    float floatArray[3] = {0.1f, 0.2f, 0.3f};
    NSLog(@"float[]    : %s", @encode(typeof(floatArray)));

    typedef struct _struct {
        short a;
        long long b;
        unsigned long long c;
    } Struct;
    NSLog(@"struct     : %s", @encode(typeof(Struct)));

}


结果:

2022-06-14 11:18:06.069425+0800 SDKSample[9300:3648699] int        : i
2022-06-14 11:18:06.069517+0800 SDKSample[9300:3648699] float      : f
2022-06-14 11:18:06.069559+0800 SDKSample[9300:3648699] float *    : ^f
2022-06-14 11:18:06.069597+0800 SDKSample[9300:3648699] char       : c
2022-06-14 11:18:06.069634+0800 SDKSample[9300:3648699] char *     : *
2022-06-14 11:18:06.069669+0800 SDKSample[9300:3648699] BOOL       : B
2022-06-14 11:18:06.069706+0800 SDKSample[9300:3648699] void       : v
2022-06-14 11:18:06.070191+0800 SDKSample[9300:3648699] void *     : ^v
2022-06-14 11:18:06.070230+0800 SDKSample[9300:3648699] NSObject * : @
2022-06-14 11:18:06.070267+0800 SDKSample[9300:3648699] NSObject   : {NSObject=#}
2022-06-14 11:18:06.070303+0800 SDKSample[9300:3648699] [NSObject] : #
2022-06-14 11:18:06.070339+0800 SDKSample[9300:3648699] NSError ** : ^@
2022-06-14 11:18:06.070374+0800 SDKSample[9300:3648699] int[]      : [5i]
2022-06-14 11:18:06.070870+0800 SDKSample[9300:3648699] float[]    : [3f]
2022-06-14 11:18:06.072298+0800 SDKSample[9300:3648699] struct     : {_struct=sqQ}

3.2 method encodings

Code Meaning
r const
n in
N inout
o out
O bycopy
R byref
V oneway

带inout 的参数表明它在发消息时对象即可传入又可传出,将参数特别标注为 in 或 out,程序将避免一些来回的开销。

增加一个 bycopy 修饰符以保证发送了一份完整的拷贝

IV 运行时API应用

运行时API的应用:

  1. 路由的实现(接口控制app跳任意界面) :kunnan.blog.csdn.net/article/det…

  2. 获取修改对象的成员属性

  3. 动态添加/交换方法的实现: blog.csdn.net/z929118967/…

  4. 属性关联: blog.csdn.net/z929118967/…

  5. iOS 间接实现多继承的方式:消息转发、类别、delegate和protocol(委托协助主体完成操作任务,将需要定制化的操作预留给委托对象来自定义实现 与block类似) 。

  6. NSClassFromString、NSSelectorFromString 使用例子

        id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")];



构建Runtime测试用例:github.com/zhangkn/KNO…

#import "KNRuntimeKit.h"

@implementation KNRuntimeKit



/**
 获取类名
 
 @param class 相应类
 @return NSString:类名
 */
+ (NSString *)fetchClassName:(Class)class {
    const char *className = class_getName(class);
    return [NSString stringWithUTF8String:className];
}

/**
 获取成员变量
 
 @param class Class
 @return NSArray
 */
+ (NSArray *)fetchIvarList:(Class)class {
    unsigned int count = 0;
    Ivar *ivarList = class_copyIvarList(class, &count);
    
    NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count];
    for (unsigned int i = 0; i < count; i++ ) {
        NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:2];
        const char *ivarName = ivar_getName(ivarList[i]);
        const char *ivarType = ivar_getTypeEncoding(ivarList[i]);
        dic[@"type"] = [NSString stringWithUTF8String: ivarType];
        dic[@"ivarName"] = [NSString stringWithUTF8String: ivarName];
        
        [mutableList addObject:dic];
    }
    free(ivarList);
    return [NSArray arrayWithArray:mutableList];
}

/**
 获取类的属性列表, 包括私有和公有属性,以及定义在延展中的属性
 
 @param class Class
 @return 属性列表数组
 */
+ (NSArray *)fetchPropertyList:(Class)class {
    unsigned int count = 0;
    objc_property_t *propertyList = class_copyPropertyList(class, &count);
    
    NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count];
    for (unsigned int i = 0; i < count; i++ ) {
        const char *propertyName = property_getName(propertyList[i]);
        [mutableList addObject:[NSString stringWithUTF8String: propertyName]];
    }
    free(propertyList);
    return [NSArray arrayWithArray:mutableList];
}


/**
 获取类的实例方法列表:getter, setter, 对象方法等。但不能获取类方法
 
 @param class <#class description#>
 @return <#return value description#>
 */
+ (NSArray *)fetchMethodList:(Class)class {
    unsigned int count = 0;
    Method *methodList = class_copyMethodList(class, &count);
    
    NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count];
    for (unsigned int i = 0; i < count; i++ ) {
        Method method = methodList[i];
        SEL methodName = method_getName(method);
        [mutableList addObject:NSStringFromSelector(methodName)];
    }
    free(methodList);
    return [NSArray arrayWithArray:mutableList];
}

/**
 获取协议列表
 
 @param class <#class description#>
 @return <#return value description#>
 */
+ (NSArray *)fetchProtocolList:(Class)class {
    unsigned int count = 0;
    __unsafe_unretained Protocol **protocolList = class_copyProtocolList(class, &count);
    
    NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count];
    for (unsigned int i = 0; i < count; i++ ) {
        Protocol *protocol = protocolList[i];
        const char *protocolName = protocol_getName(protocol);
        [mutableList addObject:[NSString stringWithUTF8String: protocolName]];
    }
    
    return [NSArray arrayWithArray:mutableList];
    return nil;
}


/**
 往类上添加新的方法与其实现
 
 @param class 相应的类
 @param methodSel 方法的名
 @param methodSelImpl 对应方法实现的方法名
 */
+ (void)addMethod:(Class)class method:(SEL)methodSel method:(SEL)methodSelImpl {
    Method method = class_getInstanceMethod(class, methodSelImpl);
    IMP methodIMP = method_getImplementation(method);
    const char *types = method_getTypeEncoding(method);
    class_addMethod(class, methodSel, methodIMP, types);
}

/**
 方法交换
 
 @param class 交换方法所在的类
 @param method1 方法1
 @param method2 方法2
 */
+ (void)methodSwap:(Class)class firstMethod:(SEL)method1 secondMethod:(SEL)method2 {
    Method firstMethod = class_getInstanceMethod(class, method1);
    Method secondMethod = class_getInstanceMethod(class, method2);
    method_exchangeImplementations(firstMethod, secondMethod);
}
@end

see also

小程序:iOS逆向

❌
❌