在前面的文章中,筆者有講解如何設置以及獲取一個section的數據,demo以下:html
#import <Foundation/Foundation.h>
#import <dlfcn.h>
#include <mach-o/loader.h>
#include <mach-o/getsect.h>
#ifndef __LP64__
#define mach_header mach_header
#else
#define mach_header mach_header_64
#endif
const struct mach_header *machHeader = NULL;
static NSString *configuration = @"";
//設置"__DATA,__customSection"的數據爲kyson
char *kString __attribute__((section("__DATA,__customSection"))) = (char *)"kyson";
int main(int argc, const char * argv[]) {
@autoreleasepool {
//設置machheader信息
if (machHeader == NULL)
{
Dl_info info;
dladdr((__bridge const void *)(configuration), &info);
machHeader = (struct mach_header_64*)info.dli_fbase;
}
unsigned long byteCount = 0;
uintptr_t* data = (uintptr_t *) getsectiondata(machHeader, "__DATA", "__customSection", &byteCount);
NSUInteger counter = byteCount/sizeof(void*);
for(NSUInteger idx = 0; idx < counter; ++idx)
{
char *string = (char*)data[idx];
NSString *str = [NSString stringWithUTF8String:string];
NSLog(@"%@",str);
}
}
return 0;
}
複製代碼
本文就帶你們詳細分析一下,這段代碼的含義。本文您將瞭解到ios
__attribute__
dladdr
uintptr_t
等含義__attribute__
關於__attribute__
,其實前面的文章有說過其中的一種用法:objective-c
__attribute__((constructor)) void myentry(){
NSLog(@"constructor");
}
複製代碼
這段代碼會優先於main方法執行。 很顯然,其於通常方法不同的地方在於有修飾符:安全
__attribute__((constructor))
複製代碼
那麼__attribute__
修飾符有什麼做用呢,這裏引用一段:bash
This section describes the syntax with which
__attribute__
may be used, and the constructs to which attribute specifiers bind, for the C language. Some details may vary for C++ and Objective-C. Because of infelicities in the grammar for attributes, some forms described here may not be successfully parsed in all cases. There are some problems with the semantics of attributes in C++. For example, there are no manglings for attributes, although they may affect code generation, so problems may arise when attributed types are used in conjunction with templates or overloading. Similarly, typeid does not distinguish between types with different attributes. Support for attributes in C++ may be restricted in future to attributes on declarations only, but not on nested declarators.函數
以上摘自gcc:gcc.gnu.org/onlinedocs/… 這裏筆者也摘抄了其餘博客的一些論述:post
__attribute__
能夠設置函數屬性(Function Attribute)、變量屬性(Variable Attribute)和類型屬性(Type Attribute)。它的書寫特徵是:__attribute__
先後都有兩個下劃線,並切後面會緊跟一對原括弧,括弧裏面是相應的__attribute__
參數,語法格式以下:__attribute__
((attribute-list)) 另外,它必須放於聲明的尾部「;」以前。ui
這裏再介紹一篇文章,用於講解__attribute__
的做用: 不使用 NSOBJECT 的 OBJECTIVE-C CLASS 這篇文章講解了atom
__attribute__((objc_root_class))
複製代碼
的用法,完整的代碼以下:spa
#import <stdio.h>
#import <stdlib.h>
#import <objc/runtime.h>
__attribute__((objc_root_class))
@interface Answer
{
Class isa;
}
+ (id)instantiate;
- (void)die;
@property(assign, nonatomic) int value;
@end
@implementation Answer
+ (id)instantiate
{
Answer *result = malloc(class_getInstanceSize(self));
result->isa = self;
return result;
}
- (void)die
{
free(self);
}
@end
int main(int argc, char const *argv[])
{
Answer *answer = [Answer instantiate];
answer.value = 42;
printf("The answer is: %d\n", answer.value);
[answer die];
return 0;
}
複製代碼
回到本文開頭的Demo,使用的是另一個屬性:
__attribute__((section("__DATA,__customSection")))
複製代碼
明顯看出,是聲明的一個變量屬性。 這個變量要「被放到」 section爲「__DATA,__customSection」裏面。這麼一來就不難理解了。
dladdr
使用dladdr方法能夠得到一個函數所在模塊,名稱以及地址。
下面咱們經過一個實例來講明:
#include <dlfcn.h>
#include <objc/objc.h>
#include <objc/runtime.h>
#include <stdio.h>
#include <string.h>
int main()
{
Dl_info info;
IMP imp = class_getMethodImplementation(objc_getClass("NSArray"),sel_registerName("description"));
printf("pointer %p\n", imp);
if (dladdr(imp,&info))
{
printf("dli_fname: %s\n", info.dli_fname);
printf("dli_sname: %s\n", info.dli_sname);
printf("dli_fbase: %p\n", info.dli_fbase);
printf("dli_saddr: %p\n", info.dli_saddr);
} else
{
printf("error: can't find that symbol.\n");
}
}
複製代碼
運行結果以下:
因此咱們能夠經過這種方式來判斷一個函數是否是被非法修改了。爲了更好說明函數dladdr
做用,這裏筆者再舉個Demo:
static inline BOOL validate_methods(const char *cls,const char *fname) __attribute__ ((always_inline));
BOOL validate_methods(const char *cls,const char *fname){
Class aClass = objc_getClass(cls);
Method *methods;
unsigned int nMethods;
Dl_info info;
IMP imp;
char buf[128];
Method m;
if(!aClass)
return NO;
methods = class_copyMethodList(aClass, &nMethods);
while (nMethods--) {
m = methods[nMethods];
printf("validating [%s %s]\n",(const char *)class_getName(aClass),(const char *)method_getName(m));
imp = method_getImplementation(m);
//imp = class_getMethodImplementation(aClass, sel_registerName("allObjects"));
if(!imp){
printf("error:method_getImplementation(%s) failed\n",(const char *)method_getName(m));
free(methods);
return NO;
}
if(!dladdr(imp, &info)){
printf("error:dladdr() failed for %s\n",(const char *)method_getName(m));
free(methods);
return NO;
}
/*Validate image path*/
if(strcmp(info.dli_fname, fname))
goto FAIL;
if (info.dli_sname != NULL && strcmp(info.dli_sname, "<redacted>") != 0) {
/*Validate class name in symbol*/
snprintf(buf, sizeof(buf), "[%s ",(const char *) class_getName(aClass));
if(strncmp(info.dli_sname + 1, buf, strlen(buf))){
snprintf(buf, sizeof(buf),"[%s(",(const char *)class_getName(aClass));
if(strncmp(info.dli_sname + 1, buf, strlen(buf)))
goto FAIL;
}
/*Validate selector in symbol*/
snprintf(buf, sizeof(buf), " %s]",(const char*)method_getName(m));
if(strncmp(info.dli_sname + (strlen(info.dli_sname) - strlen(buf)), buf, strlen(buf))){
goto FAIL;
}
}else{
printf("<redacted> \n");
}
}
return YES;
FAIL:
printf("method %s failed integrity test:\n",
(const char *)method_getName(m));
printf(" dli_fname:%s\n",info.dli_fname);
printf(" dli_sname:%s\n",info.dli_sname);
printf(" dli_fbase:%p\n",info.dli_fbase);
printf(" dli_saddr:%p\n",info.dli_saddr);
free(methods);
return NO;
}
複製代碼
回到本文開頭的Demo,不難看出,其實
if (machHeader == NULL)
{
Dl_info info;
dladdr((__bridge const void *)(configuration), &info);
machHeader = (struct mach_header_64*)info.dli_fbase;
}
複製代碼
這段代碼的用途僅僅是爲了獲取header。至於header前面的 文章也提到過了,這裏很少作講解了,拿到的header做爲函數getsectiondata
的參數:
uintptr_t* data = (uintptr_t *) getsectiondata(machHeader, "__DATA", "__customSection", &byteCount);
複製代碼
這裏須要注意的是getsectiondata
的定義以下:
extern uint8_t *getsectiondata(
const struct mach_header_64 *mhp,
const char *segname,
const char *sectname,
unsigned long *size);
複製代碼
因此一開始筆者在返回類型的時候,使用了uint8_t
結果發現無論怎麼操做都不能打印出想要的數據。改爲uintptr_t
才能打印成功。緣由你們能夠猜測一下。 最後咱們查看一下打印的結果: