android程序 函数 流程,Android FART脱壳机流程分析

if(szProcName[ 0]) {

constDexFile *dex_file = artmethod->GetDexFile;constchar*methodname =PrettyMethod(artmethod).c_str;constuint8_t*begin_ = dex_file->Begin;size_tsize_ = dex_file->Size;

memset(dexfilepath, 0, 2000);intsize_int_ = ( int) size_;

memset(dexfilepath, 0, 2000);sprintf(dexfilepath, "%s", "/sdcard/fart");mkdir(dexfilepath, 0777);

memset(dexfilepath, 0, 2000);sprintf(dexfilepath, "/sdcard/fart/%s",szProcName);mkdir(dexfilepath, 0777);

memset(dexfilepath, 0, 2000);sprintf(dexfilepath,"/sdcard/fart/%s/%d_dexfile.dex",szProcName, size_int_);intdexfilefp = open(dexfilepath, O_RDONLY, 0666);if(dexfilefp > 0) {close(dexfilefp);dexfilefp = 0;

} else{dexfilefp =open(dexfilepath, O_CREAT | O_RDWR,0666);if(dexfilefp > 0) {write(dexfilefp, ( void*) begin_,size_);fsync(dexfilefp);close(dexfilefp);}

}//下半部分开始constDexFile::CodeItem * code_item =artmethod->GetCodeItem; // (1)if(LIKELY(code_item != nullptr)) {intcode_item_len = 0;uint8_t*item = ( uint8_t*) code_item;if(code_item->tries_size_ > 0) { // (2)constuint8_t*handler_data = ( constuint8_t*) (DexFile::GetTryItems(*code_item,code_item->tries_size_));uint8_t*tail = codeitem_end(&handler_data);code_item_len = ( int)(tail - item);} else{code_item_len =16+code_item->insns_size_in_code_units_ * 2;}memset(dexfilepath, 0, 2000);intsize_int = ( int) dex_file->Size; // Length of datauint32_tmethod_idx =artmethod->get_method_idx;sprintf(dexfilepath,"/sdcard/fart/%s/%d_%ld.bin",szProcName, size_int, gettidv1);intfp2 =open(dexfilepath,O_CREAT | O_APPEND | O_RDWR,0666);if(fp2 > 0) {lseek(fp2, 0, SEEK_END);memset(dexfilepath, 0, 2000);intoffset = ( int) (item - begin_);sprintf(dexfilepath,"{name:%s,method_idx:%d,offset:%d,code_item_len:%d,ins:",methodname, method_idx,offset, code_item_len);intcontentlength = 0;while(dexfilepath[contentlength]!= 0)contentlength++;write(fp2, ( void*) dexfilepath,contentlength);longoutlen = 0;char*base64result =base64_encode(( char*) item,( long)code_item_len,&outlen);write(fp2, base64result, outlen);write(fp2, "};", 2);fsync(fp2);close(fp2);if(base64result != nullptr) {free(base64result);base64result = nullptr;}}

}

}

if(dexfilepath != nullptr) {free(dexfilepath);dexfilepath = nullptr;}

}

dumpArtMethod函数开始先通过/proc//cmdline虚拟文件读取进程pid对应的进程名,根据得到的进程名在sdcard下创建目录,所以在脱壳之前要给APP写入外部存储的权限。之后通过ArtMethod的GetDexFile函数得到DexFile指针,即ArtMethod所在的dex的指针,再从DexFile的Begin函数和Size函数得到dex文件在内存中起始的地址和dex文件的大小,接着用write函数把内存中的dex写到文件名以_dexfile.dex的文件中。

但该函数还没完,dumpArtMethod函数的下半部分,对函数的CodeItem进行dump。可能有些人就有疑问了,函数的上半部分不是把dex给dump了吗,为什么还需要取函数的CodeItem进行dump呢?对于某些壳,dumpArtMethod的上半部分已经能对dex进行整体dump,但是对于部分抽取壳,dex即使被dump下来,函数体还是以nop填充,即空函数体,FART还把函数的CodeItem给dump下来是让用户手动来修复这些dump下来的空函数。

我们来看dumpArtMethod函数的下半部分,这里将会涉及dex文件的结构,如果不了解请结合文档来看。注释(1)处,从ArtMethod中得到一个CodeItem。注释(2)处,根据CodeItem的tries_size_,即try_item的数量来计算CodeItem的大小:

(1)如果tries size 不为0,说明这个CodeItem有try_item,那么去把CodeItem的结尾地址给算出来

constuint8_t*handler_data = ( constuint8_t*) (DexFile::GetTryItems(*code_item,code_item->tries_size_));uint8_t*tail = codeitem_end(&handler_data);code_item_len = ( int)(tail - item);

codeitem_end函数怎么算出CodeItem的结束地址呢?

GetTryItems第二参数传入tries size ,即跳过所有的try_item,得到encoded_catch_handler_list的地址,然后传入codeitem_end函数

uint8_t*codeitem_end( constuint8_t** pData) {uint32_tnum_of_list = DecodeUnsignedLeb128(pData);for(; num_of_list > 0; num_of_list--) {int32_tnum_of_handlers =DecodeSignedLeb128(pData);intnum = num_of_handlers;if(num_of_handlers <= 0) {num = -num_of_handlers;}for(; num > 0; num--) {DecodeUnsignedLeb128(pData);DecodeUnsignedLeb128(pData);}if(num_of_handlers <= 0) {DecodeUnsignedLeb128(pData);}}return( uint8_t*) (*pData);}

