基于linker实现so加壳技术(补充)

总述

之前的文章给大家展示了一个so文件加壳的整体方法,而且给了一个比较简单的例子,后来我实现了一下将so写在dex中然后通过hook关键函数的方式得到so的地址。但是在实际使用过程中我发现了一个问题,就是有些got表中的变量,通过plt调用使用了页对其指令,这就要求我们的so必须在一块对其的内存上,如下图,并且inline hook也好难实现,而且dex是以只读的方式加载到内存中的,我还需要hook掉关键函数改掉它的属性,基于这三个问题,写了这篇补充
1:so地址页对其&第一个PT_LOAD和第二个PT_LOAD之间有多余占位数据
2:实现libart.so中的inlinehook
3:hook dex加载函数更改只读权限
4: 修正getsoinfo函数

so地址页对其&多余数据

由于像上面那种变量或者函数(例如strlen函数)在使用过程中,存在需要取页开始的情况,我们就必须保证so在内存中的偏移%0x1000等于0,所以我之前设想的直接在dex末尾接so内容的方式就不可取了,而且我们附在dex末尾的so是linking view,数据都在相对于文件的偏移上面,而so的执行需要Execution View,代码中lr这种函数跳转用的都是相对于so起地址的物理地址偏移,所以强行用静态的so文件会使调用函数的地址不对,而我又不想再次使用mmap将已经加载到内存中的so,再搞出来单独装载,所以想了一个折中的办法,利用系统通过ElfReader::Load结束的soinfo直接将此so的Execution View 整体dump出来,这一步可以用frida也可以用ida,或者用我上篇文章写的装载函数(当然这种更好因为不会触发任何反调试,因为我没有调用init_arry和JNI_Onloade当中的函数),在演示demo中我采用了第一种方式(不想写装载了有点累…),用idc脚本从ida里面dump一段内存

static main(void)
{
auto fp, begin, end, dexbyte;
fp = fopen("d:\\1.so", "wb");
begin = 0x0000007366280000;
end = begin + 0x0036000;
for ( dexbyte = begin; dexbyte < end;dexbyte ++ )
{
fputc(Byte(dexbyte), fp);
}
}

搞下来的so直接复制粘贴到dex末尾即可,由于需要对其,所以需要补0,当然如果想不让人看出来就补乱码,后面so也应该加密,只有在使用的时候再解密,我这里没有加密,只是补0和正常的复制,最后把filesize和checksum等一系列参数补充完整即可

由于Execution View后面都是节头去掉的部分所以都是0.这样就能解决对齐的问题嘛?答案是可以的,因为在DexClassloader加载dex的路上的函数MapFileAtAddress,是用的页对其,所以我们补完0之后so也一定是页对其的

搞定完这里,把so和插件dex都写入一个b.txt,然后直接用DexClassLoader加载起来就好,期间我模仿之前寒冰老师出的ctf题的写法,将b.txt搞到了资源目录然后使用的时候再复制到cache目录。

   copyAssetAndWrite("b.txt",getApplicationContext());....DexClassLoader loader=new DexClassLoader(path,"/sdcard","/sdcard",context.getClassLoader());

这样这部分的准备工作就完成了。

实现libart.so中的inlinehook

那么同时又产生了第二个问题,就是如何定位我的真正so的首地址。既然我将so加到了dex的末尾,那么我就可以通过dex+偏移的方式来访问我的so,那么问题又来了如何搞到dex的首地址呢?这里有2种办法,第一种就是从maps文件里面搜索,但是这样又不是很优雅,最终没有采纳。我最后是借鉴了寒冰老师的脱壳的思路,在LoadClass的关键路径上hook来搞定,我选择了LoadMethod函数,它的第一个参数就是c层的DexFile对象,那么hook搞定呢,用现成的hook框架?感觉特征太多而且就失去了学习的价值,还是自己写一个来的实在,首先想一个,既然是hook LoadMethod那么是不是用got表hook,来简单的搞定,扫了好几眼发现got表种没有这个函数555…,那么就只能挑战linline hook了。

寻找LoadMethod函数地址

