精华内容
下载资源
问答
  • <div><p>最新的安卓1.1.0版本编译安卓so文件错误,编译其他都没问题。 错误为: Could NOT find Protobuf (missing: Protobuf_LIBRARIES Protobuf_INCLUDE_DIR) 但我的protobuf已经安装完成,且同...
  • so 文件是 elf 文件,elf 文件后缀名是.so,所以也被称之为so 文件, elf 文件是 linux 底下二进制文件,可以理解为 windows 下的PE文件,在 Android 中可以比作dll,方便函数的移植,在常用于保护 Android 软件,...

    71eb6b4ad2fc981ecc3dfd81eed0aec6.png

    so 文件是啥?so 文件是 elf 文件,elf 文件后缀名是.so,所以也被称之为so 文件, elf 文件是 linux 底下二进制文件,可以理解为 windows 下的PE文件,在 Android 中可以比作dll,方便函数的移植,在常用于保护 Android 软件,增加逆向难度。

    解析 elf 文件有啥子用?最明显的两个用处就是:1、so 加固;2、用于 frida(xposed) 的检测! 

    本文使用 c 语言,编译器为 vscode。如有错误,还请斧正!!!

    一、SO 文件整体格式

    so 文件大体上可分为四部分,一般来说从上往下是ELF头部->Pargarm头部->节区(Section)->节区头,其中,除了ELF头部在文件位置固定不变外,其余三部分的位置都不固定。整体结构图可以参考非虫大佬的那张图,图片如下:

    07aa9d243ebcb4105ad2506f80a2496e.png

    解析语言之所以选择 c 语言,有两个原因:

    1、做 so 加固的时候可以需要用到,这里就干脆用 c 写成一个模板,哪里需要就哪里改,不像上次解析 dex 文件的时候用 python 写,结果后面写指令还原的时候需要用的时候在写一遍 c 版本代价太大了;

    2、在安卓源码中,有个elf.h文件,这个文件定义了我们解析时需要用到的所有数据结构,并且给出了参考注释,是很好的参考资料。elf.h文件路径如下:

    3e609c62c2a3eb6ef5553b4de3bc2a4e.png

    二、解析 ELF 头部

    ELF 头部数据格式在 elf.h 文件中已经给出,如下图所示:

    18abbedeec20dced3a0e787e61d8e321.png

    每个字段解释如下:

    1、e_ident 数组:前4个字节为.ELF,是 elf 标志头,第 5 个字节为该文件标志符,为 1 代表这是一个 32 位的 elf 文件,后面几个字节代表版本等信息。

    2、e_type 字段:表示是可执行文件还是链接文件等,安卓上的 so 文件就是分享文件,一般该字段为 3,详细请看下图。

    3、e_machine 字段:该字段标志该文件运行在什么机器架构上,例如 ARM。

    4、e_version 字段:该字段表示当前 so 文件的版本信息,一般为 1

    5、e_entry 字段:该字段是一个偏移地址,为程序启动的地址。 

    6、e_phoff 字段:该字段也是一个偏移地址,指向程序头 (Pargram Header) 的起始地址。 

    7、e_shoff 字段:该字段是一个偏移地址,指向节区头 (Section Header) 的起始地址。

    8、e_flags 字段:该字段表示该文件的权限,常见的值有 1、2、4,分别代表 read、write、exec。 

    9、e_ehsize 字段:该字段表示 elf 文件头部大小,一般固定为 52.

    10、e_phentsize 字段:该字段表示程序头 (Program Header) 大小,一般固定为 32.

    11、e_phnum 字段:该字段表示文件中有几个程序头。 

    12、e_shentsize: 该字段表示节区头 (Section Header) 大小,一般固定为 40.

    13、e_shnum 字段:该字段表示文件中有几个节区头。

    14、e_shstrndx 字段:该字段是一个数字,这个表明了.shstrtab 节区(这个节区存储着所有节区的名字,例如.text)的节区头是第几个。 

    e_type具体值(相关值后面有英文注释,这里就不再添加中文注释了):

    32f5d964b86b9bb4ea7184c7975649bd.png

    解析代码如下:

    struct DataOffest parseSoHeader(FILE *fp,struct DataOffest off){    Elf32_Ehdr header;    int i = 0;    fseek(fp,0,SEEK_SET);    fread(&header,1,sizeof(header),fp);    printf("ELF Header:\n");    printf("    Header Magic: ");    for (i = 0; i < 16; i++)    {        printf("%02x ",header.e_ident[i]);    }    printf("\n");    printf("    So File Type: 0x%02x",header.e_type);    switch (header.e_type)    {    case 0x00:        printf("(No file type)\n");        break;    case 0x01:        printf("(Relocatable file)\n");        break;    case 0x02:        printf("(Executable file)\n");        break;    case 0x03:        printf("(Shared object file)\n");        break;    case 0x04:        printf("(Core file)\n");        break;    case 0xff00:        printf("(Beginning of processor-specific codes)\n");        break;    case 0xffff:        printf("(Processor-specific)\n");        break;    default:        printf("\n");        break;    }    printf("    Required Architecture: 0x%04x",header.e_machine);    if (header.e_machine == 0x28)    {        printf("(ARM)\n");    }    else    {        printf("\n");    }    printf("    Version: 0x%02x\n",header.e_version);    printf("    Start Program Address: 0x%08x\n",header.e_entry);    printf("    Program Header Offest: 0x%08x\n",header.e_phoff);    off.programheadoffset = header.e_phoff;    printf("    Section Header Offest: 0x%08x\n",header.e_shoff);    off.sectionheadoffest = header.e_shoff;    printf("    Processor-specific Flags: 0x%08x\n",header.e_flags);    printf("    ELF Header Size: 0x%04x\n",header.e_ehsize);    printf("    Size of an entry in the program header table: 0x%04x\n",header.e_phentsize);    printf("    Program Header Size: 0x%04x\n",header.e_phnum);    off.programsize = header.e_phnum;    printf("    Size of an entry in the section header table: 0x%04x\n",header.e_shentsize);    printf("    Section Header Size: 0x%04x\n",header.e_shnum);    off.sectionsize = header.e_shnum;    printf("    String Section Index: 0x%04x\n",header.e_shstrndx);    off.shstrtabindex = header.e_shstrndx;    return off;}

    三、程序头(Program Header)解析

    程序头在elf.h文件中的数据格式是Elf32_Phdr,如下图所示:

    43770ba220444a21b5ff7b9100e16a5d.png

    每个字段解释如下:

    1、p_type 字段:该字段表明了段 (Segment) 类型,例如PT_LOAD类型,具体值看下图,实在有点多,没办法这里写完。 

    2、p_offest 字段:该字段表明了这个段在该 so 文件的起始地址。 

    3、p_vaddr 字段:该字段指明了加载进内存后的虚拟地址,我们静态解析时用不到该字段。 

    4、p_paddr 字段:该字段指明加载进内存后的实际物理地址,跟上面的那个字段一样,解析时用不到。 

    5、p_filesz 字段:该字段表明了这个段的大小,单位为字节。 

    6、p_memsz 字段:该字段表明了这个段加载到内存后使用的字节数。 

    7、p_flags 字段:该字段跟 elf 头部的 e_flags 一样,指明了该段的属性,是可读还是可写。 

    8、p_align 字段:该字段用来指明在内存中对齐字节数的。 

    p_type字段具体取值:

    49c8080c82786d0faf0c197849d31d4f.png

    解析代码:

    struct DataOffest parseSoPargramHeader(FILE *fp,struct DataOffest off){    Elf32_Half init;    Elf32_Half addr;    int i;    Elf32_Phdr programHeader;        init = off.programheadoffset;    for (i = 0; i < off.programsize; i++)    {        addr = init + (i * 0x20);        fseek(fp,addr,SEEK_SET);        fread(&programHeader,1,32,fp);        switch (programHeader.p_type)        {        case 2:            off.dynameicoff = programHeader.p_offset;            off.dynameicsize = programHeader.p_filesz;            break;        default:            break;        }        printf("\n\nSegment Header %d:\n",(i + 1));        printf("    Type of segment: 0x%08x\n",programHeader.p_type);        printf("    Segment Offset: 0x%08x\n",programHeader.p_offset);        printf("    Virtual address of beginning of segment: 0x%08x\n",programHeader.p_vaddr);        printf("    Physical address of beginning of segment: 0x%08x\n",programHeader.p_paddr);        printf("    Num. of bytes in file image of segment: 0x%08x\n",programHeader.p_filesz);        printf("    Num. of bytes in mem image of segment (may be zero): 0x%08x\n",programHeader.p_memsz);        printf("    Segment flags: 0x%08x\n",programHeader.p_flags);        printf("    Segment alignment constraint: 0x%08x\n",programHeader.p_align);    }    return off;}

    四、节区头(Section Header)解析

    节区头在 elf.h 文件中的数据结构为Elf32_Shdr,如下图所示:

    5ebb7115986dddcadac055f20bdb655c.png

    每个字段解释如下:

    1、sh_name 字段:该字段是一个索引值,是.shstrtab表(节区名字字符串表)的索引,指明了该节区的名字。 

    2、sh_type 字段:该字段表明该节区的类型,例如值为SHT_PROGBITS,则该节区可能是.text或者.rodata,至于具体怎么区分,当然看 sh_name 字段。具体取值看下图。 

    3、sh_flags 字段:跟上面的一样,就不再细说了。 

    4、sh_addr 字段:该字段是一个地址,是该节区加载进内存后的地址。 

    5、sh_offset 字段:该字段也是一个地址,是该节区在该 so 文件中的偏移地址。 

    6、sh_size 字段:该字段表明了该节区的大小,单位是字节。 

    7、sh_link 和 sh_info 字段:这两个字段只适用于少数节区,我们这里解析用不到,感兴趣的可以去看官方文档。 

    8、sh_addralign 字段:该字段指明在内存中的对齐字节。 

    9、sh_entsize 字段:该字段指明了该节区中每个项占用的字节数。 

    sh_type取值:

    52aa2e0901ec03fe6a106d6da3378d67.png

    解析代码:

    struct DataOffest parseSoSectionHeader(FILE *fp,struct DataOffest off,struct ShstrtabTable StrList[100]){    Elf32_Half init;    Elf32_Half addr;    Elf32_Shdr sectionHeader;    int i,id,n;    char ch;    int k = 0;    init = off.sectionheadoffest;    for (i = 0; i < off.sectionsize; i++)    {        addr = init + (i * 0x28);        fseek(fp,addr,SEEK_SET);        fread(&sectionHeader,1,40,fp);         switch (sectionHeader.sh_type)        {        case 2:            off.symtaboff = sectionHeader.sh_offset;            off.symtabsize = sectionHeader.sh_size;            break;        case 3:            if(k == 0)            {                off.stroffset = sectionHeader.sh_offset;                off.strsize = sectionHeader.sh_size;                k++;            }            else if (k == 1)            {                off.str1offset = sectionHeader.sh_offset;                off.str1size = sectionHeader.sh_size;                k++;            }            else            {                off.str2offset = sectionHeader.sh_offset;                off.str2size = sectionHeader.sh_size;                k++;            }            break;        default:            break;        }        id = sectionHeader.sh_name;        printf("\n\nSection Header %d\n",(i + 1));        printf("    Section Name: ");        for (n = 0; n < 50; n++)        {            ch = StrList[id].str[n];            if (ch == 0)            {                printf("\n");                break;            }            else            {                printf("%c",ch);            }        }        printf("    Section Type: 0x%08x\n",sectionHeader.sh_type);        printf("    Section Flag: 0x%08x\n",sectionHeader.sh_flags);        printf("    Address where section is to be loaded: 0x%08x\n",sectionHeader.sh_addr);        printf("    Offset: 0x%x\n",sectionHeader.sh_offset);        printf("    Size of section, in bytes: 0x%08x\n",sectionHeader.sh_size);        printf("    Section type-specific header table index link: 0x%08x\n",sectionHeader.sh_link);        printf("    Section type-specific extra information: 0x%08x\n",sectionHeader.sh_info);        printf("    Section address alignment: 0x%08x\n",sectionHeader.sh_addralign);        printf("    Size of records contained within the section: 0x%08x\n",sectionHeader.sh_entsize);    }    return off;}

    五、字符串节区解析

    PS: 从这里开始网上的参考资料很少了,特别是参考代码,所以有错误的地方还请斧正;因为以后的so加固等只涉及到几个节区,所以只解析了.shstrtab.strtab.dynstr.text.symtab.dynamic节区!!!

    在elf头部中有个e_shstrndx字段,该字段指明了.shstrtab节区头部是文件中第几个节区头部,我们可以根据这找到.shstrtab节区的偏移地址,然后读取出来,就可以为每个节区名字赋值了,然后就可以顺着锁定剩下的两个字符串节区。

    在 elf 文件中,字符串表示方式如下:字符串的头部和尾部用标示字节00标志,同时上一个字符串尾部标识符00作为下一个字符串头部标识符。例如我有两个紧邻的字符串分别是ab,那么他们在 elf 文件中 16 进制为00 97 00 98 00

    解析代码如下 (PS:因为编码问题,第一次打印字符串表没问题,但填充进 sh_name 就乱码,所以这里只放上解析.shstrtab的代码,但剩下两个节区节区代码一样):

    void parseStrSection(FILE *fp,struct DataOffest off,int flag){    int total = 0;    int i;    int ch;    int mark;    Elf32_Off init;    Elf32_Off addr;    Elf32_Word count;    mark = 1;    if (flag == 1)    {        count = off.strsize;        init = off.stroffset;    }    else if (flag == 2)    {        count = off.str1size;        init = off.str1offset;    }    else    {        count = off.str2size;        init = off.str2offset;    }         printf("String Address==>0x%x\n",init);    printf("String List %d:\n\t[1]==>",flag);    for (i = 0; i < count; i++)    {        addr = init + (i * 1);        fseek(fp,addr,SEEK_SET);        fread(&ch,1,1,fp);        if (i == 0 && ch == 0)        {            continue;        }        else if (ch != 0)        {            printf("%c",ch);        }        else if (ch == 0 && i !=0)        {            printf("\n\t[%d]==>",(++mark));        }    }    printf("\n");    }

    六、.dynamic解析

    .dynamicelf.h文件中的数据结构是Elf32-Dyn,如下图所示:

    89b9b6a0d584661b4a36fcab1793bc91.png

    第一个字段表明了类型,占 4 个字节;第二个字段是一个共用体,也占四个字节,描述了具体的项信息。解析代码如下:

    void parseSoDynamicSection(FILE *fp,struct DataOffest off){    int dynamicnum;    Elf32_Off init;    Elf32_Off addr;    Elf32_Dyn dynamicData;    int i;    init = off.dynameicoff;    dynamicnum = (off.dynameicsize / 8);    printf("Dynamic:\n");    printf("\t\tTag\t\t\tType\t\t\tName/Value\n");    for (i = 0; i < dynamicnum; i++)    {        addr = init + (i * 8);        fseek(fp,addr,SEEK_SET);        fread(&dynamicData,1,8,fp);        printf("\t\t0x%08x\t\tNOPRINTF\t\t0x%x\n",dynamicData.d_tag,dynamicData.d_un);    }    }

    七、.symtab 解析

    该节区是该 so 文件的符号表,它在elf.h文件中的数据结构是Elf32_Sym,如下所示:

    d125936fd5812382fd9053286a6b81ac.png

    每个字段解释如下:

    1、st_name 字段:该字段是一个索引值,指明了该项的名字。 

    2、st_value 字段:该字段表明了相关联符号的取值。 

    3、stz-size 字段:该字段指明了每个项所占用的字节数。 

    4、st_info 和 st_other 字段:这两个字段指明了符号的类型。 

    5、st_shndx 字段:相关索引。 

    解析代码如下(PS:由于乱码问题,索引手动固定了地址测试,有兴趣的挨个解析字符应该可以解决乱码问题):

    void parseSoDynamicSection(FILE *fp,struct DataOffest off){    int dynamicnum;    Elf32_Off init;    Elf32_Off addr;    Elf32_Dyn dynamicData;    int i;    init = off.dynameicoff;    dynamicnum = (off.dynameicsize / 8);    printf("Dynamic:\n");    printf("\t\tTag\t\t\tType\t\t\tName/Value\n");    for (i = 0; i < dynamicnum; i++)    {        addr = init + (i * 8);        fseek(fp,addr,SEEK_SET);        fread(&dynamicData,1,8,fp);        printf("\t\t0x%08x\t\tNOPRINTF\t\t0x%x\n",dynamicData.d_tag,dynamicData.d_un);    }}
    void parseSymtabSection(FILE *fp,struct DataOffest off){    Elf32_Off init;    Elf32_Off addr;    Elf32_Word count;    Elf32_Sym symtabSection;    int k,i;    init = off.symtaboff;    count = off.symtabsize;    printf("SymTable:\n");    for (i = 0; i < count; i++)    {        addr = init + (i * 16);        fseek(fp,addr,SEEK_SET);        fread(&symtabSection,1,16,fp);        printf("Symbol Name Index: 0x%x\n",symtabSection.st_name);        printf("Value or address associated with the symbol: 0x%08x\n",symtabSection.st_value);        printf("Size of the symbol: 0x%x\n",symtabSection.st_size);        printf("Symbol's type and binding attributes: %c\n",symtabSection.st_info);        printf("Must be zero; reserved: 0x%x\n",symtabSection.st_other);        printf("Which section (header table index) it's defined in: 0x%x\n",symtabSection.st_shndx);    }    }

    八、.text 解析

    PS:这部分没代码了,只简单解析一下,因为解析 arm 指令太麻烦了,估计得写个半年都不一定能搞定,后续写了会同步更新在 github!!!

    .text节区存储着可执行指令,我们可以通过节区头部的名字锁定.text的偏移地址和大小,找到该节区后,我们会发现这个节区存储的就是 arm 机器码,直接照着指令集翻译即可,没有其他的结构。通过 ida 验证如下:

    d64996f29e112cc147fdfa9dc0254d6b.png

    九、代码测试相关截图

    ca13e7c929b4b213cab2109bce2db243.png

    f5ee284b1e7b01fe72b8f8ee9a2dca3b.png

    3e8bf02d1a15a599ea03fd3e758539d7.png

    735473c479717d4da09ef9b52020aa97.png

    5cf8d423abd42d94fcb4c947f97f8b10.png

    十、frida 反调试和后序

    frida 反调试最简单的就是检查端口,检查进程名,检查 so 文件等,但最准确以及最复杂的是检查汇编指令,我们知道 frida 是通过一个大调整实现 hook,而跳转的指令就那么几条,我们是否可以通过检查每个函数第一条指令来判断是否有 frida 了!!!(ps:简单写一下原理,拉开写就太多了,这里感谢某大佬和我讨论的这个话题!!!)

    本来因为这个 so 文件解析要写到明年去了,没想到看起来代码量大,但实际要用到的地方代码量很少。。。

    源码 github 链接:

    https://github.com/windy-purple/parseso/

    64a0f588d1d424ccf6dc0fa4cd433375.png

    展开全文
  • 最近将xml读取改为rapidxml了快了许多,然而官方只有代码,没有生成各平台的库。我把生成的共享一下,里面包含了pc用的dll 和 安卓用的 v7a 和arm64 的so
  • so 文件是 elf 文件,elf 文件后缀名是.so,所以也被称之为so 文件, elf 文件是 linux 底下二进制文件,可以理解为 windows 下的PE文件,在 Android 中可以比作dll,方便函数的移植,在常用于保护 Android 软件,...

    65bcd631fca7dcee7c1e93da435f43bd.png

    so 文件是啥?so 文件是 elf 文件,elf 文件后缀名是.so,所以也被称之为so 文件, elf 文件是 linux 底下二进制文件,可以理解为 windows 下的PE文件,在 Android 中可以比作dll,方便函数的移植,在常用于保护 Android 软件,增加逆向难度。

    解析 elf 文件有啥子用?最明显的两个用处就是:1、so 加固;2、用于 frida(xposed) 的检测! 

    本文使用 c 语言,编译器为 vscode。如有错误,还请斧正!!!

    一、SO 文件整体格式

    so 文件大体上可分为四部分,一般来说从上往下是ELF头部->Pargarm头部->节区(Section)->节区头,其中,除了ELF头部在文件位置固定不变外,其余三部分的位置都不固定。整体结构图可以参考非虫大佬的那张图,图片如下:

    7e0d0f35f5f5812abdef738d09b528b0.png

    解析语言之所以选择 c 语言,有两个原因:

    1、做 so 加固的时候可以需要用到,这里就干脆用 c 写成一个模板,哪里需要就哪里改,不像上次解析 dex 文件的时候用 python 写,结果后面写指令还原的时候需要用的时候在写一遍 c 版本代价太大了;

    2、在安卓源码中,有个elf.h文件,这个文件定义了我们解析时需要用到的所有数据结构,并且给出了参考注释,是很好的参考资料。elf.h文件路径如下:

    84cffcc47dea86c3f30eb2a67ce896f0.png

    二、解析 ELF 头部

    ELF 头部数据格式在 elf.h 文件中已经给出,如下图所示:

    5a3f17fba28d44b85a775791a502de51.png

    每个字段解释如下:

    1、e_ident 数组:前4个字节为.ELF,是 elf 标志头,第 5 个字节为该文件标志符,为 1 代表这是一个 32 位的 elf 文件,后面几个字节代表版本等信息。

    2、e_type 字段:表示是可执行文件还是链接文件等,安卓上的 so 文件就是分享文件,一般该字段为 3,详细请看下图。

    3、e_machine 字段:该字段标志该文件运行在什么机器架构上,例如 ARM。

    4、e_version 字段:该字段表示当前 so 文件的版本信息,一般为 1

    5、e_entry 字段:该字段是一个偏移地址,为程序启动的地址。 

    6、e_phoff 字段:该字段也是一个偏移地址,指向程序头 (Pargram Header) 的起始地址。 

    7、e_shoff 字段:该字段是一个偏移地址,指向节区头 (Section Header) 的起始地址。

    8、e_flags 字段:该字段表示该文件的权限,常见的值有 1、2、4,分别代表 read、write、exec。 

    9、e_ehsize 字段:该字段表示 elf 文件头部大小,一般固定为 52.

    10、e_phentsize 字段:该字段表示程序头 (Program Header) 大小,一般固定为 32.

    11、e_phnum 字段:该字段表示文件中有几个程序头。 

    12、e_shentsize: 该字段表示节区头 (Section Header) 大小,一般固定为 40.

    13、e_shnum 字段:该字段表示文件中有几个节区头。

    14、e_shstrndx 字段:该字段是一个数字,这个表明了.shstrtab 节区(这个节区存储着所有节区的名字,例如.text)的节区头是第几个。 

    e_type具体值(相关值后面有英文注释,这里就不再添加中文注释了):

    2071fb71a3cae28e1635fe6142f86e88.png

    解析代码如下:

    struct DataOffest parseSoHeader(FILE *fp,struct DataOffest off){    Elf32_Ehdr header;    int i = 0;    fseek(fp,0,SEEK_SET);    fread(&header,1,sizeof(header),fp);    printf("ELF Header:\n");    printf("    Header Magic: ");    for (i = 0; i < 16; i++)    {        printf("%02x ",header.e_ident[i]);    }    printf("\n");    printf("    So File Type: 0x%02x",header.e_type);    switch (header.e_type)    {    case 0x00:        printf("(No file type)\n");        break;    case 0x01:        printf("(Relocatable file)\n");        break;    case 0x02:        printf("(Executable file)\n");        break;    case 0x03:        printf("(Shared object file)\n");        break;    case 0x04:        printf("(Core file)\n");        break;    case 0xff00:        printf("(Beginning of processor-specific codes)\n");        break;    case 0xffff:        printf("(Processor-specific)\n");        break;    default:        printf("\n");        break;    }    printf("    Required Architecture: 0x%04x",header.e_machine);    if (header.e_machine == 0x28)    {        printf("(ARM)\n");    }    else    {        printf("\n");    }    printf("    Version: 0x%02x\n",header.e_version);    printf("    Start Program Address: 0x%08x\n",header.e_entry);    printf("    Program Header Offest: 0x%08x\n",header.e_phoff);    off.programheadoffset = header.e_phoff;    printf("    Section Header Offest: 0x%08x\n",header.e_shoff);    off.sectionheadoffest = header.e_shoff;    printf("    Processor-specific Flags: 0x%08x\n",header.e_flags);    printf("    ELF Header Size: 0x%04x\n",header.e_ehsize);    printf("    Size of an entry in the program header table: 0x%04x\n",header.e_phentsize);    printf("    Program Header Size: 0x%04x\n",header.e_phnum);    off.programsize = header.e_phnum;    printf("    Size of an entry in the section header table: 0x%04x\n",header.e_shentsize);    printf("    Section Header Size: 0x%04x\n",header.e_shnum);    off.sectionsize = header.e_shnum;    printf("    String Section Index: 0x%04x\n",header.e_shstrndx);    off.shstrtabindex = header.e_shstrndx;    return off;}

    三、程序头(Program Header)解析

    程序头在elf.h文件中的数据格式是Elf32_Phdr,如下图所示:

    a35d6aa7f1d2233e9896676a8233f4e6.png

    每个字段解释如下:

    1、p_type 字段:该字段表明了段 (Segment) 类型,例如PT_LOAD类型,具体值看下图,实在有点多,没办法这里写完。 

    2、p_offest 字段:该字段表明了这个段在该 so 文件的起始地址。 

    3、p_vaddr 字段:该字段指明了加载进内存后的虚拟地址,我们静态解析时用不到该字段。 

    4、p_paddr 字段:该字段指明加载进内存后的实际物理地址,跟上面的那个字段一样,解析时用不到。 

    5、p_filesz 字段:该字段表明了这个段的大小,单位为字节。 

    6、p_memsz 字段:该字段表明了这个段加载到内存后使用的字节数。 

    7、p_flags 字段:该字段跟 elf 头部的 e_flags 一样,指明了该段的属性,是可读还是可写。 

    8、p_align 字段:该字段用来指明在内存中对齐字节数的。 

    p_type字段具体取值:

    ebf84a200e0157d559ca77e872c6a754.png

    解析代码:

    struct DataOffest parseSoPargramHeader(FILE *fp,struct DataOffest off){    Elf32_Half init;    Elf32_Half addr;    int i;    Elf32_Phdr programHeader;        init = off.programheadoffset;    for (i = 0; i < off.programsize; i++)    {        addr = init + (i * 0x20);        fseek(fp,addr,SEEK_SET);        fread(&programHeader,1,32,fp);        switch (programHeader.p_type)        {        case 2:            off.dynameicoff = programHeader.p_offset;            off.dynameicsize = programHeader.p_filesz;            break;        default:            break;        }        printf("\n\nSegment Header %d:\n",(i + 1));        printf("    Type of segment: 0x%08x\n",programHeader.p_type);        printf("    Segment Offset: 0x%08x\n",programHeader.p_offset);        printf("    Virtual address of beginning of segment: 0x%08x\n",programHeader.p_vaddr);        printf("    Physical address of beginning of segment: 0x%08x\n",programHeader.p_paddr);        printf("    Num. of bytes in file image of segment: 0x%08x\n",programHeader.p_filesz);        printf("    Num. of bytes in mem image of segment (may be zero): 0x%08x\n",programHeader.p_memsz);        printf("    Segment flags: 0x%08x\n",programHeader.p_flags);        printf("    Segment alignment constraint: 0x%08x\n",programHeader.p_align);    }    return off;}

    四、节区头(Section Header)解析

    节区头在 elf.h 文件中的数据结构为Elf32_Shdr,如下图所示:

    0039dc34c38965d5dbc3f4a6803d2ced.png

    每个字段解释如下:

    1、sh_name 字段:该字段是一个索引值,是.shstrtab表(节区名字字符串表)的索引,指明了该节区的名字。 

    2、sh_type 字段:该字段表明该节区的类型,例如值为SHT_PROGBITS,则该节区可能是.text或者.rodata,至于具体怎么区分,当然看 sh_name 字段。具体取值看下图。 

    3、sh_flags 字段:跟上面的一样,就不再细说了。 

    4、sh_addr 字段:该字段是一个地址,是该节区加载进内存后的地址。 

    5、sh_offset 字段:该字段也是一个地址,是该节区在该 so 文件中的偏移地址。 

    6、sh_size 字段:该字段表明了该节区的大小,单位是字节。 

    7、sh_link 和 sh_info 字段:这两个字段只适用于少数节区,我们这里解析用不到,感兴趣的可以去看官方文档。 

    8、sh_addralign 字段:该字段指明在内存中的对齐字节。 

    9、sh_entsize 字段:该字段指明了该节区中每个项占用的字节数。 

    sh_type取值:

    54b7317c7421236430364d474fb52044.png

    解析代码:

    struct DataOffest parseSoSectionHeader(FILE *fp,struct DataOffest off,struct ShstrtabTable StrList[100]){    Elf32_Half init;    Elf32_Half addr;    Elf32_Shdr sectionHeader;    int i,id,n;    char ch;    int k = 0;    init = off.sectionheadoffest;    for (i = 0; i < off.sectionsize; i++)    {        addr = init + (i * 0x28);        fseek(fp,addr,SEEK_SET);        fread(&sectionHeader,1,40,fp);         switch (sectionHeader.sh_type)        {        case 2:            off.symtaboff = sectionHeader.sh_offset;            off.symtabsize = sectionHeader.sh_size;            break;        case 3:            if(k == 0)            {                off.stroffset = sectionHeader.sh_offset;                off.strsize = sectionHeader.sh_size;                k++;            }            else if (k == 1)            {                off.str1offset = sectionHeader.sh_offset;                off.str1size = sectionHeader.sh_size;                k++;            }            else            {                off.str2offset = sectionHeader.sh_offset;                off.str2size = sectionHeader.sh_size;                k++;            }            break;        default:            break;        }        id = sectionHeader.sh_name;        printf("\n\nSection Header %d\n",(i + 1));        printf("    Section Name: ");        for (n = 0; n < 50; n++)        {            ch = StrList[id].str[n];            if (ch == 0)            {                printf("\n");                break;            }            else            {                printf("%c",ch);            }        }        printf("    Section Type: 0x%08x\n",sectionHeader.sh_type);        printf("    Section Flag: 0x%08x\n",sectionHeader.sh_flags);        printf("    Address where section is to be loaded: 0x%08x\n",sectionHeader.sh_addr);        printf("    Offset: 0x%x\n",sectionHeader.sh_offset);        printf("    Size of section, in bytes: 0x%08x\n",sectionHeader.sh_size);        printf("    Section type-specific header table index link: 0x%08x\n",sectionHeader.sh_link);        printf("    Section type-specific extra information: 0x%08x\n",sectionHeader.sh_info);        printf("    Section address alignment: 0x%08x\n",sectionHeader.sh_addralign);        printf("    Size of records contained within the section: 0x%08x\n",sectionHeader.sh_entsize);    }    return off;}

    五、字符串节区解析

    PS: 从这里开始网上的参考资料很少了,特别是参考代码,所以有错误的地方还请斧正;因为以后的so加固等只涉及到几个节区,所以只解析了.shstrtab.strtab.dynstr.text.symtab.dynamic节区!!!

    在elf头部中有个e_shstrndx字段,该字段指明了.shstrtab节区头部是文件中第几个节区头部,我们可以根据这找到.shstrtab节区的偏移地址,然后读取出来,就可以为每个节区名字赋值了,然后就可以顺着锁定剩下的两个字符串节区。

    在 elf 文件中,字符串表示方式如下:字符串的头部和尾部用标示字节00标志,同时上一个字符串尾部标识符00作为下一个字符串头部标识符。例如我有两个紧邻的字符串分别是ab,那么他们在 elf 文件中 16 进制为00 97 00 98 00

    解析代码如下 (PS:因为编码问题,第一次打印字符串表没问题,但填充进 sh_name 就乱码,所以这里只放上解析.shstrtab的代码,但剩下两个节区节区代码一样):

    void parseStrSection(FILE *fp,struct DataOffest off,int flag){    int total = 0;    int i;    int ch;    int mark;    Elf32_Off init;    Elf32_Off addr;    Elf32_Word count;    mark = 1;    if (flag == 1)    {        count = off.strsize;        init = off.stroffset;    }    else if (flag == 2)    {        count = off.str1size;        init = off.str1offset;    }    else    {        count = off.str2size;        init = off.str2offset;    }         printf("String Address==>0x%x\n",init);    printf("String List %d:\n\t[1]==>",flag);    for (i = 0; i < count; i++)    {        addr = init + (i * 1);        fseek(fp,addr,SEEK_SET);        fread(&ch,1,1,fp);        if (i == 0 && ch == 0)        {            continue;        }        else if (ch != 0)        {            printf("%c",ch);        }        else if (ch == 0 && i !=0)        {            printf("\n\t[%d]==>",(++mark));        }    }    printf("\n");    }

    六、.dynamic解析

    .dynamicelf.h文件中的数据结构是Elf32-Dyn,如下图所示:

    df2e8024d26c0d5ca41a8fb43e133129.png

    第一个字段表明了类型,占 4 个字节;第二个字段是一个共用体,也占四个字节,描述了具体的项信息。解析代码如下:

    void parseSoDynamicSection(FILE *fp,struct DataOffest off){    int dynamicnum;    Elf32_Off init;    Elf32_Off addr;    Elf32_Dyn dynamicData;    int i;    init = off.dynameicoff;    dynamicnum = (off.dynameicsize / 8);    printf("Dynamic:\n");    printf("\t\tTag\t\t\tType\t\t\tName/Value\n");    for (i = 0; i < dynamicnum; i++)    {        addr = init + (i * 8);        fseek(fp,addr,SEEK_SET);        fread(&dynamicData,1,8,fp);        printf("\t\t0x%08x\t\tNOPRINTF\t\t0x%x\n",dynamicData.d_tag,dynamicData.d_un);    }    }

    七、.symtab 解析

    该节区是该 so 文件的符号表,它在elf.h文件中的数据结构是Elf32_Sym,如下所示:

    7bc32bb3c31db23ce7168af03e1b1da5.png

    每个字段解释如下:

    1、st_name 字段:该字段是一个索引值,指明了该项的名字。 

    2、st_value 字段:该字段表明了相关联符号的取值。 

    3、stz-size 字段:该字段指明了每个项所占用的字节数。 

    4、st_info 和 st_other 字段:这两个字段指明了符号的类型。 

    5、st_shndx 字段:相关索引。 

    解析代码如下(PS:由于乱码问题,索引手动固定了地址测试,有兴趣的挨个解析字符应该可以解决乱码问题):

    void parseSoDynamicSection(FILE *fp,struct DataOffest off){    int dynamicnum;    Elf32_Off init;    Elf32_Off addr;    Elf32_Dyn dynamicData;    int i;    init = off.dynameicoff;    dynamicnum = (off.dynameicsize / 8);    printf("Dynamic:\n");    printf("\t\tTag\t\t\tType\t\t\tName/Value\n");    for (i = 0; i < dynamicnum; i++)    {        addr = init + (i * 8);        fseek(fp,addr,SEEK_SET);        fread(&dynamicData,1,8,fp);        printf("\t\t0x%08x\t\tNOPRINTF\t\t0x%x\n",dynamicData.d_tag,dynamicData.d_un);    }}
    void parseSymtabSection(FILE *fp,struct DataOffest off){    Elf32_Off init;    Elf32_Off addr;    Elf32_Word count;    Elf32_Sym symtabSection;    int k,i;    init = off.symtaboff;    count = off.symtabsize;    printf("SymTable:\n");    for (i = 0; i < count; i++)    {        addr = init + (i * 16);        fseek(fp,addr,SEEK_SET);        fread(&symtabSection,1,16,fp);        printf("Symbol Name Index: 0x%x\n",symtabSection.st_name);        printf("Value or address associated with the symbol: 0x%08x\n",symtabSection.st_value);        printf("Size of the symbol: 0x%x\n",symtabSection.st_size);        printf("Symbol's type and binding attributes: %c\n",symtabSection.st_info);        printf("Must be zero; reserved: 0x%x\n",symtabSection.st_other);        printf("Which section (header table index) it's defined in: 0x%x\n",symtabSection.st_shndx);    }    }

    八、.text 解析

    PS:这部分没代码了,只简单解析一下,因为解析 arm 指令太麻烦了,估计得写个半年都不一定能搞定,后续写了会同步更新在 github!!!

    .text节区存储着可执行指令,我们可以通过节区头部的名字锁定.text的偏移地址和大小,找到该节区后,我们会发现这个节区存储的就是 arm 机器码,直接照着指令集翻译即可,没有其他的结构。通过 ida 验证如下:

    0875873b7807d4f5006ec8c3fd106367.png

    九、代码测试相关截图

    960b50fd02bcbeb3a36082a8b80646ab.png

    b9339b4a0e492ac0af32898cb6719f78.png

    5936e5d172a5cd7957efe6831083ad9b.png

    6ad88c699e9451d9069cbf66a4bab9cf.png

    e093b55c45b45800f55e30da95fdc611.png

    十、frida 反调试和后序

    frida 反调试最简单的就是检查端口,检查进程名,检查 so 文件等,但最准确以及最复杂的是检查汇编指令,我们知道 frida 是通过一个大调整实现 hook,而跳转的指令就那么几条,我们是否可以通过检查每个函数第一条指令来判断是否有 frida 了!!!(ps:简单写一下原理,拉开写就太多了,这里感谢某大佬和我讨论的这个话题!!!)

    本来因为这个 so 文件解析要写到明年去了,没想到看起来代码量大,但实际要用到的地方代码量很少。。。

    源码 github 链接:

    https://github.com/windy-purple/parseso/

    4990ef89d9076e8277d85572715c377c.png

    展开全文
  • 1 承接上一篇说rel got plt的帖子, 这篇...上一贴《安卓SO中GOT REL PLT 作用与关系》 https://bbs.pediy.com/thread-221821.htm    有前辈写过类似的内容 《基于Android的ELF PLT/GOT符号重定向过程及ELF H...

    转:https://bbs.pediy.com/thread-222731.htm

    1 承接上一篇说rel got plt的帖子, 这篇叨叨下重定位类型

    上一贴《安卓SO中GOT REL PLT 作用与关系》

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

     

    有前辈写过类似的内容

    《基于Android的ELF PLT/GOT符号重定向过程及ELF Hook实现》

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

     

     

    2 重定位类型总结:

     

     

    3 这个有什么卵用?

    (1)提高看汇编的能力,摆弄明白了,一眼就能从汇编里看出来,怎么访问或调用符号的。

    (2)got表hook时,有点用处。

    (3)DIY魔改ELF时,有点用处。

    (4)写调试器时,有点用处。

    (5)写VMP或变形时,有点用处。

     

     

    4 样本上传弄好了,有感兴趣的可以看下

    都是很无聊很蛋疼的细节,估计没什么人感兴趣~

     

    展开全文
  • so 文件是 elf 文件,elf 文件后缀名是.so,所以也被称之为so 文件, elf 文件是 linux 底下二进制文件,可以理解为 windows 下的PE文件,在 Android 中可以比作dll,方便函数的移植,在常用于保护 Android 软件,...

    7abb13de7f9d167d307058906e50fd0a.png

    so 文件是啥?so 文件是 elf 文件,elf 文件后缀名是.so,所以也被称之为so 文件, elf 文件是 linux 底下二进制文件,可以理解为 windows 下的PE文件,在 Android 中可以比作dll,方便函数的移植,在常用于保护 Android 软件,增加逆向难度。

    解析 elf 文件有啥子用?最明显的两个用处就是:1、so 加固;2、用于 frida(xposed) 的检测! 

    本文使用 c 语言,编译器为 vscode。如有错误,还请斧正!!!

    一、SO 文件整体格式

    so 文件大体上可分为四部分,一般来说从上往下是ELF头部->Pargarm头部->节区(Section)->节区头,其中,除了ELF头部在文件位置固定不变外,其余三部分的位置都不固定。整体结构图可以参考非虫大佬的那张图,图片如下:

    a8d6eeb906de6a30dd51d7ae5d1fe701.png

    解析语言之所以选择 c 语言,有两个原因:

    1、做 so 加固的时候可以需要用到,这里就干脆用 c 写成一个模板,哪里需要就哪里改,不像上次解析 dex 文件的时候用 python 写,结果后面写指令还原的时候需要用的时候在写一遍 c 版本代价太大了;

    2、在安卓源码中,有个elf.h文件,这个文件定义了我们解析时需要用到的所有数据结构,并且给出了参考注释,是很好的参考资料。elf.h文件路径如下:

    1ad469c0f3f2fe5c04ef5e232cc76a7b.png

    二、解析 ELF 头部

    ELF 头部数据格式在 elf.h 文件中已经给出,如下图所示:

    974d88ecfc1108e647c41c0ae4464108.png

    每个字段解释如下:

    1、e_ident 数组:前4个字节为.ELF,是 elf 标志头,第 5 个字节为该文件标志符,为 1 代表这是一个 32 位的 elf 文件,后面几个字节代表版本等信息。

    2、e_type 字段:表示是可执行文件还是链接文件等,安卓上的 so 文件就是分享文件,一般该字段为 3,详细请看下图。

    3、e_machine 字段:该字段标志该文件运行在什么机器架构上,例如 ARM。

    4、e_version 字段:该字段表示当前 so 文件的版本信息,一般为 1

    5、e_entry 字段:该字段是一个偏移地址,为程序启动的地址。 

    6、e_phoff 字段:该字段也是一个偏移地址,指向程序头 (Pargram Header) 的起始地址。 

    7、e_shoff 字段:该字段是一个偏移地址,指向节区头 (Section Header) 的起始地址。

    8、e_flags 字段:该字段表示该文件的权限,常见的值有 1、2、4,分别代表 read、write、exec。 

    9、e_ehsize 字段:该字段表示 elf 文件头部大小,一般固定为 52.

    10、e_phentsize 字段:该字段表示程序头 (Program Header) 大小,一般固定为 32.

    11、e_phnum 字段:该字段表示文件中有几个程序头。 

    12、e_shentsize: 该字段表示节区头 (Section Header) 大小,一般固定为 40.

    13、e_shnum 字段:该字段表示文件中有几个节区头。

    14、e_shstrndx 字段:该字段是一个数字,这个表明了.shstrtab 节区(这个节区存储着所有节区的名字,例如.text)的节区头是第几个。 

    e_type具体值(相关值后面有英文注释,这里就不再添加中文注释了):

    491a0f659199395b39b3137adf7bd683.png

    解析代码如下:

    struct DataOffest parseSoHeader(FILE *fp,struct DataOffest off){    Elf32_Ehdr header;    int i = 0;    fseek(fp,0,SEEK_SET);    fread(&header,1,sizeof(header),fp);    printf("ELF Header:\n");    printf("    Header Magic: ");    for (i = 0; i < 16; i++)    {        printf("%02x ",header.e_ident[i]);    }    printf("\n");    printf("    So File Type: 0x%02x",header.e_type);    switch (header.e_type)    {    case 0x00:        printf("(No file type)\n");        break;    case 0x01:        printf("(Relocatable file)\n");        break;    case 0x02:        printf("(Executable file)\n");        break;    case 0x03:        printf("(Shared object file)\n");        break;    case 0x04:        printf("(Core file)\n");        break;    case 0xff00:        printf("(Beginning of processor-specific codes)\n");        break;    case 0xffff:        printf("(Processor-specific)\n");        break;    default:        printf("\n");        break;    }    printf("    Required Architecture: 0x%04x",header.e_machine);    if (header.e_machine == 0x28)    {        printf("(ARM)\n");    }    else    {        printf("\n");    }    printf("    Version: 0x%02x\n",header.e_version);    printf("    Start Program Address: 0x%08x\n",header.e_entry);    printf("    Program Header Offest: 0x%08x\n",header.e_phoff);    off.programheadoffset = header.e_phoff;    printf("    Section Header Offest: 0x%08x\n",header.e_shoff);    off.sectionheadoffest = header.e_shoff;    printf("    Processor-specific Flags: 0x%08x\n",header.e_flags);    printf("    ELF Header Size: 0x%04x\n",header.e_ehsize);    printf("    Size of an entry in the program header table: 0x%04x\n",header.e_phentsize);    printf("    Program Header Size: 0x%04x\n",header.e_phnum);    off.programsize = header.e_phnum;    printf("    Size of an entry in the section header table: 0x%04x\n",header.e_shentsize);    printf("    Section Header Size: 0x%04x\n",header.e_shnum);    off.sectionsize = header.e_shnum;    printf("    String Section Index: 0x%04x\n",header.e_shstrndx);    off.shstrtabindex = header.e_shstrndx;    return off;}

    三、程序头(Program Header)解析

    程序头在elf.h文件中的数据格式是Elf32_Phdr,如下图所示:

    61ac82fa67e34e9480e816893921ab30.png

    每个字段解释如下:

    1、p_type 字段:该字段表明了段 (Segment) 类型,例如PT_LOAD类型,具体值看下图,实在有点多,没办法这里写完。 

    2、p_offest 字段:该字段表明了这个段在该 so 文件的起始地址。 

    3、p_vaddr 字段:该字段指明了加载进内存后的虚拟地址,我们静态解析时用不到该字段。 

    4、p_paddr 字段:该字段指明加载进内存后的实际物理地址,跟上面的那个字段一样,解析时用不到。 

    5、p_filesz 字段:该字段表明了这个段的大小,单位为字节。 

    6、p_memsz 字段:该字段表明了这个段加载到内存后使用的字节数。 

    7、p_flags 字段:该字段跟 elf 头部的 e_flags 一样,指明了该段的属性,是可读还是可写。 

    8、p_align 字段:该字段用来指明在内存中对齐字节数的。 

    p_type字段具体取值:

    ecfe3fe4515568118ed6bbe873d80c08.png

    解析代码:

    struct DataOffest parseSoPargramHeader(FILE *fp,struct DataOffest off){    Elf32_Half init;    Elf32_Half addr;    int i;    Elf32_Phdr programHeader;        init = off.programheadoffset;    for (i = 0; i < off.programsize; i++)    {        addr = init + (i * 0x20);        fseek(fp,addr,SEEK_SET);        fread(&programHeader,1,32,fp);        switch (programHeader.p_type)        {        case 2:            off.dynameicoff = programHeader.p_offset;            off.dynameicsize = programHeader.p_filesz;            break;        default:            break;        }        printf("\n\nSegment Header %d:\n",(i + 1));        printf("    Type of segment: 0x%08x\n",programHeader.p_type);        printf("    Segment Offset: 0x%08x\n",programHeader.p_offset);        printf("    Virtual address of beginning of segment: 0x%08x\n",programHeader.p_vaddr);        printf("    Physical address of beginning of segment: 0x%08x\n",programHeader.p_paddr);        printf("    Num. of bytes in file image of segment: 0x%08x\n",programHeader.p_filesz);        printf("    Num. of bytes in mem image of segment (may be zero): 0x%08x\n",programHeader.p_memsz);        printf("    Segment flags: 0x%08x\n",programHeader.p_flags);        printf("    Segment alignment constraint: 0x%08x\n",programHeader.p_align);    }    return off;}

    四、节区头(Section Header)解析

    节区头在 elf.h 文件中的数据结构为Elf32_Shdr,如下图所示:

    c1ebb8a0952de075b2fc0c256f937dab.png

    每个字段解释如下:

    1、sh_name 字段:该字段是一个索引值,是.shstrtab表(节区名字字符串表)的索引,指明了该节区的名字。 

    2、sh_type 字段:该字段表明该节区的类型,例如值为SHT_PROGBITS,则该节区可能是.text或者.rodata,至于具体怎么区分,当然看 sh_name 字段。具体取值看下图。 

    3、sh_flags 字段:跟上面的一样,就不再细说了。 

    4、sh_addr 字段:该字段是一个地址,是该节区加载进内存后的地址。 

    5、sh_offset 字段:该字段也是一个地址,是该节区在该 so 文件中的偏移地址。 

    6、sh_size 字段:该字段表明了该节区的大小,单位是字节。 

    7、sh_link 和 sh_info 字段:这两个字段只适用于少数节区,我们这里解析用不到,感兴趣的可以去看官方文档。 

    8、sh_addralign 字段:该字段指明在内存中的对齐字节。 

    9、sh_entsize 字段:该字段指明了该节区中每个项占用的字节数。 

    sh_type取值:

    ae06f47c540c287879270369624b15f9.png

    解析代码:

    struct DataOffest parseSoSectionHeader(FILE *fp,struct DataOffest off,struct ShstrtabTable StrList[100]){    Elf32_Half init;    Elf32_Half addr;    Elf32_Shdr sectionHeader;    int i,id,n;    char ch;    int k = 0;    init = off.sectionheadoffest;    for (i = 0; i < off.sectionsize; i++)    {        addr = init + (i * 0x28);        fseek(fp,addr,SEEK_SET);        fread(&sectionHeader,1,40,fp);         switch (sectionHeader.sh_type)        {        case 2:            off.symtaboff = sectionHeader.sh_offset;            off.symtabsize = sectionHeader.sh_size;            break;        case 3:            if(k == 0)            {                off.stroffset = sectionHeader.sh_offset;                off.strsize = sectionHeader.sh_size;                k++;            }            else if (k == 1)            {                off.str1offset = sectionHeader.sh_offset;                off.str1size = sectionHeader.sh_size;                k++;            }            else            {                off.str2offset = sectionHeader.sh_offset;                off.str2size = sectionHeader.sh_size;                k++;            }            break;        default:            break;        }        id = sectionHeader.sh_name;        printf("\n\nSection Header %d\n",(i + 1));        printf("    Section Name: ");        for (n = 0; n < 50; n++)        {            ch = StrList[id].str[n];            if (ch == 0)            {                printf("\n");                break;            }            else            {                printf("%c",ch);            }        }        printf("    Section Type: 0x%08x\n",sectionHeader.sh_type);        printf("    Section Flag: 0x%08x\n",sectionHeader.sh_flags);        printf("    Address where section is to be loaded: 0x%08x\n",sectionHeader.sh_addr);        printf("    Offset: 0x%x\n",sectionHeader.sh_offset);        printf("    Size of section, in bytes: 0x%08x\n",sectionHeader.sh_size);        printf("    Section type-specific header table index link: 0x%08x\n",sectionHeader.sh_link);        printf("    Section type-specific extra information: 0x%08x\n",sectionHeader.sh_info);        printf("    Section address alignment: 0x%08x\n",sectionHeader.sh_addralign);        printf("    Size of records contained within the section: 0x%08x\n",sectionHeader.sh_entsize);    }    return off;}

    五、字符串节区解析

    PS: 从这里开始网上的参考资料很少了,特别是参考代码,所以有错误的地方还请斧正;因为以后的so加固等只涉及到几个节区,所以只解析了.shstrtab.strtab.dynstr.text.symtab.dynamic节区!!!

    在elf头部中有个e_shstrndx字段,该字段指明了.shstrtab节区头部是文件中第几个节区头部,我们可以根据这找到.shstrtab节区的偏移地址,然后读取出来,就可以为每个节区名字赋值了,然后就可以顺着锁定剩下的两个字符串节区。

    在 elf 文件中,字符串表示方式如下:字符串的头部和尾部用标示字节00标志,同时上一个字符串尾部标识符00作为下一个字符串头部标识符。例如我有两个紧邻的字符串分别是ab,那么他们在 elf 文件中 16 进制为00 97 00 98 00

    解析代码如下 (PS:因为编码问题,第一次打印字符串表没问题,但填充进 sh_name 就乱码,所以这里只放上解析.shstrtab的代码,但剩下两个节区节区代码一样):

    void parseStrSection(FILE *fp,struct DataOffest off,int flag){    int total = 0;    int i;    int ch;    int mark;    Elf32_Off init;    Elf32_Off addr;    Elf32_Word count;    mark = 1;    if (flag == 1)    {        count = off.strsize;        init = off.stroffset;    }    else if (flag == 2)    {        count = off.str1size;        init = off.str1offset;    }    else    {        count = off.str2size;        init = off.str2offset;    }         printf("String Address==>0x%x\n",init);    printf("String List %d:\n\t[1]==>",flag);    for (i = 0; i < count; i++)    {        addr = init + (i * 1);        fseek(fp,addr,SEEK_SET);        fread(&ch,1,1,fp);        if (i == 0 && ch == 0)        {            continue;        }        else if (ch != 0)        {            printf("%c",ch);        }        else if (ch == 0 && i !=0)        {            printf("\n\t[%d]==>",(++mark));        }    }    printf("\n");    }

    六、.dynamic解析

    .dynamicelf.h文件中的数据结构是Elf32-Dyn,如下图所示:

    bb6f30ef85c37f85d13d0b616ff5766a.png

    第一个字段表明了类型,占 4 个字节;第二个字段是一个共用体,也占四个字节,描述了具体的项信息。解析代码如下:

    void parseSoDynamicSection(FILE *fp,struct DataOffest off){    int dynamicnum;    Elf32_Off init;    Elf32_Off addr;    Elf32_Dyn dynamicData;    int i;    init = off.dynameicoff;    dynamicnum = (off.dynameicsize / 8);    printf("Dynamic:\n");    printf("\t\tTag\t\t\tType\t\t\tName/Value\n");    for (i = 0; i < dynamicnum; i++)    {        addr = init + (i * 8);        fseek(fp,addr,SEEK_SET);        fread(&dynamicData,1,8,fp);        printf("\t\t0x%08x\t\tNOPRINTF\t\t0x%x\n",dynamicData.d_tag,dynamicData.d_un);    }    }

    七、.symtab 解析

    该节区是该 so 文件的符号表,它在elf.h文件中的数据结构是Elf32_Sym,如下所示:

    9465e43132a1ede884eeb6a82925e03a.png

    每个字段解释如下:

    1、st_name 字段:该字段是一个索引值,指明了该项的名字。 

    2、st_value 字段:该字段表明了相关联符号的取值。 

    3、stz-size 字段:该字段指明了每个项所占用的字节数。 

    4、st_info 和 st_other 字段:这两个字段指明了符号的类型。 

    5、st_shndx 字段:相关索引。 

    解析代码如下(PS:由于乱码问题,索引手动固定了地址测试,有兴趣的挨个解析字符应该可以解决乱码问题):

    void parseSoDynamicSection(FILE *fp,struct DataOffest off){    int dynamicnum;    Elf32_Off init;    Elf32_Off addr;    Elf32_Dyn dynamicData;    int i;    init = off.dynameicoff;    dynamicnum = (off.dynameicsize / 8);    printf("Dynamic:\n");    printf("\t\tTag\t\t\tType\t\t\tName/Value\n");    for (i = 0; i < dynamicnum; i++)    {        addr = init + (i * 8);        fseek(fp,addr,SEEK_SET);        fread(&dynamicData,1,8,fp);        printf("\t\t0x%08x\t\tNOPRINTF\t\t0x%x\n",dynamicData.d_tag,dynamicData.d_un);    }}
    void parseSymtabSection(FILE *fp,struct DataOffest off){    Elf32_Off init;    Elf32_Off addr;    Elf32_Word count;    Elf32_Sym symtabSection;    int k,i;    init = off.symtaboff;    count = off.symtabsize;    printf("SymTable:\n");    for (i = 0; i < count; i++)    {        addr = init + (i * 16);        fseek(fp,addr,SEEK_SET);        fread(&symtabSection,1,16,fp);        printf("Symbol Name Index: 0x%x\n",symtabSection.st_name);        printf("Value or address associated with the symbol: 0x%08x\n",symtabSection.st_value);        printf("Size of the symbol: 0x%x\n",symtabSection.st_size);        printf("Symbol's type and binding attributes: %c\n",symtabSection.st_info);        printf("Must be zero; reserved: 0x%x\n",symtabSection.st_other);        printf("Which section (header table index) it's defined in: 0x%x\n",symtabSection.st_shndx);    }    }

    八、.text 解析

    PS:这部分没代码了,只简单解析一下,因为解析 arm 指令太麻烦了,估计得写个半年都不一定能搞定,后续写了会同步更新在 github!!!

    .text节区存储着可执行指令,我们可以通过节区头部的名字锁定.text的偏移地址和大小,找到该节区后,我们会发现这个节区存储的就是 arm 机器码,直接照着指令集翻译即可,没有其他的结构。通过 ida 验证如下:

    647de8a652045dc17cf08cc8a669a078.png

    九、代码测试相关截图

    55d0f47c00a86bf72b0c634331a0c96b.png

    fb33e2642107a2ff3adfbbf42aed9bbd.png

    417e193f9dd87c9d82140e99caa78b78.png

    931d38c2e74b714d193f7fece69c951e.png

    a466ebc60abdae680e733a6455de0132.png

    十、frida 反调试和后序

    frida 反调试最简单的就是检查端口,检查进程名,检查 so 文件等,但最准确以及最复杂的是检查汇编指令,我们知道 frida 是通过一个大调整实现 hook,而跳转的指令就那么几条,我们是否可以通过检查每个函数第一条指令来判断是否有 frida 了!!!(ps:简单写一下原理,拉开写就太多了,这里感谢某大佬和我讨论的这个话题!!!)

    本来因为这个 so 文件解析要写到明年去了,没想到看起来代码量大,但实际要用到的地方代码量很少。。。

    源码 github 链接:

    https://github.com/windy-purple/parseso/

    f5d95e9da77f75740651bd63533a858b.png

    展开全文
  • [Java] 纯文本查看 复制代码package com.tudou.soshelltest;import java.io.ByteArrayOutputStream;import java.util.List;import com.tudou.soshell.ELF32_Phdr;import ...import com.tudou.soshe...
  • [Java] 纯文本查看 复制代码package com.tudou.soshelltest;import java.io.ByteArrayOutputStream;import java.util.List;import com.tudou.soshell.ELF32_Phdr;import ...import com.tudou.soshe...
  • Windows+VS2017+dlib+OpenCV编译安卓so文件

    千次阅读 2019-03-25 23:13:18
    程序员的生活在于折腾。。。 博文内容如题,为了方便搜索,我...项目背景:最近在做一个图像质量检测工具,用到dlib和opencv两个开源库,需要打包.so接口给安卓端机使用 opencv+VS2017编译Android项目这里有一个o...
  • ...4.在Debuggger 中的attach 上android 所对应的安卓程序就行了 选择我们所需要的程序进程就可以了 下面来张ida pro 调试的布局。 下面就可以进行对应的后续操作了
  • 由于一个程序运行异常,我在application.mk里面添加了一个编译的参数 -fexceptions,正是由于它,导致在intel cpu的手机上,我们的程序运行就黑屏,没有异常退出。是黑屏没反应,ndk 版本是r9b,不知道是否和ndk版本...
  • SO Helper.,安卓.so文件破解,主要用于查看JNI接口,可以分析到调用的方法。
  • 此工具中readelf.exe,经本人测试,可有效反编译.so文件
  • 没有找到免费的,自己生成分享。别忘记修改SerialPort类中的System.loadLibrary("SerialPort");
  • 安卓开发.SO文件

    2016-02-23 10:18:23
    Android系统目前支持七种不同的CPU架构ARMv5,ARMv7 (从2010年起),x86 (从...应用程序二进制接口(Application Binary Interface)定义了二进制文件(尤其是.so文件)如何运行在相应的系统平台上,从使用的指令集,
  •  最近在学习安卓加固方面的知识,看到了jiangwei212的博客,其中有对so文件加固的两篇文章通过节加密函数和通过hash段找到函数地址直接加密函数,感觉写的特别好,然后自己动手实践探索so加密,这里记录一下学习...
  • 自己编译的SDL2.0安卓使用的so文件,armv7a架构,需要的小伙伴自己下载
  • 在Android Library中引入so文件,然后再主模块引入有so文件的模块时报错: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.xxxxx.xxxxxxx-1/base.apk"],...
  • 导出So文件使用一、首先下载NDK: 下载NDK,此处是我已经下载过后自动配置好路径了,如果没下载过,红色方框会有download ndk ,点击下载即可二、配置NDK 如图在系统环境变量path中添加以下路径: 测试配置:打开...
  • QT Android调用FFMPEG库部署到Android设备。 ubuntu16.04下编译ffmpeg2020版本 qt使用的是5.12 ndk是21r 可以打开mp4,flv,与网络流等
  • 安卓百度地图开发so文件引用失败问题研究
  • 这样看来,在今后相当长的一段时间内,Android逆向中不可避免的会频繁接触到与So加固的对抗了。❖工具的初衷搜集常见So加固方案(主要是日常分析中遇到的)自动化对抗加固方案,解放双手开源工具源码1.把切实可行的...
  • 下面小编就为大家带来一篇完美解决安卓jni项目会删除其他so文件的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 2、android_server推送至安卓模拟器中(前提是安卓模拟器已打开) cmd命令:adb push "D:\liuzhiyong\environment\IDA 7.0\dbgsrv\android_x86_server" /data/local/tmp - 目录1(本地android_x86_server路径):D:...
  • ![图片说明]...百度了一个星期都找不到方法,将近崩溃啦,重新配置环境也配置了好多次,各种ndk版本也换了好多,都不能解决问题,眼睛都废了。所以求各位大神出手相救。。。。
  • 后来查出原因是C++的代码层逻辑问题 把g_pEffectPlay注释打开,就会发生上面的结果。 2种解决方法 1.如果肯定要继承cocos2dx的类,并且是静态的。条件跟我上面写的一样,在构造函数里面加上retain()函数 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 713
精华内容 285
关键字:

安卓so文件