前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS小技能:__attribute__的应用

iOS小技能:__attribute__的应用

作者头像
公众号iOS逆向
发布2022-08-22 11:22:33
3340
发布2022-08-22 11:22:33
举报
文章被收录于专栏:iOS逆向与安全

引言

LLVM和其他 GCC 特性一样,Clang 支持了 __attribute__, 还加入了一小部分扩展特性。

__attribute__ 语法格式为:__attribute__ ((attribute-list))

constructor(priority), destructor(priority) 分别可以在main() 先后执⾏,可⽤于全局资源初始化和回收。

destructor让系统在main()函数退出或者调用了exit()之后,调用我们的函数。

I __attribute__的应用案例

1.1 代码注入

  1. ARM (通过汇编调用svc实现用户态到内核态的转换)
代码语言:javascript
复制
// 使用inline方式将函数在调用处强制展开,防止被hook和追踪符号
static __attribute__((always_inline)) void anti_debug()
#ifdef __arm__
    asm volatile(
                 "mov r0,#31\n"
                 "mov r1,#0\n"
                 "mov r2,#0\n"
                 "mov r12,#26\n"
                 "svc #80\n"
                 );
#endif
#ifdef __arm64__
    asm volatile(
                 "mov x0,#26\n"
                 "mov x1,#31\n"
                 "mov x2,#0\n"
                 "mov x3,#0\n"
                 "mov x16,#0\n"
                 "svc #128\n"
                 );
#endif
}
  1. 代码注入: facebook/fishhook符号表替换
代码语言:javascript
复制
/*
 * A structure representing a particular intended rebinding from a symbol
 * name to its replacement
 */
struct rebinding {//rebinding结构体
  const char *name;  //符号名称,C字符串,用来表明我们要hook哪个函数。
  void *replacement; //新函数的地址
  void **replaced;   //原始函数地址的指针!
};

//重新绑定符号
FISHHOOK_VISIBILITY
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel);
//rebindings[] 是一个 rebinding类型数组,用来存储需要hook的函数
//rebindings_nel 表示数组的长度


/*
 * Rebinds as above, but only in the specified image. The header should point
 * to the mach-o header, the slide should be the slide offset. Others as above.
 */
FISHHOOK_VISIBILITY
int rebind_symbols_image(void *header,
                         intptr_t slide,
                         struct rebinding rebindings[],
                         size_t rebindings_nel);
//指定镜像的header, slide 表示偏移量


hook ptrace函数,进行反反调试。

PT_DENY_ATTACH is an Apple-specific constant that can prevent debuggers (gdb, DTrace, etc.) from debugging your binary in kernel-level. ptrace(PT_DENY_ATTACH, 0, 0, 0);

代码语言:javascript
复制
#if !defined(PT_DENY_ATTACH)
#define PT_DENY_ATTACH 31
#endif

Rebinding customRebind = {"ptrace", my_ptrace, (void*)&orig_ptrace};
//第一个参数为需要替换的符号
//第二个参数为自己实现的函数名称
//第三个参数为原函数地址,因为fishhook是基于地址进行替换的+ `__attribute__((constructor))`实现注入


rebind_symbols((struct rebinding[1]){customRebind},1);

int my_ptrace(int _request, pid_t _pid, caddr_t _addr, int _data){
    if(_request != PT_DENY_ATTACH){
        return orig_ptrace(_request,_pid,_addr,_data);
    }    
    return 0;
}
  1. 自定义打印方法:用真正的方法替换去拦截 NSLog 的功能(iOS 11 之后这种方法失效了),使用__attribute__((constructor)); 进行实现,extern进行申明公共方法。
代码语言:javascript
复制

#ifdef DEBUG

// iOS 11 之前用真正的方法替换去实现拦截 NSLog 的功能,iOS 11 之后这种方法失效了,所以只能用宏定义的方式覆盖 NSLog。这也就意味着在 iOS 11 下一些如果某些代码编译时机比 QMUI 早,则这些代码里的 NSLog 是无法被替换为 KNLog 的
extern void _NSSetLogCStringFunction(void (*)(const char *string, unsigned length, BOOL withSyslogBanner));
static void PrintNSLogMessage(const char *string, unsigned length, BOOL withSyslogBanner) {
    QMUILog(@"NSLog", @"%s", string);
}