由于LoadMethod函数是导出函数,所以我们可以从libart.so的导出表里面找到它的地址,通过类型为PT_DYNAMIC的段就能找到,这里由于上篇文章已经介绍了elf的文件格式所以这里只贴一点代码不做过多的介绍

   char line[1024];int *startr;int *end;int n=1;FILE *fp=fopen("/proc/self/maps","r");while (fgets(line, sizeof(line), fp)) {//从maps种扫描libart.so的地址if (strstr(line, libname) ) {__android_log_print(6,"r0ysue","");if(n==1){startr = reinterpret_cast<int *>(strtoul(strtok(line, "-"), NULL, 16));end = reinterpret_cast<int *>(strtoul(strtok(NULL, " "), NULL, 16));}else{strtok(line, "-");end = reinterpret_cast<int *>(strtoul(strtok(NULL, " "), NULL, 16));}n++;}}size_t   gnu_nbucket_ = 0;// skip symndxuint32_t     gnu_maskwords_ = 0;uint32_t  gnu_shift2_ =0;ElfW(Addr)*  gnu_bloom_filter_= nullptr;uint32_t*  gnu_bucket_ = nullptr;uint32_t*  gnu_chain_ = nullptr;
//导出表4项int phof=0;Elf64_Ehdr header;memcpy(&header,startr,sizeof(Elf64_Ehdr));uint64 rel= 0;size_t size=0;long* plt= nullptr;char* strtab_= nullptr;Elf64_Sym* symtab_= nullptr;Elf64_Phdr cc;memcpy (&cc,((char*)(startr)+header.e_phoff),sizeof(Elf64_Phdr));for(int y=0;y<header.e_phnum;y++){memcpy(&cc, (char *) (startr) +header.e_phoff+sizeof(Elf64_Phdr) * y, sizeof(Elf64_Phdr));if(cc.p_type==6) {//程序头偏移phof=cc.p_paddr-cc.p_offset;}}for(int y=0;y<header.e_phnum;y++){memcpy(&cc, (char *) (startr) +header.e_phoff+sizeof(Elf64_Phdr) * y, sizeof(Elf64_Phdr));if(cc.p_type==2) {//p_type=2代表找到了动态段Elf64_Dyn dd;for(y=0;y==0||dd.d_tag!=0;y++) {memcpy(&dd, (char *) (startr) + cc.p_offset + y * sizeof(Elf64_Dyn)+0x1000,sizeof(Elf64_Dyn));if(dd.d_tag==0x6ffffef5){//找到了 DT_GNU_HASH段也就是导出表gnu_nbucket_ = reinterpret_cast<uint32_t*>((char*)startr + dd.d_un.d_ptr-phof)[0];// skip symndxgnu_maskwords_ = reinterpret_cast<uint32_t*>((char*)startr + dd.d_un.d_ptr-phof)[2];gnu_shift2_ = reinterpret_cast<uint32_t*>((char*)startr + dd.d_un.d_ptr-phof)[3];gnu_bloom_filter_ = reinterpret_cast<ElfW(Addr)*>((char*)startr + dd.d_un.d_ptr + 16-phof);gnu_bucket_ = reinterpret_cast<uint32_t*>(gnu_bloom_filter_ + gnu_maskwords_);// amend chain for symndx = header[1]gnu_chain_ = reinterpret_cast<uint32_t *>( gnu_bucket_ +gnu_nbucket_-reinterpret_cast<uint32_t *>((char *) startr +dd.d_un.d_ptr-phof)[1]);}if(dd.d_tag==5 ){//得到字符串表的首地址strtab_=reinterpret_cast< char*>((char *) startr+dd.d_un.d_ptr-phof);}if(dd.d_tag==6 ){//得到符号表的首地址symtab_= reinterpret_cast<Elf64_Sym *>(((char *) startr + dd.d_un.d_ptr-phof));}}}}

很容易就得到了导出表、符号表、字符串表的信息,接下来只需要模仿安卓源码中soinfo::gnu_lookup函数的写法来,获取我们的导出符号的函数地址就好,这里我直接将symbol_name.gnu_hash函数抽出来实现了一下,他这种计算hash的方式很简单,也就5行代码

  char* name_=symname;uint32_t h = 5381;const uint8_t* name = reinterpret_cast<const uint8_t*>(name_);while (*name != 0) {h += (h << 5) + *name++; // h*33 + c = h + h * 32 + c = h + h << 5 + c}//实现symbol_name.gnu_hashint index=0;uint32_t h2 = h >> gnu_shift2_;uint32_t bloom_mask_bits = sizeof(ElfW(Addr))*8;uint32_t word_num = (h / bloom_mask_bits) & gnu_maskwords_;ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];n = gnu_bucket_[h % gnu_nbucket_];//模仿安卓源码直接抄do {Elf64_Sym * s = symtab_ + n;char * sb=strtab_+ s->st_name;if (strcmp(sb ,reinterpret_cast<const char *>(name_)) == 0 ) {break;}} while ((gnu_chain_[n++] & 1) == 0);

这样最终得到了我们传入符号在符号表当中的索引,那么只要拿到当中的st_value就是地址偏移了

void* mysym=(char *)startr+sb->st_value-phof

粗略的实现inline hook

由于水平太低有点看不懂大佬的思路…,只能先自己思考一下如何实现inline hook了,由此提出以下几个问题
1.首先构想一个整体的思路,如何实现每个我们需要hook的函数都能跳转到我们自己的函数执行?
这个问题可以用一条指令就是b一个地址,但是又有一个问题,这个地址是有范围要求的,如果不在同一个so当中很难满足这个界限要求,这一点在https://armconverter.com/ 中可以得到验证,所以这种方式是不可以的,那么就只剩一种方式了br或blr一个寄存器的值,根据网上资料(实在太菜大佬的文章只能看懂这一点)x17和x16是不经常使用的(我只在安卓源码art_jni_dlsym_lookup_stub中看到了使用),那么就需要赋值和br来替换函数的前几条指令代码如下,一共用了16位来跳转到我自己的代码,所以要把这四条指令保存下来,这里保存指令我用的全局变量不知道有没有更好的方式,这里面我用的blr因为我还要跳回来所以不能用br,用br不会保存x30寄存器,就会直接跳回上一级函数,就不会执行原函数了,但是这样又有一个问题,就是我这4条指令可能会覆盖x30的入栈指令那么在之后的x30恢复的时候就会无法恢复x30,所以我决定在x30的入栈指令之后执行我的操作,具体做法是扫描指令如果满足STX29, X30, [SP,#0x80]这种类似的,才在他的后一位写入我们的跳转指令 ,如果直接扫到了最后,那么没办法了只能从开头开始,16进制和汇编的转换都可以从https://armconverter.com/ 当中找到这里就不在计算了,如下图

    int s=0;for(n=0;*(int*)((char *)startr+sb->st_value-phof+n)!=0xd65f03c0;n=n+4){int code=*(int*)((char *)startr+sb->st_value-phof+n);if(code>>32==0xa9&&(code&0xfff)==0xbfd){__android_log_print(6,"r0ysue","%x",n);s=1;break;}}if(s=1){n=n+4;} else{n=0;}
one= *(int*)((char *)startr+sb->st_value-phof+n);
two= *(int*)((char *)startr+sb->st_value-phof+n+4);
three[ns=*(int*)((char *)startr+sb->st_value-phof+n+8);
four[]=*(int*)((char *)startr+sb->st_value-phof+n+12);*(int*)((char *)startr+sb->st_value-phof+n)=0x58000051;//
*(int*)((char *)startr+sb->st_value-phof+n+4)=0xd63f0220;
*(long**)((char *)startr+sb->st_value-phof+n+8)= reinterpret_cast<long*>(myloadmethod);//自己函数的地址


成功的跳到我写的函数,那么现在就构思如何写自己的壳函数了,有一个问题,就是arm64不能操作pc,所以我要如何控制回来的时候跳到哪里呢,我把目光盯向了x30,执行完RET这条汇编之后,pc会跳到x30处(也就是32位下的lr),那么只要我指定x30就好了,由于上面用了4条指令而我是在第2条指令跳转的,所以要把x30+8,这一段我发现如果不用裸函数写,他会自动加上对x30寄存器的保护,所以我选择了裸函数,之后就是对寄存器的保护就好了.

void  __attribute((naked)) myloadmethod(){asm("add x30,x30,8");//指定跳回去的位置asm("sub SP, SP, #0x100");//申请栈asm("stp X29, X30, [SP,#0x10]");//保护寄存器asm("stp X0, X1, [SP,#0x20]");asm("stp X2, X3, [SP,#0x30]");asm("stp X4, X5, [SP,#0x40]");asm("stp X6, X7, [SP,#0x50]");asm("stp X8, X9, [SP,#0x60]");asm("stp X10, X11, [SP,#0x70]");asm("stp X12, X13, [SP,#0x80]");asm("stp X14, X15, [SP,#0x90]");asm("ldp X16, X17, [SP,#0x10]");//保存x30到x17,当然这里用mov或者ldr都行我复制粘贴省事了asm("mov X16,SP");//这里参数处理我们想好就先用栈替代asm("mov x0,x0");//占位,会改成BL _Z4pltsv,也就是我们自己写的第二个函数asm("ldp X8, X9, [SP,#0x60]");//寄存器恢复asm("ldp X10, X11, [SP,#0x70]");asm("ldp X12, X13, [SP,#0x80]");asm("ldp X14, X15, [SP,#0x90]");asm("ldp X0, X1, [SP,#0x20]");asm("ldp X2, X3, [SP,#0x30]");asm("ldp X4, X5, [SP,#0x40]");asm("ldp X6, X7, [SP,#0x50]");asm("ldp X29, X30, [SP,#0x10]");asm("add SP, SP, #0x100");asm("mov x0,x0");//之前在原函数中占位的第1条指令asm("mov x0,x0");//之前在原函数中占位的第2条指令asm("mov x0,x0");//之前在原函数中占位的第3条指令asm("mov x0,x0");//之前在原函数中占位的第4条指令asm("RET");}

这里就是下一个问题了,我们如何执行我们自己的函数,又如何将原来的4条指令执行一遍,这里我选择了再做一层包装,再写一个跳转,这会就不用裸函数了,而且指令直接用b 相对地址就可以了,因为是在同一个so当中,由于我不知道在内存中他俩谁在前面,所以写了个判断,而且恢复占用指令的代码也写在了这个包装函数里面

    int code;if((long)myloadmethod>(long)plts){int off=(long)myloadmethod-(long)plts+48;code=0x97ffffff-off/4;}else{int off=(long)plts-(long)myloadmethod-48;code=off/4|0x94000000;}*(int*)((char *)myloadmethod+52)= reinterpret_cast<int>(code);

在这个包装函数中,主要有2方面的内容,于是简单的代码就出来了
1.执行我们在外面传入的hook函数
2.恢复占用的指令

void plts(){func st=(func)fc;st();memcpy((char*)myloadmethod+0x60,&one,4);memcpy((char*)myloadmethod+0x64,&two,4);memcpy((char*)myloadmethod+0x68,&three,4);memcpy((char*)myloadmethod+0x6c,&four,4);}

这样一个简单的inlinehook框架就写完了,就在我迫不及待的去试一下的时候,发现有个大问题,就是单个hook是没啥问题,但是要同时hook2个函数就不行了,因为我用了大量的全局变量,新的输入会覆盖旧的输入,5555…,而我的需求正好是hook 2个函数,气死我了,那么只能做一个思考了,如何完善这个框架,而且尽量做到不大改,因为上面太多了,那么既用了全局变量,就只能以数组的形式存储使用了,直接用一个全局变量ns存储我们的索引。

one[ns]= *(int*)((char *)startr+sb->st_value-phof+n);two[ns]= *(int*)((char *)startr+sb->st_value-phof+n+4);three[ns]=*(int*)((char *)startr+sb->st_value-phof+n+8);four[ns]=*(int*)((char *)startr+sb->st_value-phof+n+12);funtab[ns]= reinterpret_cast<long>((char *) startr + sb->st_value - phof);
ns++;

那么在使用的时候又有一个问题,就是调用顺序可能和我们自己写的hook的顺序不太一样,所以如何确定我们调用的函数属于哪一个索引又是一个问题,这里我直接用之前的x30来确定,因为x30来自与原函数,所以与原函数不会差距太大,用这个来判断调用的函数属于哪个索引

void plts(){unsigned long addr=0;int uu;__asm__("mov %[input_n], x17\r\n":[result_m] "=r" (addr):[input_n] "r" (addr));int yy=0;for(yy=0;yy<ns;yy++){if(addr-(unsigned long)funtab[yy]<0x100){break;}}func st=(func)fc[yy];st();memcpy((char*)myloadmethod+0x60,&one[yy],4);memcpy((char*)myloadmethod+0x64,&two[yy],4);memcpy((char*)myloadmethod+0x68,&three[yy],4);memcpy((char*)myloadmethod+0x6c,&four[yy],4);__android_log_print(6,"r0ysue","");
}

那么这里我们的框架就完善了,其实还有个问题我没有解决,就是如何更改参数,我这里只能先用栈来解决更改参数的问题(没想好如何解决是个败笔),那么就可以完成我们的hook来获得dex首地址了,hook写在init里面就好

void io(void* a,void*b){long** dex= static_cast<long **>(b);size= reinterpret_cast<size_t>(*(dex + 2));long* begin=(*(dex+1));if(size==0x392000&&fl==0) {//是我们自己大小的dex而且只取一次fl=1;realel= reinterpret_cast<long*>(begin );//拿到真正的dex地址jiake();//释放so,加壳函数上篇文章讲过了,所以这里不再赘述,只需注意修正got表中的变量的值即可}
}void _init(){mainfun("_ZN3art11ClassLinker10LoadMethodERKNS_7DexFileERKNS_21ClassDataItemIteratorENS_6HandleINS_6mirror5ClassEEEPNS_9ArtMethodE", "libart.so",reinterpret_cast<void *>(io));
}

hook dex加载函数更改只读权限

那么现在就只剩下一个问题了,就是如何将dex加载进内存中,这里模仿寒冰老师的ctf赛题,java代码直接拿过来用就好,就是上面第一节的代码,但是这里有一个问题,DexClassLoader是以只读的方式将dex文件加载到内存中,我们需要可读可写可执行,所以需要改权限,但是这里又遇到了一个问题,调用mprotect的时候直接报错,Permission denied,我也不知道为啥在这里卡了好久,最后在 https://blog.csdn.net/earbao/article/details/120308836 中找到了答案,这里就要求我们取hook MapFileAtAddress将其第三个函数改为PROT_WRITE|PROT_READ|PROT_EXEC,这就是我刚才要优化我的inlinehook 框架的原因,我hook了2个函数一个是LoadMethod和MapFileAtAddress,更改代码如下

void jjj(void* a,void* b,int c,int d){d=7;__asm__("str %[input_n], [X16,#0x30]\r\n"//用到了刚才保存的寄存器,这里直接用:[result_m] "=r" (d):[input_n] "r" (d));
}
mainfun("_ZN3art6MemMap16MapFileAtAddressEPhmiiilbbPKcPNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEE", "libart.so",reinterpret_cast<void *>(jjj));

修正getsoinfo函数

这样程序就设计完成了,我试没有任何问题,我兴高采烈地去找大佬试一下,没想到打脸了,直接就崩溃了,得知他是bullhead之后,我就拿起了我的82年的Nexus 5x,调试了一下,发现获得soinfo指针那里直接返回了一个特别大的值,哎,看来不同型号的手机linker不一样,我打开ida易看果然,不同型号的手机linker中,__dl_g_soinfo_handles_map偏移不一样,我上篇文章用的是偏移,所以会有这个问题,如下图,左面是0xfa460右面是0xfd460

那么这就很烦了,好难受,上篇文章的内容就有局限性了,只能针对某一型号的手机,那是不可以的,我要解决问题,就还是从soinfo_from_handle这个inline函数入手,这会就看一看他是如何实现的,eee…,看不懂,还是看汇编吧,还是汇编友好一点。

static soinfo* soinfo_from_handle(void* handle) {if ((reinterpret_cast<uintptr_t>(handle) & 1) != 0) {auto it = g_soinfo_handles_map.find(reinterpret_cast<uintptr_t>(handle));if (it == g_soinfo_handles_map.end()) {return nullptr;} else {return it->second;}}

还是汇编简单一点,这部分内容就很容易理解了,只要拿到最后的x0就好,那么他做了什么呢
1.首先取了__dl_g_soinfo_handles_map的值
2.然后将其中的值与他后面一个字节取余操作
3.取上一步结果乘8然后取首先取了__dl_g_soinfo_handles_map的值中的值加这个偏移
4.取2次地址中的值并且加0x18
5.最后再取一次它其中地址的值就是soninfo指针了

.text:000000000000CC5C                 ADRP            X12, #__dl_g_soinfo_handles_map_ptr@PAGE
.text:000000000000CC60                 LDR             X12, [X12,#__dl_g_soinfo_handles_map_ptr@PAGEOFF]
.text:000000000000CC64                 LDR             X8, [X12,#(qword_FA468 - 0xFA460)]
.text:000000000000CC68                 CBZ             X8, loc_CCF4
.text:000000000000CC6C                 SUB             X9, X8, #1
.text:000000000000CC70                 AND             X10, X9, X8
.text:000000000000CC74                 CBZ             X10, loc_CC90
.text:000000000000CC78                 MOV             X11, X19
.text:000000000000CC7C                 CMP             X8, X19
.text:000000000000CC80                 B.HI            loc_CC94
.text:000000000000CC84                 UDIV            X11, X19, X8
.text:000000000000CC88                 MSUB            X11, X11, X8, X19
.text:000000000000CC8C                 B               loc_CC94
.text:000000000000CC90 ; ---------------------------------------------------------------------------
.text:000000000000CC90
.text:000000000000CC90 loc_CC90                                ; CODE XREF: __dl__Z10do_dlclosePv+5C↑j
.text:000000000000CC90                 AND             X11, X9, X19
.text:000000000000CC94
.text:000000000000CC94 loc_CC94                                ; CODE XREF: __dl__Z10do_dlclosePv+68↑j
.text:000000000000CC94                                         ; __dl__Z10do_dlclosePv+74↑j
.text:000000000000CC94                 LDR             X12, [X12]
.text:000000000000CC98                 LDR             X12, [X12,X11,LSL#3]
.text:000000000000CC9C                 CBZ             X12, loc_CCF4
.text:000000000000CCA0
.text:000000000000CCA0 loc_CCA0                                ; CODE XREF: __dl__Z10do_dlclosePv+A4↓j
.text:000000000000CCA0                                         ; __dl__Z10do_dlclosePv+CC↓j
.text:000000000000CCA0                 LDR             X12, [X12]
.text:000000000000CCA4                 CBZ             X12, loc_CCF4
.text:000000000000CCA8                 LDR             X13, [X12,#8]
.text:000000000000CCAC                 CMP             X13, X19
.text:000000000000CCB0                 B.NE            loc_CCC4
.text:000000000000CCB4                 LDR             X13, [X12,#0x10]
.text:000000000000CCB8                 CMP             X13, X19
.text:000000000000CCBC                 B.NE            loc_CCA0
.text:000000000000CCC0                 B               loc_CCEC
.text:000000000000CCC4 ; ---------------------------------------------------------------------------
.text:000000000000CCC4
.text:000000000000CCC4 loc_CCC4                                ; CODE XREF: __dl__Z10do_dlclosePv+98↑j
.text:000000000000CCC4                 CBZ             X10, loc_CCDC
.text:000000000000CCC8                 CMP             X13, X8
.text:000000000000CCCC                 B.CC            loc_CCE0
.text:000000000000CCD0                 UDIV            X14, X13, X8
.text:000000000000CCD4                 MSUB            X13, X14, X8, X13
.text:000000000000CCD8                 B               loc_CCE0
.text:000000000000CCDC ; ---------------------------------------------------------------------------
.text:000000000000CCDC
.text:000000000000CCDC loc_CCDC                                ; CODE XREF: __dl__Z10do_dlclosePv:loc_CCC4↑j
.text:000000000000CCDC                 AND             X13, X13, X9
.text:000000000000CCE0
.text:000000000000CCE0 loc_CCE0                                ; CODE XREF: __dl__Z10do_dlclosePv+B4↑j
.text:000000000000CCE0                                         ; __dl__Z10do_dlclosePv+C0↑j
.text:000000000000CCE0                 CMP             X13, X11
.text:000000000000CCE4                 B.EQ            loc_CCA0
.text:000000000000CCE8                 B               loc_CCF4
.text:000000000000CCEC ; ---------------------------------------------------------------------------
.text:000000000000CCEC
.text:000000000000CCEC loc_CCEC                                ; CODE XREF: __dl__Z10do_dlclosePv+A8↑j
.text:000000000000CCEC                 LDR             X0, [X12,#0x18]
.text:000000000000CCF0                 CBNZ            X0, loc_CC50

翻译成最终c代码如下,这样就能解决刚才的不同手机的适配问题了

    void* nmm=(char*)startr+realoff;char *next=(char*)nmm+8;unsigned long map= reinterpret_cast<unsigned long>(strstr);unsigned long mapnext=*next;mapnext=map%mapnext;nmm=(long*)((char*)(*(long*)nmm)+mapnext*8);long* final= reinterpret_cast<long *>((char *) (**(long **) nmm) + 0x18);void* soinfo= reinterpret_cast<void *>(*final);return static_cast<uint64 *>(soinfo);

那么又产生了下一个问题,就是如何拿到__dl_g_soinfo_handles_map。__dl_g_soinfo_handles_map是linker中的符号的值,但不是导出符号的值,基于此我没有在动态段中找到它对应的的符号,如下图只有寥寥的几个符号

那怎么办呢,我想到了可以通过节头表索引,直接打开/system/bin/linker64文件读它的节头表,比读段简单了许多,代码如下

   int fd;void *start;struct stat sb;fd = open("/system/bin/linker64", O_RDONLY); /*打开/etc/passwd */fstat(fd, &sb); /* 取得文件大小 */start = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);__android_log_print(6, "r0ysue", "%p", start);Elf64_Ehdr header;memcpy(&header, start, sizeof(Elf64_Ehdr));int secoff = header.e_shoff;int secsize = header.e_shentsize;int secnum = header.e_shnum;int secstr = header.e_shstrndx;Elf64_Shdr strtab;memcpy(&strtab, (char *) start + secoff + secstr * secsize, sizeof(Elf64_Shdr));int strtaboff = strtab.sh_offset;char strtabchar[strtab.sh_size];memcpy(&strtabchar, (char *) start + strtaboff, strtab.sh_size);Elf64_Shdr enumsec;int gotoff = 0;int gotsize = 0;int strtabsize = 0;int stroff = 0;for (int n = 0; n < secnum; n++) {memcpy(&enumsec, (char *) start + secoff + n * secsize, sizeof(Elf64_Shdr));if (strcmp(&strtabchar[enumsec.sh_name], ".symtab") == 0) {gotoff = enumsec.sh_offset;gotsize = enumsec.sh_size;__android_log_print(6, "r0ysue", "%x", gotsize);}if (strcmp(&strtabchar[enumsec.sh_name], ".strtab") == 0) {stroff = enumsec.sh_offset;strtabsize = enumsec.sh_size;__android_log_print(6, "r0ysue", "%x", stroff);}}int realoff=0;char relstr[strtabsize];Elf64_Sym tmp;memcpy(&relstr, (char *) start + stroff, strtabsize);for (int n = 0; n < gotsize; n = n + sizeof(Elf64_Sym)) {memcpy(&tmp, (char *)start + gotoff+n, sizeof(Elf64_Sym));if(tmp.st_name!=0&&strstr(relstr+tmp.st_name,"soinfo_handles_map"))realoff=tmp.st_value;}

总结

这样就完成了上面4个问题的解决,比较基础,主要是对elf文件格式的一个解析,我只是提供了一个例子,没有对数据进行加密,正常情况下我觉得还应该对dex中的so信息进行加密保护效果才更强,而且我的hook貌似不稳定,又崩溃的几率,感谢大家观看

基于linker实现so加壳补充从dex中加载so相关推荐

  1. 【Android 逆向】加壳技术简介 ( 动态加载 | 第一代加壳技术 - DEX 整体加固 | 第二代加壳技术 - 函数抽取 | 第三代加壳技术 - VMP / Dex2C | 动态库加壳技术 )

    文章目录 一.动态加载 二.第一代加壳技术 ( DEX 整体加固 ) 三.第二代加壳技术 ( 函数抽取 ) 四.第三代加壳技术 ( Java 函数 -> Native 函数 ) 五.so 动态库 ...

  2. 什么是加壳和脱壳技术?加壳和脱壳技术是什么意思?

    什么是加壳和脱壳技术?加壳和脱壳技术是什么意思? 加壳,是一种通过一系列数学运算,将可执行程序文件或动态链接库文件的编码进行改变(目前还有一些加壳软件可以压缩.加密驱动程序),以达到缩小文件体积或加密 ...

  3. 加壳器第二部分,加壳器

    1加壳器代码概述 为了软件的方便使用,这里我采用了mfc框架进行编写,其大致过程如下: 1.将加壳程序读入内存(因为要增加区段用于存放壳代码,并加密代码段) 2.加载壳代码 3.对一系列参数进行初始化 ...

  4. APK加壳【2】内存加载dex实现详解

    来源 本文要实验的方案同样来源于CSDN大牛Jack_Jia的一篇翻译博文: Android4.0内存Dex数据动态加载技术 原文的地址是 http://2013.hackitoergosum.org ...

  5. linux srelf 加壳代码,浅谈被加壳ELF文件的DUMP修复

    前面的文章中,我已经介绍了如何调试被加壳的ELF,这里不在叙述,直接进入正题,以某加固为例,如何DUMP和修复被加壳的ELF,使其能调试加载 我们先来看看被加壳ELF的头和Program Header ...

  6. 让你自己制作的木马和病毒用360安全卫士杀不出。加壳,免杀,加花,捆绑,压缩。

    最近我做了一个木马,但极易被360安全卫士给杀掉.现在我来尝试如何达到让360免杀的概念!!! 一.制作病毒 确保你有一个木马或病毒.像这样--- w 我们来用360杀杀看. 我们发现,它能被杀出!! ...

  7. mysql left join中on后加条件判断和where中加条件的区别

    left join中关于where和on条件的几个知识点:1.多表left join是会生成一张临时表,并返回给用户2.where条件是针对最后生成的这张临时表进行过滤,过滤掉不符合where条件的记 ...

  8. 技术扫盲 软件加壳(转)

    首先我想大家应该先明白"壳"的概念.在自然界中,我想大家对壳这东西应该都不会陌生了,植物用它来保护种子,动物用它来保护身体等等.同样,在一些计算机软件里也有一段专门负责保护软件不被 ...

  9. 病毒加壳技术与脱壳杀毒方法解析

    壳是什么?脱壳又是什么?这是很多经常感到迷惑和经常提出的问题,其实这个问题一点也不幼稚.当你想听说脱壳这个名词并试着去了解的时候,说明你已经在各个安全站点很有了一段日子了.下面,我们进入"壳 ...

  10. 软件加壳的原理及实现[转]

    转载自 https://blog.csdn.net/woshigeshusheng/article/details/68489843,侵删 加壳的实现 我是个初学者,所知有限,难免会有错误,如果有人发 ...

最新文章

  1. 怎么把文字变成图形_PPT 中实现文字矢量化
  2. “MSDN 开发论坛”大煞风景
  3. 《致命躯壳》:不完美,却有着独特之处的轻量级魂like新作
  4. Web服务器超时处理
  5. php框架快速入门,php-laravel4.0框架 简单快速入门
  6. matlab如何用代码导入文件_20+行Matlab代码实现文件扫描
  7. activiti5第二弹----使用activiti5提供的测试类进行测试
  8. android mysql 乱码_android POST数据遇到的UTF-8编码(乱码)问题解决办法
  9. Spring Boot基础学习笔记09:Thymeleaf模板引擎
  10. mf模型 svd++_【MF】SVD
  11. unittest和另一个可用单元测试框架nosetest
  12. mysql 空值处理
  13. 20161129 计算95除以55,商是多少,余数是多少?(商和余数要求分两行显示)
  14. 高通平台 LCD 的 LK部分代码解析
  15. 中国地区表-mysql-包含钓鱼岛-2019年10月更新-[1]
  16. 霹雳灯双灯c语言程序,单片机霹雳游侠灯源程序
  17. Android P 正式到来
  18. 为什么总有一些程序员,持续陷入焦虑、无法幸福?
  19. Dubbo监控中心Dubbo-admin安装
  20. python search用法,Python-re中search()函数的用法详解(查找ip)

热门文章

  1. 数学建模——层次分析法模型
  2. 基于RSA解题时yafu的使用
  3. linux拷贝依赖库到指定目录,Linux 批量依赖库拷贝(ldd)
  4. web前端设计与开发作品:旅游酒店网站设计——中国风的温泉酒店预订网站全套html模板(13页) HTML+CSS+JavaScript
  5. Linux下的编曲软件,MuseScore(免费作曲编曲软件) V3.0.1 Linux版
  6. 技嘉Z370 HD3P + i7-8700K + GTX1080 装黑苹果 High Sierra 10.13.6
  7. python编程:从入门到实践_《Python编程:从入门到实践》【PDF】
  8. H5游戏《守塔兵团》你必须要知道的4件事
  9. Smart3D输出/输入空三结果
  10. Unity简单实现调用电脑打印机打印图片功能