codeitem_end函数的开头读取encoded_catch_handler_list结构中包含多少个encoded_catch_handler结构,如果不为0,遍历所有encoded_catch_handler结构,读取encoded_catch_handler结构中有多少encoded_type_addr_pair结构,有的话全部跳过,即跳过了整个encoded_catch_handler_list结构。最后函数返回的pData即为CodeItem的结尾地址。

得到了CodeItem结尾地址,用CodeItem结尾的地址减去CodeItem的起始地址得到CodeItem的真实大小。

(2)如果tries size 为0,那么就没有try_item,直接就能把CodeItem的大小计算出来:

code_item_len= 16+ code_item->insns_size_in_code_units_ * 2;

CodeItem的大小计算出来之后,接下来可以看到,有几个变量以格式化的方式打印到dexfilepath

sprintf(dexfilepath,"{name:%s,method_idx:%d,offset:%d,code_item_len:%d,ins:",methodname,method_idx,offset,code_item_len);

methodname 函数的名称

method idx 来源FART新增的函数:`uint32_t get_method_idx{ return dex_method_index ; },函数返回dex_method_index_,dex_method_index_是函数在method_ids`中的索引

offset 是该函数的CodeItem相对于dex文件开始的偏移

code_item_len Codeitem的长度

数据组装好之后,写入到以.bin为后缀的文件中:

write(fp2, ( void*) dexfilepath,contentlength);longoutlen = 0;char*base64result =base64_encode(( char*) item,( long)code_item_len,&outlen);write(fp2, base64result, outlen);write(fp2, "};", 2);

对于上面的dexfilepath,它们是明文字符,直接写入即可。而对于CodeItem中的bytecode这种非明文字符,直接写入不太好看,所以FART选择对它们进行base64编码后再写入。

分析到这里好像已经结束了,从主动调用,到dex整体dump,再到函数CodeItem的dump,都已经分析了。但是FART中确实还有一部分逻辑是没有分析的。如果你使用过FART来脱过壳,会发现它dump下来的dex中还有以_execute.dex结尾的dex文件。这种dex是怎么生成的呢?

这一部分的代码也是在artruntimeart_method.cc文件中

extern"C"voiddumpDexFileByExecute(ArtMethod * artmethod)SHARED_LOCKS_REQUIRED(Locks::mutator_lock_){char*dexfilepath = ( char*) malloc( sizeof( char) * 2000);if(dexfilepath == nullptr) {LOG(INFO) < 0) {read(fcmdline, szProcName, 256);close(fcmdline);}if(szProcName[ 0]) {

constDexFile *dex_file = artmethod->GetDexFile;constuint8_t*begin_ = dex_file->Begin; // Start of data.size_tsize_ = dex_file->Size; // Length of data.

memset(dexfilepath, 0, 2000);intsize_int_ = ( int) size_;

memset(dexfilepath, 0, 2000);sprintf(dexfilepath, "%s", "/sdcard/fart");mkdir(dexfilepath, 0777);

memset(dexfilepath, 0, 2000);sprintf(dexfilepath, "/sdcard/fart/%s",szProcName);mkdir(dexfilepath, 0777);

memset(dexfilepath, 0, 2000);sprintf(dexfilepath,"/sdcard/fart/%s/%d_dexfile_execute.dex",szProcName, size_int_);intdexfilefp = open(dexfilepath, O_RDONLY, 0666);if(dexfilefp > 0) {close(dexfilefp);dexfilefp = 0;

} else{dexfilefp =open(dexfilepath, O_CREAT | O_RDWR,0666);if(dexfilefp > 0) {write(dexfilefp, ( void*) begin_,size_);fsync(dexfilefp);close(dexfilefp);}

}}

if(dexfilepath != nullptr) {free(dexfilepath);dexfilepath = nullptr;}

}

可以看到,dumpDexFileByExecute函数有点像dumpArtMethod函数的上半部分,即对dex文件的整体dump。那么,dumpDexFileByExecute在哪里被调用呢?

通过搜索,在artruntimeinterpreterinterpreter.cc文件的开始,看到了FART在art命名空间下定义了一个dumpDexFileByExecute函数

namespaceart{extern"C"voiddumpDexFileByExecute( ArtMethod* artmethod);namespaceinterpreter{......}}

同时在文件其中找到了对dumpDexFileByExecute函数的调用:

staticinlineJValue Execute(Thread* self, constDexFile::CodeItem* code_item,ShadowFrame& shadow_frame, JValue result_register){if( strstr(PrettyMethod(shadow_frame.GetMethod).c_str, "")!= nullptr){dumpDexFileByExecute(shadow_frame.GetMethod);}......}

在Execute函数中,通过判断函数名称中是否存在即是否为静态代码块来决定要不要调用dumpDexFileByExecute,如果存在则调用dumpDexFileByExecute函数,并传入一个ArtMethod指针。

dumpDexFileByExecute中对dex进行了整体dump,可以把它看作是dumpArtMethod方式的互补,有时dumpArtMethod中得不到想得到的dex,用dumpDexFileByExecute或许能得到惊喜。

3. 结语

非常感谢FART作者能够开源FART,这使得人们对抗ART环境下App壳得到了良好的思路。FART脱壳机理论上来讲能脱大多数壳,但是仍有例外,需要自行摸索。

4. 参考

https://bbs.pediy.com/thread-252630.htm

https://source.android.google.cn/devices/tech/dalvik/dex-format返回搜狐,查看更多

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值