static void HackNSLog(void) __attribute__((constructor));
static void HackNSLog(void) {
    _NSSetLogCStringFunction(PrintNSLogMessage);
}

#define NSLog(...) KNLog(@"NSLog", __VA_ARGS__)// iOS 11 以后真正生效的是这一句
#endif

1.2 对格式化字符串进行类型检查

代码语言:javascript
复制
extern int
my_printf (void *my_object, const char *my_format, ...)
  __attribute__((format(printf, 2, 3)));
 //format 属性用于指定一个函数接收类似 printf, scanf, strftime 和 strfmon 风格的参数,应该按照参数对格式化字符串进行类型检查。

1.3 控制符号的可见性

#define STD_EXPORTS __attribute__ ((visibility("default")))

SymbolVisibility

The -fvisibility=vis compiler option lets you set the visibility for symbols in the current compilation. When set to hidden, symbols not explicitly marked as visible are hidden.

代码语言:javascript
复制
__attribute__((visibility("default"))) void MyFunction1() {}
__attribute__((visibility("hidden"))) void MyFunction2() {}

1.4 表明一些函数参数应该是非空的指针

代码语言:javascript
复制
extern void *
my_memcpy (void *dest, const void *src, size_t len)
  __attribute__((nonnull (1, 2)));

1.5 确保线程在应用整个生命周期内都能一直运行

AFNetworking 在网络请求线程的入口使用 noreturn 属性,用于网络请求的 NSThread。

https://github.com/AFNetworking/AFNetworking/blob/1.1.0/AFNetworking/AFURLConnectionOperation.m#L157

代码语言:javascript
复制
+ (void) __attribute__((noreturn)) networkRequestThreadEntryPoint:(id)__unused object {//确保这个线程在应用整个生命周期内都能一直运行
    do {
        @autoreleasepool {
            [[NSRunLoop currentRunLoop] run];
        }
    } while (YES);
}


+ (NSThread *)networkRequestThread {//专门用于网络请求的 NSThread
    static NSThread *_networkRequestThread = nil;
    static dispatch_once_t oncePredicate;
    
    dispatch_once(&oncePredicate, ^{
        _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
        [_networkRequestThread start];
    });
    
    return _networkRequestThread;
}

1.6 检查能否使用特定的属性

可以用 __has_attribute 这个指令

代码语言:javascript
复制
#ifndef AX_REQUIRES_SUPER
#if __has_attribute(objc_requires_super)
#define AX_REQUIRES_SUPER __attribute__((objc_requires_super))
#else
#define AX_REQUIRES_SUPER

__attribute((objc_requires_super)) was first introduced as work in progress into CLANG in September 2012 and was documented in October 2013. On both OS X and iOS there is now a NS_REQUIRES_SUPER macro that conditionally wraps the objc_requires_super attribute depending on compiler support. Once a method declaration is appended with this macro, the compiler will produce a warning if super is not called by a subclass overriding the method.

II 导出和隐藏符号

2.1 导出符号信息

  • 查看导出符号信息:nm -gm tmp_64.dylib

(__DATA,__data) external (undefined) external _CFDataCreate (from CoreFoundation) (undefined) external _CFNotificationCenterGetDarwinNotifyCenter (from CoreFoundation) (__TEXT,__text) external (undefined) external _IOObjectRelease (from IOKit) (undefined) external _IORegistryEntryCreateCFProperty (from IOKit) 000000010ffa3f97 (__DATA,__objc_data) external OBJC_CLASS

BslyjNwZmPCJkVst 000000010ffa3f97 (__DATA,__objc_data) external _OBJC_CLASS_

_ChiDDQmRSQpwQJgm

2.2 __attribute__控制符号是否导出

SymbolVisibility :https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/CppRuntimeEnv/Articles/SymbolVisibility.html

The -fvisibility=vis compiler option lets you set the visibility for symbols in the current compilation. When set to hidden, symbols not explicitly marked as visible are hidden.

代码语言:javascript
复制
#define EXPORT __attribute__((visibility("default")))

隐藏未明确标记为可见的符号:

  1. 在编译参数中加入-exported_symbols_list export_list
  2. 在编译参数中指定-fvisibility=hidden,对指定符号增加visibility(“default”)来导出符号
代码语言:javascript
复制
__attribute__((visibility("default"))) void MyFunction1() {}
__attribute__((visibility("hidden"))) void MyFunction2() {}

static 参数修饰,不会导出符号信息

代码语言:javascript
复制
static char _person_name[30] = {'\0'};

2.3 Pragmas控制符号是否导出

代码语言:javascript
复制
void f() { }
 
#pragma GCC visibility push(default)
void g() { }
void h() { }
#pragma GCC visibility pop


III ptrace系统调用

为了方便应用软件的开发和调试,unix的早期版本提供了一种对运行中的进程进行跟踪和控制手段:系统调用ptrace;通过ptrace,可以对另一个进程实现调试跟踪,同时ptrace提供了一个PT_DENY_ATTACH = 31参数用于告诉系统阻止调试器的依附

代码语言:javascript
复制
//ptrace系统调用 用于实现断点调试和对进程进行跟踪和控制
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void  *data);
//enum __ptrace_request request:指示了ptrace要执行的命令。
//pid_t pid: 指示ptrace要跟踪的进程。
//void *addr: 指示要监控的内存地址。
//void *data: 存放读取出的或者要写入的数据。

//PT_DENY_ATTACH is an Apple-specific constant that can prevent debuggers (gdb, DTrace, etc.) from debugging your binary in kernel-level.
//ptrace(PT_DENY_ATTACH, 0, 0, 0);

gdb利用ptrace系统调用,在被调试程序和gdb之间建立跟踪关系。然后所有发送给被调试程序的信号(除SIGKILL)都会被gdb截获,gdb根据截获的信号,查看被调试程序相应的内存地址,并控制被调试的程序继续运行。

3.1 syscall

syscall是通过软中断来实现从用户态到内核态,syscall (26,31,0,0)来调用系统函数ptrace(PT_DENY_ATTACH, 0, 0, 0);

ptrace的系统调用函数号是26,31是PT_DENY_ATTACH(用于告诉系统阻止调试器的依附)。int syscall(int, ...);#define SYS_ptrace 26

3.2 反调试

iOS动态防护:【Dynamic protection】反调试、反反调试、反注入、hook检测、完整性校验 https://blog.csdn.net/z929118967/article/details/84612698

  1. 运行时期,断点ptrace,直接返回
  2. 分析如何调用的ptrace,hook ptrace
  3. 通过tweak,替换disable_gdb函数
  4. 修改 PT_DENY_ATTACH:在二进制文件中 ,修改 PT_DENY_ATTACH的31,改成 任意一个值,如PT_ATTACH 0。https://blog.csdn.net/z929118967/article/details/78233844AlipayWalletTweakF.xm
  5. ARM (通过汇编调用svc实现用户态到内核态的转换)
代码语言:javascript
复制
// 使用inline方式将函数在调用处强制展开,防止被hook和追踪符号
static __attribute__((always_inline)) void anti_debug()
#ifdef __arm__
    asm volatile(
                 "mov r0,#31\n"
                 "mov r1,#0\n"
                 "mov r2,#0\n"
                 "mov r12,#26\n"
                 "svc #80\n"
                 );
#endif
#ifdef __arm64__
    asm volatile(
                 "mov x0,#26\n"
                 "mov x1,#31\n"
                 "mov x2,#0\n"
                 "mov x3,#0\n"
                 "mov x16,#0\n"
                 "svc #128\n"
                 );
#endif
}

see also

小程序:iOS逆向

Function-Attributes: https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 iOS逆向 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • I __attribute__的应用案例
    • 1.1 代码注入
      • 1.2 对格式化字符串进行类型检查
        • 1.3 控制符号的可见性
          • 1.4 表明一些函数参数应该是非空的指针
            • 1.5 确保线程在应用整个生命周期内都能一直运行
              • 1.6 检查能否使用特定的属性
              • II 导出和隐藏符号
                • 2.1 导出符号信息
                  • 2.2 __attribute__控制符号是否导出
                    • 2.3 Pragmas控制符号是否导出
                    • III ptrace系统调用
                      • 3.1 syscall
                        • 3.2 反调试
                        • see also
                        相关产品与服务
                        云开发 CloudBase
                        云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档