This paper introduces the structure of SO, and records the parsing method and process of SO file, which lays a foundation for the encryption and decryption of SO library.

1. What is so file?

So file is a program function library under Linux, that is, compiled code and data that can be used by other programs, also called dynamic link library.

2. What is the structure of SO?

The file format is called the ELF file format.

The binary structure diagram (Flying Insect God) is as follows:

The header file structure is as follows:

The overall structure is as follows:

Structure field, please refer to the TengQiMing teacher 【 】 the ELF file format analysis: link: pan.baidu.com/s/1GEzePWkK… Extraction code: IBCQ

Structure viewing tool: IDA Pro, which can be searched and downloaded on the Internet.

2.1 the ELF Header

ELF header structure, one at the beginning of the file, sizeof length (ELF32_Ehdr).

struct Elf32_Ehdr {
  unsigned char e_ident[EI_NIDENT]; // File identifies ELF, 32-bit or 64-bit
  Elf32_Half    e_type;      
  Elf32_Half    e_machine;   
  Elf32_Word    e_version;   
  Elf32_Addr    e_entry;     
  Elf32_Off     e_phoff;     // Offset in the file of Program header
  Elf32_Off     e_shoff;     // Section header offset in the file
  Elf32_Word    e_flags;     
  Elf32_Half    e_ehsize;    // The size of the ELF header itself
  Elf32_Half    e_phentsize; // Program header specifies the size of the table
  Elf32_Half    e_phnum;     // Program header specifies the number of tables
  Elf32_Half    e_shentsize; // Section table size
  Elf32_Half    e_shnum;     // Section number of tables
  Elf32_Half    e_shstrndx;  // Section The subscript of the Section of the string table
};

// c++ file stream parsing --fstream ioFile
Elf32_Ehdr* elf32Ehdr;  ELF Header File Header
elf32Ehdr = new Elf32_Ehdr[1];
ioFile.seekg(0, ios::beg);
ioFile.read((char*) elf32Ehdr, sizeof(Elf32_Ehdr));

// Char * fileContent
Elf32_Ehdr* elf32Ehdr = (Elf32_Ehdr*) fileContent;
Copy the code

2.2 the Program Header

The program header structure, immediately after the ELF header, may be multiple. The start position is elf32Ehdr->e_phoff. The number is elf32Ehdr->e_phnum. The length is sizeof(Elf32_Phdr) * elf32Ehdr->e_phnum.

struct Elf32_Phdr {
  Elf32_Word p_type;   / / type
  Elf32_Off  p_offset; // Segment offset in the file
  Elf32_Addr p_vaddr;  // The first byte of the segment is at the start of the virtual address space, in the entire program header
  Elf32_Addr p_paddr;  P_paddr and p_vaddr are normally the same
  Elf32_Word p_filesz; // The length of space that a segment occupies in the ELF file, possibly 0
  Elf32_Word p_memsz;  // The length of space that a segment occupies in the virtual space of a process, which may be 0
  Elf32_Word p_flags;  // Segment permissions, such as readable "R", writable "W", and executable "X"
  Elf32_Word p_align;  // Segment alignment property, the actual alignment byte is equal to 2 ^ p_align
};

// c++ parse the Program Header
Elf32_Phdr* elf32Phdr = new Elf32_Phdr[elf32Ehdr->e_phnum];
ioFile.seekg(elf32Ehdr->e_phoff, ios::beg);
ioFile.read((char*) elf32Phdr, sizeof(Elf32_Phdr) * elf32Ehdr->e_phnum);

// Character array parsing
Elf32_Phdr* elf32Phdr = (Elf32_Phdr*) &fileContent[elf32Ehdr->e_phoff];
Copy the code

Section 2.3 the Header

Section header structure, at the end of the file, may have more than one. The start position is elf32Ehdr->e_shoff. The number is elf32Ehdr->e_shnum. The length is sizeof(Elf32_Shdr) * elf32Ehdr->e_shnum.

struct Elf32_Shdr {
  Elf32_Word sh_name;      // Segment name, string table in.shstrtab. Sh_name is the offset of the segment name
  Elf32_Word sh_type;      // Segment type (SHT_*)
  Elf32_Word sh_flags;     // segment flag (SHF_*)
  Elf32_Addr sh_addr;      // The virtual address of the segment, if the segment can be loaded, otherwise 0
  Elf32_Off  sh_offset;    // Segment offset, if the segment exists in the file, otherwise meaningless
  Elf32_Word sh_size;      // The length of the segment
  Elf32_Word sh_link;      // Section link information
  Elf32_Word sh_info;      // Additional information for the section
  Elf32_Word sh_addralign; // Segment address alignment
  Elf32_Word sh_entsize;   // The length of the item
};

// c++ parses node information
Elf32_Shdr* elf32Shdr = new Elf32_Shdr[elf32Ehdr->e_shnum];
Elf32_Shdr* elf32ShdrPointer = elf32Shdr;
ioFile.seekg(elf32Ehdr->e_shoff, ios::beg);
ioFile.read((char*) elf32ShdrPointer, sizeof(Elf32_Shdr) * elf32Ehdr->e_shnum);

// Character array parsing
Elf32_Shdr* elf32Shdr = (Elf32_Shdr *) &fileContent[elf32Ehdr->e_shoff];
Copy the code

2.4. Shstrtab string table

String tables hold table names, etc., in the format \0abc\0efg\0acxd\0. In c++ strings end in \0. For example, if the string table starts at position 1000 and Section Header name=5, the Section Header name offset is 1000+5 (efG).

Printf ("%s\n", shstrtabSection) will print blank because the initial character is \0.
Elf32_Off shstrtabOffset = elf32Shdr[elf32Ehdr->e_shstrndx].sh_offset;
char* buffer= new char[1];
int strTableSize = 0;
ioFile.seekg(shstrtabOffset, ios::beg);
for (int i = 0; i < elf32Ehdr->e_shnum; ++i) {
    buffer[0] = '\ 0';
    do {
        ioFile.read(buffer, sizeof(char));
        strTableSize++;
    } while (buffer[0] != '\ 0');
}
shstrtabSection = new char[strTableSize];
ioFile.seekg(shstrtabOffset, ios::beg);
ioFile.read(shstrtabSection, sizeof(char) * strTableSize);

// Character array parsing
Elf32_Off shstrtabOffset = elf32Shdr[elf32Ehdr->e_shstrndx].sh_offset;
char* shstrtabSection = &fileContent[shstrtabOffset];
Copy the code

2.5 other Section

The parsing methods are similar

	Elf32_Hash elf32Hash; / /. Hash structure
	Elf32_Dyn* elf32Dyn; //.dynamic Dynamic section

for (int i = 0; i < elf32Ehdr->e_shnum; ++i) {
    Elf32_Off offset = elf32Shdr[i].sh_offset;
    Elf32_Word size = elf32Shdr[i].sh_size;
    if (offset == 0 || size == 0) {
        continue;
    }
    if (elf32Shdr[i].sh_type == SHT_HASH && strcmp(shstrtabSection + elf32Shdr[i].sh_name, ".hash") = =0) {
    	ioFile.seekg(offset, ios::beg);
    	ioFile.read((char*)&elf32Hash.nbucket, sizeof(Elf32_Word));
        ioFile.read((char*)&elf32Hash.nchain, sizeof(Elf32_Word));
        elf32Hash.bucketArr = new Elf32_Word[elf32Hash.nbucket];
        elf32Hash.chainArr = new Elf32_Word[elf32Hash.nchain];
        ioFile.read((char*) elf32Hash.bucketArr, sizeof(Elf32_Word) * elf32Hash.nbucket);
        ioFile.read((char*) elf32Hash.chainArr, sizeof(Elf32_Word) * elf32Hash.nchain); }}for (int i = 0; i < elf32Ehdr->e_shnum; ++i) {
    Elf32_Off offset = elf32Shdr[i].sh_offset;
    Elf32_Word size = elf32Shdr[i].sh_size;
    if (offset == 0 || size == 0) {
        continue;
    }
    if (elf32Shdr[i].sh_type == SHT_DYNAMIC &&
        strcmp(shstrtabSection + elf32Shdr[i].sh_name, ".dynamic") = =0) {
        Elf32_Word count = size / elf32Shdr[i].sh_entsize;
        elf32Dyn = new Elf32_Dyn[count];
        ioFile.seekg(offset, ios::beg);
        ioFile.read((char*) elf32Dyn, size); }}Copy the code

3. View the SO structure

You can run the readelf command to view the information. For details about how to use the command, see: Usage Description of the readelf command. I use the command to read all the information and store it in a file, which is convenient for comparing the parsed data.

// Read the data in XXX. So and save it to the info.txt file in the current directory to compare the correctness of the parse
realelf -a xxx.so > info.txt

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00  // The fifth digit, 1, represents 32 bits, and 2 represents 64 bits.
  Class:                             ELF32  // 32 bit ELF file
  Data:                              2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: DYN (Shared object file) Machine: ARM Version: 0x1 Entry point address: 0x0 Start of program headers: 52 (bytes into file) // Start of section headers: 12920 (bytes into file) // Section headers start offset is 12920 Flags: 0x5000200, Version5 EABI, soft-float ABI Size of this header: 52 (bytes) // ELF header Size of Program headers: 32 (bytes) // Size of program headers Number of program headers: 8 // Number of program headers Size of section headers: 40 (bytes) // Section header size Number of section headers: 27 // Number of section headers section Header String Table index: 26 // The Section headers list is the same as the Section headers list. // Move to Offset, parse FileSiz length data, Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000034 0x00000034 0x00000034 0x00100 0x00100 R 0x4 LOAD 0x000000 0x00000000 0x00000000 0x02de7 0x02de7 R E 0x1000 LOAD 0x002e08 0x00003e08 0x00003e08 0x00204  0x00209 RW 0x1000 DYNAMIC 0x002e58 0x00003e58 0x00003e58 0x00110 0x00110 RW 0x4 NOTE 0x000134 0x00000134 0x00000134 0x000bc 0x000bc R 0x4 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10 EXIDX 0x002630 0x00002630 0x00002630 0x001a0 0x001a0 R 0x4 GNU_RELRO 0x002e08 0x00003e08 0x00003e08 0x001f8 0x001f8 RW 0x4 Section Headers: // Move to Off, parse the Size of the data, Section Header [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [0] NULL 00000000 000000 00000000 000 .note.android.ide NOTE 00000134 000134 000098 00 A 0 0 4 [ 2] .note.gnu.build-i NOTE 000001cc 0001cc 000024 00 A 0 0 4 [  3] .dynsym DYNSYM 000001f0 0001f0 000350 10 A 4 1 4 [ 4] .dynstr STRTAB 00000540 000540 000373 00 A 0 0 1 [ 5] .gnu.hash GNU_HASH 000008b4 0008b4 00015c 04 A 3 0 4 [ 6] .hash HASH 00000a10 000a10 000170 04 A 3 0 4 [ 7] .gnu.version  VERSYM 00000b80 000b80 00006a 02 A 3 0 2 [ 8] .gnu.version_d VERDEF 00000bec 000bec 00001c 00 A 4 1 4 [ 9] .gnu.version_r VERNEED 00000c08 000c08 000040 00 A 4 2 4 [10] .rel.dyn REL 00000c48 000c48 0000c8 08 A 3 0 4 [11] .rel.plt REL 00000d10 000d10 0000f0 08 AI 3 20 4 [12] .plt PROGBITS 00000e00 000e00 00017c 00 AX 0 0 4 [13] .text PROGBITS 00000f7c 000f7c 0016b4 00 AX 0 0 4 [14] .ARM.exidx ARM_EXIDX 00002630 002630 0001a0 08 AL 13 0 4 [15] .ARM.extab PROGBITS 000027d0 0027d0 000180 00 A 0 0 4 [16] .rodata PROGBITS 00002950 002950 000497 01 AMS 0 0 1 [17] .fini_array FINI_ARRAY 00003e08 002e08 000008 04 WA 0 0 4 [18] .data.rel.ro PROGBITS 00003e10 002e10 000048 00 WA 0 0 4 [19] .dynamic DYNAMIC 00003e58 002e58 000110 08 WA 4 0 4 [20] .got PROGBITS 00003f68 002f68 000098 00 WA 0 0 4 [21] .data PROGBITS 00004000 003000 00000c 00 WA 0 0 4 [22] .bss NOBITS 0000400c 00300c 000005 00 WA 0 0 4 [23] .comment PROGBITS 00000000 00300c 000109 01 MS 0 0 1 [24] .note.gnu.gold-ve NOTE 00000000 003118 00001c 00 0 0 4 [25] .ARM.attributes ARM_ATTRIBUTES 00000000 003134 000034 00 0 0 1 [26] .shstrtab STRTAB 00000000 003168 00010f 00 0 0 1Copy the code

4. C + + parsing the ELF

4.1 Parsing Methods

There are two kinds of parsing

  1. File streams don’t have to be loaded into memory and can be modified at any time, but parsing is a bit more cumbersome.
  2. After the string is loaded into memory, it only needs to use a pointer to point to the offset position, which is easy to parse.

So encryption will be done later, so the form of file stream read can be modified to achieve encryption. After loading the so library, decrypt it by parsing the string.

4.2 Parsing Logic

4.2.1 Preparing Resources

Elf. H * * * * * * * * * * * * * * * * AndroidSdk\ndk-bundle\sysroot\usr\include\ Linux \elf.h, copy this header file to the project. Elf32_Half is undefined. You can copy this into elf.h.

GCC encryption logic using C or C ++ code, and then compiled with GCC, this Linux support, Windows need to install software, you can baidu.

Hello World! Try compiling a simple program first.

// linux
g++ xxx.cpp
./a.out

// windows
g++ xxx.cpp
a.exe
Copy the code

4.2.2 code

Create elfParser. CPP file (32 bit parser. 64 bit parser. elfparser. CPP)

#include <iostream>
#include "ElfParser.h" // Let's leave it at that

using namespace std;

void parseSoByPath(const string& soPath) {
    printf("parseSoByPath soPath=%s\n", soPath.c_str());

    fstream ioFile;
    ioFile.open(soPath.c_str(), ios::in | ios::out); // Open the file in read-write mode
    if(! ioFile) {printf("open %s failed! \n", soPath.c_str());
        return;
    }
    char* soPathArr = new char[strlen(soPath.c_str());strcpy(soPathArr, soPath.c_str());
    if (isElf64(ioFile)) { // Check whether it is 32-bit or 64-bit
        ELF64Struct elf64Struct;
        elf64Struct.fileName = soPathArr;
        elf64Struct.parseSo(ioFile);
        elf64Struct.encryptSo(ioFile);
// elf64Struct.decryptSo(ioFile);
    } else {
        ELF32Struct elf32Struct;
        elf32Struct.fileName = soPathArr;
        elf32Struct.parseSo(ioFile);
        elf32Struct.encryptSo(ioFile);
// elf32Struct.decryptSo(ioFile);
    }
    ioFile.close(a);/*fstream ioFile; ioFile.open(soPath.c_str(), ios::in | ios::out); if (! ioFile) { printf("open %s failed! \n", soPath.c_str()); return; } ioFile.seekg(0, ios::end); long long int fileSize = ioFile.tellg(); char* fileContent = new char[fileSize]; ioFile.seekg(0, ios::beg); ioFile.read(fileContent, fileSize); ioFile.close(); char* soPathArr = new char[strlen(soPath.c_str())]; strcpy(soPathArr, soPath.c_str()); if (isElf64(fileContent)) { ELF64Struct elf64Struct; elf64Struct.fileName = soPathArr; elf64Struct.parseSo(fileContent); elf64Struct.encryptSo(fileContent); elf64Struct.decryptSo(fileContent); } else { ELF32Struct elf32Struct; elf32Struct.fileName = soPathArr; elf32Struct.parseSo(fileContent); // elf32Struct.encryptSo(fileContent); elf32Struct.decryptSo(fileContent); } ioFile.close(); * /
}

int main(a) {

	// Specify a path
    string filePath[] =  {
            ".. /resources/arm64-v8a/libDataEncryptionLib.so".".. /resources/armeabi-v7a/libDataEncryptionLib.so".".. /resources/x86/libDataEncryptionLib.so".".. /resources/x86_64/libDataEncryptionLib.so"
    };
    for (int i = 0; i < sizeof(filePath) / sizeof(filePath[0]); ++i) {
        parseSoByPath(filePath[i]);
    }
    return 0;
}

Copy the code

Elfparser. h file (do not copy, please refer to it)


#include <string>
#include <fstream>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>

#ifndef ANDROID_PROGRAM
#define ANDROID_PROGRAM

#define PAGE_SIZE 4096
#include "include/c_log.h"
#include "include/elf.h"

#else

#include "include/android_log.h"
#include <elf.h>

#endif

/** * Check if it is a 64-bit ELF file * @param ioFile * @return */
int isElf64(fstream& ioFile) {
    // Read ELF header information to determine 32-bit or 64-bit
    Elf64_Ehdr elf64Ehdr;
    ioFile.seekg(0, ios::beg);
    ioFile.read((char*)&elf64Ehdr, sizeof(Elf64_Ehdr));
    if (elf64Ehdr.e_ident[EI_CLASS] == 2) {
        return 1;
    }
    return 0;
}

/** * Check if it is a 64-bit ELF file * @param ioFile * @return */
int isElf64(char* fileContent) {
    // Read ELF header information to determine 32-bit or 64-bit
    Elf64_Ehdr* elf64Ehdr = (Elf64_Ehdr*) fileContent;
    if (elf64Ehdr->e_ident[EI_CLASS] == 2) {
        return 1;
    }
    return 0;
}

class ELF32Struct {
private:

public:
    char* fileName;
    void parseSo(fstream& ioFile);
    void encryptSo(fstream& ioFile);
    void decryptSo(fstream& ioFile);
    void encryptOrDecryptSo(fstream &ioFile, int isDecrypt);

    void parseSo(char* fileContent);
    void encryptSo(char* fileContent);
    void decryptSo(char* fileContent);
    void encryptOrDecryptSo(char* fileContent, int isDecrypt);

    void decryptSo(unsigned long long base);
};

class ELF64Struct {
private:

public:
    char* fileName;
    void parseSo(fstream& ioFile);
    void encryptSo(fstream& ioFile);
    void decryptSo(fstream& ioFile);
    void encryptOrDecryptSo(fstream &ioFile, int isDecrypt);

    void parseSo(char* fileContent);
    void encryptSo(char* fileContent);
    void decryptSo(char* fileContent);
    void encryptOrDecryptSo(char* fileContent, int isDecrypt);

    void decryptSo(unsigned long long base);
};

void ELF32Struct::parseSo(fstream &ioFile) {
    LOGD("\n--ELF32Struct start-------------------------------------------\n");
    LOGD("filePath=%s\n", fileName);

    Elf32_Ehdr* elf32Ehdr;  ELF Header File Header
    Elf32_Shdr* elf32Shdr; // Section Header list Section Header list
    char* shstrtabSection;   //.shstrtab string table
    char* dynstrSection; //.dynstr symbol string list
    Elf32_Sym* elf32Sym; //.dynsym symbol table
    Elf32_Word elf32SymSize;
    Elf32_Rel* elf32Rel; //.rel. Dyn relocation
    Elf32_Hash elf32Hash; / /. Hash structure
    Elf32_Dyn* elf32Dyn; //.dynamic Dynamic section
    Elf32_Phdr* elf32Phdr;  // Segment/Program Header list

    // Read the ELF header to determine whether it is 32-bit or 64-bit
    // Read elf headers
    elf32Ehdr = new Elf32_Ehdr[1];
    ioFile.seekg(0, ios::beg);
    ioFile.read((char*) elf32Ehdr, sizeof(Elf32_Ehdr));

    if (printELFHeader == 1) {
        // Enter ELF header information
        LOGD("\nELF Header:\n");
        LOGD(" %s "."Magic:"); // Output magic
        for (int i = 0; i < sizeof(elf32Ehdr->e_ident) / sizeof(elf32Ehdr->e_ident[0]); ++i) {
            LOGD("% 2.2 x", elf32Ehdr->e_ident[i]);
        }
        LOGD("\n");
        LOGD(" %-38s%s\n"."Type:".tranElfHeaderType(elf32Ehdr->e_type).c_str());
        LOGD(" %-38s%d\n"."Machine:", elf32Ehdr->e_machine);
        LOGD(" %-38s%d\n"."Entry point address:", elf32Ehdr->e_entry);
        LOGD(" %-38s%d\n"."Start of program headers:", elf32Ehdr->e_phoff);
        LOGD(" %-38s%d\n"."Start of section headers:", elf32Ehdr->e_shoff);
        LOGD(" %-38s%d\n"."Size of this header:", elf32Ehdr->e_ehsize);
        LOGD(" %-38s%d\n"."Size of program headers:", elf32Ehdr->e_phentsize);
        LOGD(" %-38s%d\n"."Number of program headers:", elf32Ehdr->e_phnum);
        LOGD(" %-38s%d\n"."Size of section headers:", elf32Ehdr->e_shentsize);
        LOGD(" %-38s%d\n"."Number of section headers:", elf32Ehdr->e_shnum);
        LOGD(" %-38s%d\n"."Section header string table index:", elf32Ehdr->e_shstrndx);
    }


    // Parse the Program Header
    elf32Phdr = new Elf32_Phdr [elf32Ehdr->e_phnum];
    ioFile.seekg(elf32Ehdr->e_phoff, ios::beg);
    ioFile.read((char*) elf32Phdr, sizeof(Elf32_Phdr) * elf32Ehdr->e_phnum);

    if (printProgramHeader == 1) {
        LOGD("\n\nProgram Headers:\n");
        LOGD(" %-14s %-8s %-10s %-10s %-7s %-7s %3s %6s\n"."Type"."Offset"."VirtAddr"."PhysAddr"."FileSiz"."MemSiz"."Flg"."Align");
        for (int i = 0; i < elf32Ehdr->e_phnum; ++i) {
            LOGD("% - 14 s 0 x % % 0 x to 6.6 x to 8.8 x 8.8 x 0 0 x % % % 0 x to 5.5 x to 5.5 x x - 3-6 x x % % \ n".tranProgramHeaderType(elf32Phdr[i].p_type).c_str(), elf32Phdr[i].p_offset, elf32Phdr[i].p_vaddr, elf32Phdr[i].p_paddr, elf32Phdr[i].p_filesz, elf32Phdr[i].p_memsz, elf32Phdr[i].p_flags, elf32Phdr[i].p_align ); }}// Parse Section, node information
    elf32Shdr = new Elf32_Shdr[elf32Ehdr->e_shnum];
    Elf32_Shdr* elf32ShdrPointer = elf32Shdr;
    ioFile.seekg(elf32Ehdr->e_shoff, ios::beg);
    ioFile.read((char*) elf32ShdrPointer, sizeof(Elf32_Shdr) * elf32Ehdr->e_shnum);

    // String Table offset
    Elf32_Off shstrtabOffset = elf32Shdr[elf32Ehdr->e_shstrndx].sh_offset;
    char* buffer= new char[1];
    int strTableSize = 0;
    ioFile.seekg(shstrtabOffset, ios::beg);
    for (int i = 0; i < elf32Ehdr->e_shnum; ++i) {
        buffer[0] = '\ 0';
        do {
            ioFile.read(buffer, sizeof(char));
            strTableSize++;
        } while (buffer[0] != '\ 0');
    }
    shstrtabSection = new char[strTableSize];
    ioFile.seekg(shstrtabOffset, ios::beg);
    ioFile.read(shstrtabSection, sizeof(char) * strTableSize);

    if (printSectionHeader == 1) {
        LOGD("\n\nSection Headers:\n");
        // Outputs Section header information
        LOGD(" %s%-26s%-16s%-9s%-7s%-7s%-3s%-4s%-4s%-4s%-3s\n"."[Nr]"."Name"."Type"."Addr"."Off"."Size"."ES"."Flg"."lk"."Inf"."Al");
        for (int i = 0; i < elf32Ehdr->e_shnum; ++i) {
            LOGD("[% 2 d] % % % 25 s to 15 s 8.8 x % 6.6 x to 6.6 x to 2.2 x % % % 3 s % 2 x % 4 x % 2 x \ n",
                    i,
                    shstrtabSection + elf32Shdr[i].sh_name,
                    tranSectionHeaderType(elf32Shdr[i].sh_type).c_str(),
                    elf32Shdr[i].sh_addr,
                    elf32Shdr[i].sh_offset,
                    elf32Shdr[i].sh_size,
                    elf32Shdr[i].sh_entsize,
                    tranSectionHeaderFlagType(elf32Shdr[i].sh_flags).c_str(), elf32Shdr[i].sh_link, elf32Shdr[i].sh_info, elf32Shdr[i].sh_addralign ); }}for (int i = 0; i < elf32Ehdr->e_shnum; ++i) {
        Elf32_Off offset = elf32Shdr[i].sh_offset;
        Elf32_Word size = elf32Shdr[i].sh_size;
        if (offset == 0 || size == 0) {
            continue;
        }
        // Read section.dynstr dynamically linked string
        if (elf32Shdr[i].sh_type == SHT_STRTAB &&
            strcmp(shstrtabSection + elf32Shdr[i].sh_name, ".dynstr") = =0) {
            dynstrSection = new char[size];
            ioFile.seekg(offset, ios::beg);
            ioFile.read(dynstrSection, sizeof(char) * size);
            if (printDynstr == 1) {
                LOGD("\n\n%s:\n", shstrtabSection + elf32Shdr[i].sh_name);
                for (int j = 0; j < size; ++j) {
                    if (j == 0) {
                        LOGD("%s\n", dynstrSection);
                    }
                    if (dynstrSection[j] == 0&& j ! = size -1) {
                        LOGD("%s\n", dynstrSection + (j + 1)); }}}break; }}for (int i = 0; i < elf32Ehdr->e_shnum; ++i) {
        Elf32_Off offset = elf32Shdr[i].sh_offset;
        Elf32_Word size = elf32Shdr[i].sh_size;
        if (offset == 0 || size == 0) {
            continue;
        }
        if (elf32Shdr[i].sh_type == SHT_DYNSYM &&
            strcmp(shstrtabSection + elf32Shdr[i].sh_name, ".dynsym") = =0) {
            elf32SymSize = size / elf32Shdr[i].sh_entsize;
            elf32Sym = new Elf32_Sym[elf32SymSize];
            ioFile.seekg(offset, ios::beg);
            ioFile.read((char *) elf32Sym, sizeof(Elf32_Sym) * elf32SymSize);
            if (printDynsym == 1) {
                LOGD("\n\n%s:\n", shstrtabSection + elf32Shdr[i].sh_name);
                LOGD(" %-4s %-8s %-5s %7s %6s %8s %-4s %s\n"."Num:"."Value"."Size"."Type"."Bind"."Vis"."Ndx"."Name");
                for (int j = 0; j < elf32SymSize; ++j) {
                    LOGD(" %-3d: ", j);
                    LOGD("%-8.8x ", elf32Sym[j].st_value);
                    LOGD("%-5d ", elf32Sym[j].st_size);
                    LOGD("%7s "."");
                    LOGD("%6s "."");
                    LOGD("%8s "."");
                    LOGD("%-4x ", elf32Sym[j].st_shndx);
                    LOGD("%s", dynstrSection + elf32Sym[j].st_name);
                    LOGD("\n"); }}break; }}for (int i = 0; i < elf32Ehdr->e_shnum; ++i) {
        Elf32_Off offset = elf32Shdr[i].sh_offset;
        Elf32_Word size = elf32Shdr[i].sh_size;
        if (offset == 0 || size == 0) {
            continue;
        }
        if (elf32Shdr[i].sh_type == SHT_HASH && strcmp(shstrtabSection + elf32Shdr[i].sh_name, ".hash") = =0) {
            ioFile.seekg(offset, ios::beg);
            ioFile.read((char*)&elf32Hash.nbucket, sizeof(Elf32_Word));
            ioFile.read((char*)&elf32Hash.nchain, sizeof(Elf32_Word));
            elf32Hash.bucketArr = new Elf32_Word[elf32Hash.nbucket];
            elf32Hash.chainArr = new Elf32_Word[elf32Hash.nchain];
            ioFile.read((char*) elf32Hash.bucketArr, sizeof(Elf32_Word) * elf32Hash.nbucket);
            ioFile.read((char*) elf32Hash.chainArr, sizeof(Elf32_Word) * elf32Hash.nchain);
            if (printHash == 1) {
                LOGD("\n\n%s:\n", shstrtabSection + elf32Shdr[i].sh_name);
                LOGD(" nbucket=%d:\n", elf32Hash.nbucket);
                LOGD("[% 2 s] % s \ n" 8.8."Nr"."value");
                for (Elf32_Word j = 0; j < elf32Hash.nbucket; ++j) {
                    LOGD(" [%2d] ", j);
                    LOGD("% 8.8 d", elf32Hash.bucketArr[j]);
                    LOGD("\n");
                }
                LOGD(" nchain=%d:\n", elf32Hash.nchain);
                LOGD("[% 2 s] % s \ n" 8.8."Nr"."value");
                for (Elf32_Word j = 0; j < elf32Hash.nchain; ++j) {
                    LOGD(" [%2d] ", i);
                    LOGD("% 8.8 d", elf32Hash.chainArr[j]);
                    LOGD("\n"); }}break; }}for (int i = 0; i < elf32Ehdr->e_shnum; ++i) {
        Elf32_Off offset = elf32Shdr[i].sh_offset;
        Elf32_Word size = elf32Shdr[i].sh_size;
        if (offset == 0 || size == 0) {
            continue;
        }
        if (elf32Shdr[i].sh_type == SHT_REL &&
            strcmp(shstrtabSection + elf32Shdr[i].sh_name, ".rel.dyn") = =0) {
            int count = size / elf32Shdr[i].sh_entsize;
            elf32Rel = new Elf32_Rel[count];
            ioFile.seekg(offset, ios::beg);
            ioFile.read((char *) elf32Rel, sizeof(Elf32_Rel) * count);
            if (printRelDyn == 1) {
                LOGD("\n\n%s:\n", shstrtabSection + elf32Shdr[i].sh_name);
                LOGD("%8s %8s %14s %10s %s\n"."Offset"."Info"."Type"."Sym.Value"."Sym.Name");
                for (int j = 0; j < count; ++j) {
                    LOGD("%-8.8x: ", elf32Rel[j].r_offset);
                    LOGD("%-8.8x ", elf32Rel[j].r_info);
                    LOGD("%14s "."");
                    LOGD("%10s "."");
                    LOGD("%s"."");
                    LOGD("\n"); }}}}for (int i = 0; i < elf32Ehdr->e_shnum; ++i) {
        Elf32_Off offset = elf32Shdr[i].sh_offset;
        Elf32_Word size = elf32Shdr[i].sh_size;
        if (offset == 0 || size == 0) {
            continue;
        }
        if (elf32Shdr[i].sh_type == SHT_DYNAMIC &&
            strcmp(shstrtabSection + elf32Shdr[i].sh_name, ".dynamic") = =0) {
            Elf32_Word count = size / elf32Shdr[i].sh_entsize;
            elf32Dyn = new Elf32_Dyn[count];
            ioFile.seekg(offset, ios::beg);
            ioFile.read((char *) elf32Dyn, size);
            if (printDynamic == 1) {
                LOGD("\n\n%s:\n", shstrtabSection + elf32Shdr[i].sh_name);
                LOGD(" %-10s %-28s %s\n"."Tag"."Type"."Name/Value");
                for (int j = 0; j < count; ++j) {
                    LOGD("0 x % 8.8 x", elf32Dyn[j].d_tag);
                    LOGD("%-28s "."");
                    LOGD("%s"."");
                    LOGD("\n"); }}}}LOGD("\n");
    LOGD("\n--ELF32Struct end---------------------------------------------\n");
}

void ELF32Struct::parseSo(char* fileContent) {
    LOGD("\n--ELF32Struct start-------------------------------------------\n");
    LOGD("filePath=%s\n", fileName);

    // Read the ELF header to determine whether it is 32-bit or 64-bit
    // Read elf headers
    Elf32_Ehdr* elf32Ehdr = (Elf32_Ehdr*) fileContent;

    if (printELFHeader == 1) {
        // Enter ELF header information
        LOGD("\nELF Header:\n");
        LOGD(" %s "."Magic:"); // Output magic
        for (int i = 0; i < sizeof(elf32Ehdr->e_ident) / sizeof(elf32Ehdr->e_ident[0]); ++i) {
            LOGD("% 2.2 x", elf32Ehdr->e_ident[i]);
        }
        LOGD("\n");
        LOGD(" %-38s%s\n"."Type:".tranElfHeaderType(elf32Ehdr->e_type).c_str());
        LOGD(" %-38s%d\n"."Machine:", elf32Ehdr->e_machine);
        LOGD(" %-38s%d\n"."Entry point address:", elf32Ehdr->e_entry);
        LOGD(" %-38s%d\n"."Start of program headers:", elf32Ehdr->e_phoff);
        LOGD(" %-38s%d\n"."Start of section headers:", elf32Ehdr->e_shoff);
        LOGD(" %-38s%d\n"."Size of this header:", elf32Ehdr->e_ehsize);
        LOGD(" %-38s%d\n"."Size of program headers:", elf32Ehdr->e_phentsize);
        LOGD(" %-38s%d\n"."Number of program headers:", elf32Ehdr->e_phnum);
        LOGD(" %-38s%d\n"."Size of section headers:", elf32Ehdr->e_shentsize);
        LOGD(" %-38s%d\n"."Number of section headers:", elf32Ehdr->e_shnum);
        LOGD(" %-38s%d\n"."Section header string table index:", elf32Ehdr->e_shstrndx);
    }

    // Parse the Program Header
    Elf32_Phdr* elf32Phdr = (Elf32_Phdr*) &fileContent[elf32Ehdr->e_phoff];
    if (printProgramHeader == 1) {
        LOGD("\n\nProgram Headers:\n");
        LOGD(" %-14s %-8s %-10s %-10s %-7s %-7s %3s %6s\n"."Type"."Offset"."VirtAddr"."PhysAddr"."FileSiz"."MemSiz"."Flg"."Align");
        for (int i = 0; i < elf32Ehdr->e_phnum; ++i) {
            LOGD("");
            LOGD("%-14x ", elf32Phdr[i].p_type/*tranProgramHeaderType(elf32Phdr[i].p_type).c_str()*/);
            LOGD("0 x % 6.6 x", elf32Phdr[i].p_offset);
            LOGD("0 x % 8.8 x", elf32Phdr[i].p_vaddr);
            LOGD("0 x % 8.8 x", elf32Phdr[i].p_paddr);
            LOGD("0 x % 5.5 x", elf32Phdr[i].p_filesz);
            LOGD("0 x % 5.5 x", elf32Phdr[i].p_memsz);
            LOGD("%-3x ", elf32Phdr[i].p_flags);
            LOGD("%-6x", elf32Phdr[i].p_align);
            LOGD("\n"); }}// Parse Section, node information
    Elf32_Shdr* elf32Shdr = (Elf32_Shdr *) &fileContent[elf32Ehdr->e_shoff];

    // String Table offset
    Elf32_Off shstrtabOffset = elf32Shdr[elf32Ehdr->e_shstrndx].sh_offset;
    char* shstrtabSection = &fileContent[shstrtabOffset];

    if (printSectionHeader == 1) {
        LOGD("\n\nSection Headers:\n");
        // Outputs Section header information
        LOGD(" %s%-26s%-16s%-9s%-7s%-7s%-3s%-4s%-4s%-4s%-3s\n"."[Nr]"."Name"."Type"."Addr"."Off"."Size"."ES"."Flg"."lk"."Inf"."Al");
        for (int i = 0; i < elf32Ehdr->e_shnum; ++i) {
            LOGD(" [%2d]", i);
            LOGD("%-25x ", elf32Shdr[i].sh_name);
            LOGD("%-15s ".tranSectionHeaderType(elf32Shdr[i].sh_type).c_str());
            LOGD("%-8.8x ", elf32Shdr[i].sh_addr);
            LOGD("%-6.6x ", elf32Shdr[i].sh_offset);
            LOGD("%-6.6x ", elf32Shdr[i].sh_size);
            LOGD("%-2.2x ", elf32Shdr[i].sh_entsize);
            LOGD("%3s ".tranSectionHeaderFlagType(elf32Shdr[i].sh_flags).c_str());
            LOGD("%2x ", elf32Shdr[i].sh_link);
            LOGD("%4x ", elf32Shdr[i].sh_info);
            LOGD("%2x ", elf32Shdr[i].sh_addralign);
            LOGD("\n"); }}char* dynstrSection;
    for (int i = 0; i < elf32Ehdr->e_shnum; ++i) {
        Elf32_Off offset = elf32Shdr[i].sh_offset;
        Elf32_Word size = elf32Shdr[i].sh_size;
        if (offset == 0 || size == 0) {
            continue;
        }
        // Read section.dynstr dynamically linked string
        if (elf32Shdr[i].sh_type == SHT_STRTAB &&
            strcmp(shstrtabSection + elf32Shdr[i].sh_name, ".dynstr") = =0) {
            dynstrSection = &fileContent[offset];
            if (printDynstr == 1) {
                LOGD("\n\n%s:\n", shstrtabSection + elf32Shdr[i].sh_name);
                for (int j = 0; j < size; ++j) {
                    if (j == 0) {
                        LOGD("%s\n", dynstrSection);
                    }
                    if (dynstrSection[j] == 0&& j ! = size -1) {
                        LOGD("%s\n", dynstrSection + (j + 1)); }}}break; }}for (int i = 0; i < elf32Ehdr->e_shnum; ++i) {
        Elf32_Off offset = elf32Shdr[i].sh_offset;
        Elf32_Word size = elf32Shdr[i].sh_size;
        if (offset == 0 || size == 0) {
            continue;
        }
        if (elf32Shdr[i].sh_type == SHT_DYNSYM &&
            strcmp(shstrtabSection + elf32Shdr[i].sh_name, ".dynsym") = =0) {
            Elf32_Word elf32SymSize = size / elf32Shdr[i].sh_entsize;
            Elf32_Sym* elf32Sym = (Elf32_Sym*) &fileContent[offset];
            if (printDynsym == 1) {
                LOGD("\n\n%s:\n", shstrtabSection + elf32Shdr[i].sh_name);
                LOGD(" %-4s %-8s %-5s %7s %6s %8s %-4s %s\n"."Num:"."Value"."Size"."Type"."Bind"."Vis"."Ndx"."Name");
                for (int j = 0; j < elf32SymSize; ++j) {
                    LOGD(" %-3d: ", j);
                    LOGD("%-8.8x ", elf32Sym[j].st_value);
                    LOGD("%-5d ", elf32Sym[j].st_size);
                    LOGD("%7s "."");
                    LOGD("%6s "."");
                    LOGD("%8s "."");
                    LOGD("%-4x ", elf32Sym[j].st_shndx);
                    LOGD("%s", dynstrSection + elf32Sym[j].st_name);
                    LOGD("\n"); }}break; }}for (int i = 0; i < elf32Ehdr->e_shnum; ++i) {
        Elf32_Off offset = elf32Shdr[i].sh_offset;
        Elf32_Word size = elf32Shdr[i].sh_size;
        if (offset == 0 || size == 0) {
            continue;
        }
        if (elf32Shdr[i].sh_type == SHT_HASH && strcmp(shstrtabSection + elf32Shdr[i].sh_name, ".hash") = =0) {
            Elf32_Hash elf32Hash;
            elf32Hash.nbucket = *(Elf32_Word*) &fileContent[offset];
            elf32Hash.nchain = *(Elf32_Word*) &fileContent[offset + sizeof(Elf32_Word)];
            LOGD(" nbucket=%d:\n", elf32Hash.nbucket);
            LOGD(" nchain=%d:\n", elf32Hash.nchain);
            elf32Hash.bucketArr = (Elf32_Word *) &fileContent[offset + sizeof(Elf32_Word) * 2];
            elf32Hash.chainArr = (Elf32_Word *) &fileContent[offset + sizeof(Elf32_Word) * (2 + elf32Hash.nbucket)];
            if (printHash == 1) {
                LOGD("\n\n%s:\n", shstrtabSection + elf32Shdr[i].sh_name);
                LOGD(" nbucket=%d:\n", elf32Hash.nbucket);
                LOGD("[% 2 s] % s \ n" 8.8."Nr"."value");
                for (Elf32_Word j = 0; j < elf32Hash.nbucket; ++j) {
                    LOGD(" [%2d] ", j);
                    LOGD("% 8.8 d", elf32Hash.bucketArr[j]);
                    LOGD("\n");
                }
                LOGD(" nchain=%d:\n", elf32Hash.nchain);
                LOGD("[% 2 s] % s \ n" 8.8."Nr"."value");
                for (Elf32_Word j = 0; j < elf32Hash.nchain; ++j) {
                    LOGD(" [%2d] ", j);
                    LOGD("% 8.8 d", elf32Hash.chainArr[j]);
                    LOGD("\n"); }}break; }}for (int i = 0; i < elf32Ehdr->e_shnum; ++i) {
        Elf32_Off offset = elf32Shdr[i].sh_offset;
        Elf32_Word size = elf32Shdr[i].sh_size;
        if (offset == 0 || size == 0) {
            continue;
        }
        if (elf32Shdr[i].sh_type == SHT_REL &&
            strcmp(shstrtabSection + elf32Shdr[i].sh_name, ".rel.dyn") = =0) {
            int count = size / elf32Shdr[i].sh_entsize;
            Elf32_Rel* elf32Rel = (Elf32_Rel*) &fileContent[offset];
            if (printRelDyn == 1) {
                LOGD("\n\n%s:\n", shstrtabSection + elf32Shdr[i].sh_name);
                LOGD("%8s %8s %14s %10s %s\n"."Offset"."Info"."Type"."Sym.Value"."Sym.Name");
                for (int j = 0; j < count; ++j) {
                    LOGD("%-8.8x: ", elf32Rel[j].r_offset);
                    LOGD("%-8.8x ", elf32Rel[j].r_info);
                    LOGD("%14s "."");
                    LOGD("%10s "."");
                    LOGD("%s"."");
                    LOGD("\n"); }}break; }}for (int i = 0; i < elf32Ehdr->e_shnum; ++i) {
        Elf32_Off offset = elf32Shdr[i].sh_offset;
        Elf32_Word size = elf32Shdr[i].sh_size;
        if (offset == 0 || size == 0) {
            continue;
        }
        if (elf32Shdr[i].sh_type == SHT_DYNAMIC &&
            strcmp(shstrtabSection + elf32Shdr[i].sh_name, ".dynamic") = =0) {
            Elf32_Word count = size / elf32Shdr[i].sh_entsize;
            Elf32_Dyn* elf32Dyn = (Elf32_Dyn*) &fileContent[offset];
            if (printDynamic == 1) {
                LOGD("\n\n%s:\n", shstrtabSection + elf32Shdr[i].sh_name);
                LOGD(" %-10s %-28s %s\n"."Tag"."Type"."Name/Value");
                for (int j = 0; j < count; ++j) {
                    LOGD("0 x % 8.8 x", elf32Dyn[j].d_tag);
                    LOGD("%-28s "."");
                    LOGD("%s"."");
                    LOGD("\n"); }}}}LOGD("\n");
    LOGD("\n--ELF32Struct end---------------------------------------------\n");
}
Copy the code

Elfparse. h is a header file that I share with JNI and run locally. I want to use printf to output logs when compiling locally GCC and Log when calling JNI app. APP calls are introduced android_log.h(see previous article). C_log. h — encapsulates printf and ADAPTS LOG

#ifndef SOLIBENCRYPTION_C_LOG_H
#define SOLIBENCRYPTION_C_LOG_H

#include <stdarg.h>
void LOG(const char* format, ...) {
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
}

void LOGV(const char* format, ...) {
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
}
void LOGD(const char* format, ...) {
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
}
void LOGI(const char* format, ...) {
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
}
void LOGW(const char* format, ...) {
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
}
void LOGE(const char* format, ...) {
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
}
void LOGF(const char* format, ...) {
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
}

#endif //SOLIBENCRYPTION_C_LOG_H

Copy the code

In ElfParser. H

#ifndef ANDROID_PROGRAM
#define ANDROID_PROGRAM

#define PAGE_SIZE 4096
#include "include/c_log.h"
#include "include/elf.h"

#else

#include "include/android_log.h"
#include <elf.h>

#endif
Copy the code

ANDROID_PROGRAM is a variable that I define in the JNI compilation sodecrypt.cpp, and then I load elfparse. h to see that this variable is defined, which means that it is the JNI environment, so I load android_log.h. The elfparse. CPP I compiled locally did not define this variable, so I loaded c_log.h.

5. Summary

  1. You need to prepare the NDK development environment, please refer to my first two articles, BASIC C/C ++ syntax.
  2. For an overview of the ELF structure, see ELF File Format Analysis and compare the structure diagram to understand the structure fields in detail.
  3. Use the command readelf to save it to a file that can then be parsed to see if the reference resolution is correct.
  4. ELF Header, Program Headers, Section Headers, etc.

This article is for reference only, copy and paste code easy to forget, and understanding is not deep, only encountered problems to solve to deepen understanding.

6. References

Android SO(ELF) file parsing Android SO(ELF) file parsing