加密so文件中指定的函数

2023-11-14

加密so文件中指定的函数

作者: 0n1y3nd丶  分类: 逆向学习  发布时间: 2014-09-04 22:24  ė  61条评论

前言

         上一篇文章中详细分析了对so文件中自定义section的加密,这一篇来分析下对so文件中自定义函数的加密
          原文地址:http://bbs.pediy.com/showthread.php?t=191649

0×1

        需要加密的函数是 : Java_com_example_shelldemo2_MainActivity_getString

0×2

加密

a、整个加密过程中最重要的便是在文件中找到指定函数的位置,然后去做加密处理,最后将加密过后的数据写入原位置

b、从一个ELF动态链接库文件中,根据已知的函数名称,找到相应的函数起始地址的过程如下:

从ELF的header找到文件的偏移 ehdr->e_phoff。找到d_tag为 PT_DYNAMICpargram header
一个phdr对应一个 Segment。包含了好几个节
从这个Segment开始位置找到名为: DT_DYNAMIC 的节,然后从这个节中找到 Elf32_Sym 这个一个结构
根据结构的成员变量 st_name,来和函数名称比较,相符,就用 st_valuest_size两个变量找到函数的数据
c、结合代码详细分析此流程
①、main函数:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
int main ( int argc , char * * argv ) {
     char secName [ ] = ".text" ;
     char funcName [ ] = "Java_com_example_shelldemo2_MainActivity_getString" ;
     char * content = NULL ;
     int fd , i ;
     Elf32_Off secOff ;
     funcInfo info ;
 
     if ( argc < 2 ) {
         puts ( "Usage: shellAdder2 libxxx.so .(section) function" ) ;
         return - 1 ;
     }
     fd = open ( argv [ 1 ] , O_RDWR ) ;
     if ( fd < 0 ) {
         printf ( "open %s failed\n" , argv [ 1 ] ) ;
         goto _error ;
     }
 
     /*
    根据section名称 找到section,返回值是section table的sh_offset
    */
 
     secOff = findTargetSectionAddr ( fd , secName ) ;   
     if ( secOff == - 1 ) {
         printf ( "Find section %s failed\n" , secName ) ;
         goto _error ;
     }
 
     /*
    根据函数名称,找到函数信息,返回的是一个结构体,包含函数数据的偏移地址和大小
    */
 
     if ( getTargetFuncInfo ( fd , funcName , &info ) == - 1 ) {
         printf ( "Find function %s failed\n" , funcName ) ;
         goto _error ;
     }
 
     /*
    根据函数的偏移地址和大小,去读取函数,然后做加密处理
    */
 
     content = ( char * ) malloc ( info . st_size ) ;
     if ( content == NULL ) {
         puts ( "Malloc space failed" ) ;
         goto _error ;
     }
 
     lseek ( fd , info . st_value - 1 , SEEK_SET ) ;
     if ( read ( fd , content , info . st_size ) != info . st_size ) {
         puts ( "Malloc space failed" ) ;
         goto _error ;
     }
 
     /*
    将函数数据加密处理
    */
 
     for ( i = 0 ; i < info . st_size - 1 ; i ++ ) {
         content [ i ] = ~ content [ i ] ;
     }
 
     /*
    加密后,写回原位置
    */
 
     lseek ( fd , info . st_value , SEEK_SET ) ;
     if ( write ( fd , content , info . st_size ) != info . st_size ) {
         puts ( "Write modified content to .so failed" ) ;
         goto _error ;
     }
     puts ( "Complete!" ) ;
 
_error :
     free ( content ) ;
     close ( fd ) ;
     return 0 ;
}

②、findTargetSectionAddr 函数,根据section名称找到对应的section位置,这里就不做详细介绍了,上一篇中已经详细提到过。

③、 getTargetFuncInfo 函数,根据函数名称,寻找函数地址及大小
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
static char getTargetFuncInfo ( int fd , const char * funcName , funcInfo * info ) {
     char flag = - 1 , * dynstr ;
     int i ;
     Elf32_Sym funSym ;
     Elf32_Phdr phdr ;
     Elf32_Off dyn_off ;
     Elf32_Word dyn_size , dyn_strsz ;
     Elf32_Dyn dyn ;
     Elf32_Addr dyn_symtab , dyn_strtab , dyn_hash ;
     unsigned funHash , nbucket , nchain , funIndex ;
 
     lseek ( fd , ehdr . e_phoff , SEEK_SET ) ;      //elf header中e_phoff表示的是程序头的偏移
                                           /*
                                           循环遍历,找到.dynamic这个section   并记录其大小和偏移
    */
     for ( i = 0 ; i < ehdr . e_phnum ; i ++ ) {       
         if ( read ( fd , &phdr , sizeof ( Elf32_Phdr ) ) != sizeof ( Elf32_Phdr ) ) {    //读取peogram header 保存在phdr中
             puts ( "Read segment failed" ) ;
             goto _error ;
         }
         if ( phdr . p_type ==    PT_DYNAMIC ) {    // PT_DYNAMIC 表示数组元素给出动态链接信息
             dyn_size = phdr . p_filesz ;      // p_filesz  段在文件映像中所占的字节数
             dyn_off = phdr . p_offset ;        // 从文件头到该段第一个字节的偏移
             flag = 0 ;
             printf ( "Find section %s, size = 0x%x, addr = 0x%x\n" , ".dynamic" , dyn_size , dyn_off ) ;
             break ;
         }
     }
     if ( flag ) {
         puts ( "Find .dynamic failed" ) ;
         goto _error ;
     }
     flag = 0 ;
 
     lseek ( fd , dyn_off , SEEK_SET ) ;    //定位到.dynamic
     for ( i = 0 ; i < dyn_size / sizeof ( Elf32_Dyn ) ; i ++ ) {
         if ( read ( fd , &dyn , sizeof ( Elf32_Dyn ) ) != sizeof ( Elf32_Dyn ) ) {
             puts ( "Read .dynamic information failed" ) ;
             goto _error ;
         }     
         if ( dyn . d_tag == DT_SYMTAB ) {        //循环遍历,找到d_tag为DT_SYMATAB的一项,  dyn此元素包含符号表的地址
             dyn_symtab = dyn . d_un . d_ptr ;      //此 Elf32_Addr 对象代表程序的虚拟地址
             flag += 1 ;
             printf ( "Find .dynsym, addr = 0x%x\n" , dyn_symtab ) ;
         }
         if ( dyn . d_tag == DT_HASH ) {          //循环遍历,找到.hash
             dyn_hash = dyn . d_un . d_ptr ;
             flag += 2 ;
             printf ( "Find .hash, addr = 0x%x\n" , dyn_hash ) ;
         }
         if ( dyn . d_tag == DT_STRTAB ) {        //找到.dynstr的位置
             dyn_strtab = dyn . d_un . d_ptr ;
             flag += 4 ;
             printf ( "Find .dynstr, addr = 0x%x\n" , dyn_strtab ) ;
         }
         if ( dyn . d_tag == DT_STRSZ ) {          //找到.dynstr的大小
             dyn_strsz = dyn . d_un . d_val ;
             flag += 8 ;
             printf ( "Find .dynstr size, size = 0x%x\n" , dyn_strsz ) ;
         }
     }
     if ( ( flag & 0x0f ) != 0x0f ) {            //判断是不是全部都找到了
         puts ( "Find needed .section failed\n" ) ;
         goto _error ;
     }
 
     dynstr = ( char * ) malloc ( dyn_strsz ) ;    //根据大小申请内存空间
     if ( dynstr == NULL ) {
         puts ( "Malloc .dynstr space failed" ) ;
         goto _error ;
     }
 
     lseek ( fd , dyn_strtab , SEEK_SET ) ;      //定位到dyn_strtab  读取.dynstr里面的字符串值
     if ( read ( fd , dynstr , dyn_strsz ) != dyn_strsz ) {
         puts ( "Read .dynstr failed" ) ;
         goto _error ;
     }
     //  print_all(dynstr, dyn_strsz);
 
     funHash = elfhash ( funcName ) ;        //调用elf_hash函数,根据函数名计算一个索引值
     printf ( "Function %s hashVal = 0x%x\n" , funcName , funHash ) ;
 
     lseek ( fd , dyn_hash , SEEK_SET ) ;      //读取hash表中的nbucket
     if ( read ( fd , &nbucket , 4 ) != 4 ) {
         puts ( "Read hash nbucket failed\n" ) ;
         goto _error ;
     }
     printf ( "nbucket = %d\n" , nbucket ) ;
 
     if ( read ( fd , &nchain , 4 ) != 4 ) {    //读取hash表中的nchain
         puts ( "Read hash nchain failed\n" ) ;
         goto _error ;
     }
     //  printf("nchain = %d\n", nchain);
 
     funHash = funHash % nbucket ;
     printf ( "funHash mod nbucket = %d \n" , funHash ) ;
 
     lseek ( fd , funHash * 4 , SEEK_CUR ) ;
     if ( read ( fd , &funIndex , 4 ) != 4 ) {
         puts ( "Read funIndex failed\n" ) ;
         goto _error ;
     }
 
     lseek ( fd , dyn_symtab + funIndex * sizeof ( Elf32_Sym ) , SEEK_SET ) ;    //
     if ( read ( fd , &funSym , sizeof ( Elf32_Sym ) ) != sizeof ( Elf32_Sym ) ) {
         puts ( "Read funSym failed" ) ;
         goto _error ;
     }
 
     if ( strcmp ( dynstr + funSym . st_name , funcName ) != 0 ) //循环,在chain表中查找需要的符号表
     {
         while ( 1 )
         {
             lseek ( fd , dyn_hash + 4 * ( 2 + nbucket + funIndex ) , SEEK_SET ) ;    //在chain数组中定位
 
             if ( read ( fd , &funIndex , 4 ) != 4 )
             {
                 puts ( "Read funIndex failed\n" ) ;
                 goto _error ;
             }
 
             if ( funIndex == 0 )
             {
                 puts ( "Cannot find funtion!\n" ) ;
                 goto _error ;
             }
 
             lseek ( fd , dyn_symtab + funIndex * sizeof ( Elf32_Sym ) , SEEK_SET ) ; //根据索引,在syn_symtab中找到对应的结构体
 
             if ( read ( fd , &funSym , sizeof ( Elf32_Sym ) ) != sizeof ( Elf32_Sym ) )
             {
                 puts ( "In FOR loop, Read funSym failed" ) ;
                 goto _error ;
             }
 
             if ( strcmp ( dynstr + funSym . st_name , funcName ) == 0 )
             {
                 break ;
             }
         }
     }
 
     printf ( "Find: %s, offset = 0x%x, size = 0x%x\n" , funcName , funSym . st_value , funSym . st_size ) ;
     info -> st_value = funSym . st_value ;
     info -> st_size = funSym . st_size ;
     free ( dynstr ) ;
     return 0 ;
 
_error :
     free ( dynstr ) ;
     return - 1 ;
}
该函数返回包含函数信息的结构体funcInfo
1
2
3
4
typedef struct _funcInfo {
         Elf32_Addr st_value ;
         Elf32_Word st_size ;
} funcInfo ;
st_value代表位置, st_size 代表大小, 根据位置和大小 去读取函数的信息。
d、文字叙述:
根据 e_phoff找到第一个 program  header
C++
1
2
3
4
5
6
7
8
9
10
11
typedef struct
{
     Elf32_Word p_type ; /*segment的类型:PT_LOAD = 1 可加载的段*/
     Elf32_Off p_offset ; /*从文件头到该段第一个字节的偏移*/
     Elf32_Addr p_vaddr ; /*该段第一个字节被放到内存中的虚拟地址*/
     Elf32_Addr p_paddr ; /*在linux中这个成员没有任何意义,值与p_vaddr相同*/
     Elf32_Word p_filesz ; /*该段在文件映像中所占的字节数*/
     Elf32_Word p_memsz ; /*该段在内存映像中占用的字节数*/
     Elf32_Word p_flags ; /*段标志*/
     Elf32_Word p_align ; /*p_vaddr是否对齐*/
} Elf32_phdr ;
根据 p_type 找到  p_type == PT_DYNAMIC 的这个 phdr  [数组元素给出动态链接信息]
此段包含. dynamic这个 section
通过. dynamic 这个里面的p_offset就能找到 Elf32_Dyn 这样一个结构体
1
2
3
4
5
6
7
typedef struct dynamic {
     Elf32_Sword d_tag ; //标识
     union {
             Elf32_Sword d_val ; //表示一个整数值,可以有多种解释
             Elf32_Addr d_ptr ; //代表程序的虚拟地址
             } d_un ;
     } Elf32_Dyn ;
根据 d_tag标识,找到标识为 d_tag == DT_SYMTAB、 d_tag == DT_HASH、 d_tag ==DT_STRTAB、 d_tag == DT_STRSZ、的这一系列section。
申请大小为d_uu.d_val的内存空间,为读取.dynstr做准备
可以借助readelf查看.dynstr这个节里的内容
可以知道.dynstr这个section中保存的正是所有函数的名称
通过函数名称,调用elfhash函数,计算出一个hash值
读取hash表里面的一些信息
读取.hash这个section的前四字节。保存在nbucket
读取.hash这个section的第4-8字节。保存在nchain
那么,这两个数值有什么用呢
这得从hash表的组织结构说起
hash表的结构如下:
nbucket
nchain
bucket[nbucket]
chain[nchain]
4部分,nbucket,nchain,bucket数组,chain数组
两个数组中都保存着符号表索引。
hash函数根据函数名称得到一个hash值,设为X,则bucket[X % nbucket]给出了一个索引,设为Y
该索引可用于符号表,也可用于chain表,如果符号表项不是所需要的,那么chain[Y]则给出了具有相同hash值的下一个符号表项
我们就可以根据chain这个类似于链的结构,一直向下搜索。直到找到所需的符号表项。或者chain项中包含值STN_UNDEF。
通过hash表,能够找到一系列的符号表
符号表结构如下
1
2
3
4
5
6
7
8
typedef    struct {
         Elf32_Word st_name ; //名称,索引到字符串表
         Elf32_Addr st_value ; //给出相关联的符号的取值。依赖于具体的上下文.
         Elf32_Word st_size ; //相关的尺寸大小
         unsignedchar st_info ; //给出符号的类型和绑定属性.
         unsignedchar st_other ; //该成员当前包含 0,其含义没有定义。
         Elf32_Half st_shndx ; //给出相关的节区头部表索引。某些索引具有特殊含义。
} Elf32_sym ;
通过st_name来判断找到的符号表正是我们需要的
所有有了下面这样一个循环
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
if ( strcmp ( dynstr + funSym . st_name , funcName ) != 0 )
   {
     while ( 1 )
     {
             lseek ( fd , dyn_hash + 4 * ( 2 + nbucket + funIndex ) , SEEK_SET ) ;
 
             if ( read ( fd , & funIndex , 4 ) != 4 )
               {
                 puts ( "Read funIndex failed\n" ) ;
             goto _error ;
               }
 
             if ( funIndex == 0 )
               {
             puts ( "Cannot find funtion!\n" ) ;
             goto _error ;
             }
 
             lseek ( fd , dyn_symtab + funIndex * sizeof ( Elf32_Sym ) , SEEK_SET ) ;
 
             if ( read ( fd , & funSym , sizeof ( Elf32_Sym ) ) != sizeof ( Elf32_Sym ) )
               {
             puts ( "In FOR loop, Read funSym failed" ) ;
             goto _error ;
               }
 
             if ( strcmp ( dynstr + funSym . st_name , funcName ) == 0 )
               {
                 break ;
               }
     }
   }
这个循环的发生是在bucket数组中没有找到我们需要的符号表才进入的。也就是说,这个过程其实是在遍历chain这个数组
 lseek(fd, dyn_hash + 4 * (2 + nbucket + funIndex), SEEK_SET);
其中funIndex是函数的索引值
hash表前两项是  nbucket 和  nchain 两个int型数据。
还有一个int型数组bucket[] 长度是nbucket
所以,根据 dyn_hash + (2 + nbucket + funIndex) * 4 便实现了根据索引定位到chain数组中函数的位置
然后再去 dyn_symtab中找到对应的 Elf32_Sym结构
根据其中的 st_name来进行判断,循环查找

0×3

解密
a、解密的过程和加密过程基本上一致的,只是说我们必须得到内存中去寻找那一系列的地址。
大致过程是:
在内存中找到so文件被加载到的地址,记为base
寻找函数
修改内存页属性
解密函数数据
还原内存页属性
b、解密入口函数如下:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void init_getString ( ) {
     const char target_fun [ ] = "Java_com_example_shelldemo2_MainActivity_getString" ;
     funcInfo info ;
     int i ;
     unsigned int npage , base = getLibAddr ( ) ;
 
     __android_log_print ( ANDROID_LOG_INFO , "JNITag" , "base addr =  0x%x" , base ) ;
 
     if ( getTargetFuncInfo ( base , target_fun , &info ) == - 1 ) {
       print_debug ( "Find Java_com_example_shelldemo2_MainActivity_getString failed" ) ;
       return ;
     }
 
     npage = info . st_size / PAGE_SIZE + ( ( info . st_size % PAGE_SIZE == 0 ) ? 0 : 1 ) ;
 
     if ( mprotect ( ( void * ) ( ( base + info . st_value ) / PAGE_SIZE * PAGE_SIZE ) , npage , PROT_READ | PROT_EXEC | PROT_WRITE ) != 0 ) {
         print_debug ( "mem privilege change failed" ) ;
     }
 
     for ( i = 0 ; i < info . st_size - 1 ; i ++ ) {
         char * addr = ( char * ) ( base + info . st_value - 1 + i ) ;
         * addr = ~ ( * addr ) ;
     }
 
     if ( mprotect ( ( void * ) ( ( base + info . st_value ) / PAGE_SIZE * PAGE_SIZE ) , npage , PROT_READ | PROT_EXEC ) != 0 ) {
         print_debug ( "mem privilege change failed" ) ;
     }
 
}

里面用到了getLibAddr,getTargetFuncInfo两个函数。

getLibAddr函数用来在程序的内存空间中找到so被加载到的位置。

getTargetFuncInfo函数是用来在程序内存空间中的so中找到需要解密的函数。

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
static char getTargetFuncInfo ( unsigned long base , const char * funcName , funcInfo * info ) {
     char flag = - 1 , * dynstr ;
     int i ;
     Elf32_Ehdr * ehdr ;
     Elf32_Phdr * phdr ;
     Elf32_Off dyn_vaddr ;
     Elf32_Word dyn_size , dyn_strsz ;
     Elf32_Dyn * dyn ;
     Elf32_Addr dyn_symtab , dyn_strtab , dyn_hash ;
     Elf32_Sym * funSym ;
     unsigned funHash , nbucket ;
     unsigned * bucket , * chain ;
 
     ehdr = ( Elf32_Ehdr * ) base ;
     phdr = ( Elf32_Phdr * ) ( base + ehdr -> e_phoff ) ;
//    __android_log_print(ANDROID_LOG_INFO, "JNITag", "phdr =  0x%p, size = 0x%x\n", phdr, ehdr->e_phnum);
     for ( i = 0 ; i < ehdr -> e_phnum ; ++ i ) {
//        __android_log_print(ANDROID_LOG_INFO, "JNITag", "phdr =  0x%p\n", phdr);
         if ( phdr -> p_type ==    PT_DYNAMIC ) {
             flag = 0 ;
             print_debug ( "Find .dynamic segment" ) ;
             break ;
         }
         phdr ++ ;
     }
     if ( flag )
         goto _error ;
     dyn_vaddr = phdr -> p_vaddr + base ;
     dyn_size = phdr -> p_filesz ;
     __android_log_print ( ANDROID_LOG_INFO , "JNITag" , "dyn_vadd =  0x%x, dyn_size =  0x%x" , dyn_vaddr , dyn_size ) ;
     flag = 0 ;
     for ( i = 0 ; i < dyn_size / sizeof ( Elf32_Dyn ) ; ++ i ) {
         dyn = ( Elf32_Dyn * ) ( dyn_vaddr + i * sizeof ( Elf32_Dyn ) ) ;
         if ( dyn -> d_tag == DT_SYMTAB ) {
             dyn_symtab = ( dyn -> d_un ) . d_ptr ;
             flag += 1 ;
             __android_log_print ( ANDROID_LOG_INFO , "JNITag" , "Find .dynsym section, addr = 0x%x\n" , dyn_symtab ) ;
         }
         if ( dyn -> d_tag == DT_HASH ) {
             dyn_hash = ( dyn -> d_un ) . d_ptr ;
             flag += 2 ;
             __android_log_print ( ANDROID_LOG_INFO , "JNITag" , "Find .hash section, addr = 0x%x\n" , dyn_hash ) ;
         }
         if ( dyn -> d_tag == DT_STRTAB ) {
             dyn_strtab = ( dyn -> d_un ) . d_ptr ;
             flag += 4 ;
             __android_log_print ( ANDROID_LOG_INFO , "JNITag" , "Find .dynstr section, addr = 0x%x\n" , dyn_strtab ) ;
         }
         if ( dyn -> d_tag == DT_STRSZ ) {
             dyn_strsz = ( dyn -> d_un ) . d_val ;
             flag += 8 ;
             __android_log_print ( ANDROID_LOG_INFO , "JNITag" , "Find strsz size = 0x%x\n" , dyn_strsz ) ;
         }
     }
     if ( ( flag & 0x0f ) != 0x0f ) {
         print_debug ( "Find needed .section failed\n" ) ;
         goto _error ;
     }
     dyn_symtab += base ;
     dyn_hash += base ;
     dyn_strtab += base ;
     dyn_strsz += base ;
 
     funHash = elfhash ( funcName ) ;
     funSym = ( Elf32_Sym * ) dyn_symtab ;
     dynstr = ( char * ) dyn_strtab ;
     nbucket = * ( ( int * ) dyn_hash ) ;
     bucket = ( int * ) ( dyn_hash + 8 ) ;
     chain = ( unsigned int * ) ( dyn_hash + 4 * ( 2 + nbucket ) ) ;
 
     flag = - 1 ;
     __android_log_print ( ANDROID_LOG_INFO , "JNITag" , "hash = 0x%x, nbucket = 0x%x\n" , funHash , nbucket ) ;
     for ( i = bucket [ funHash % nbucket ] ; i != 0 ; i = chain [ i ] ) {
         __android_log_print ( ANDROID_LOG_INFO , "JNITag" , "Find index = %d\n" , i ) ;
         if ( strcmp ( dynstr + ( funSym + i ) -> st_name , funcName ) == 0 ) {
             flag = 0 ;
             __android_log_print ( ANDROID_LOG_INFO , "JNITag" , "Find %s\n" , funcName ) ;
             break ;
         }
     }
     if ( flag ) goto _error ;
     info -> st_value = ( funSym + i ) -> st_value ;
     info -> st_size = ( funSym + i ) -> st_size ;
     __android_log_print ( ANDROID_LOG_INFO , "JNITag" , "st_value = %d, st_size = %d" , info -> st_value , info -> st_size ) ;
     return 0 ;
_error :
     return - 1 ;
}

可以看到,和加密过程中寻找函数地址是一样的。只是在定位过程中得计算偏移地址。

0×4

 加密程序执行效果:
解密过程效果:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

加密so文件中指定的函数 的相关文章

随机推荐

  • 安装Pycharm工具 -- ubuntu18.04

    在Ubuntu18 04下 pycharm工具的安装及其快捷方式的创建 下载pycharm安装包 tar gz包 网址 https www jetbrains com pycharm tar gz 安装包解压缩 此处没有指定解压到哪个路径
  • 最经典的黑客技术入门知识

    最经典的黑客技术入门知识 整理 Ackarlix 第一节 什么是黑客 以我的理解 黑客 大体上应该分为 正 邪 两类 正派黑客依靠自己掌握的知识帮助系统管理员找出系统中的漏洞并加以完善 而邪派黑客则是通过各种黑客技能对系统进行攻击 入侵或者
  • js原型和原型链你只要看这一篇

    一 原型概述 任何对象都有一个原型对象 这个原型对象由对象的内置属性 proto 指向它的构造函数的prototyoe指向的对象 即任何对象都是由一个构造函数创建的 被创建的对象都可以获得构造函数的prototype属性 注意 对象是没有p
  • mysql数据库内置函数大全_MySQL数据库——内置函数

    MySQL数据库 内置函数 建表并插入数据 create table student id char 36 primary key name varchar 8 not null age int 3 default 0 mobile cha
  • win7用友u8安装教程_如何在win7系统中安装用友u8(图文)

    现在很多大企业或公司都会用到用友u8软件 相信大家对用友u8都比较熟悉了 一些新手不知道如何在win7系统中安装用友u8 所以今天给大家带来就是在win7系统中安装用友u8的方法 解决方法如下 1 打开 控制面板 程序和功能 打开或关闭wi
  • 【C++】:用sort对string类型进行排序

    前言 这个问题来自于leetcode上面的一道题 Valid Anagram Given two strings s and t write a function to determine if t is an anagram of s F
  • 第3关:文件查看器

    编程要求 实现对给定文件夹目录结构的展示 并以文件名按升序排序的形式打印至控制台 如果是文件夹则在其名字之前加上 若是文件则加上 上级目录与下级目录 下级文件用两个空格作为间隔 补充完善右侧代码区中的showDirStructure Fil
  • 【经典】synergy共享鼠标键盘/一套鼠标键盘操作多台电脑

    使用场景 用一套鼠标键盘控制两个或多个电脑屏幕 所有电脑位于同一局域网下 win10 操作系统 安装 synergy step1 下载 下载地址 synergy step2 安装 选择自己想要安装在的目录然后一直 next 最后 finis
  • java生成PDF(图片,模板,表格)

    刚接到了一个需求 生成一个pdf 一开始以为挺简单的 通过模板生成嘛 我也发过相应的文章 根据模板直接生成pdf 响应到前端或者根据模板生成pdf 直接指定下载位置 这两种方案都可以 不过这篇文章主要讲的生成的pdf是既有模板填充还需要自己
  • hdu 2586 How far away ?

    Problem acm hdu edu cn showproblem php pid 2586 Meaning 给一棵 n 个点的树 和 n 1 条边的边权 多次询问树上两点的距离 Analysis 以任意顶点为根 DFS 预处理出所有结点
  • 【数据库MongoDB的学习】

    一 数据库和文件的主要区别 1 数据库有数据库表 行和列的概念 让我们存储操作数据更方便 2 数据库提供了非常方便的接口 可以让 nodejs php java net 很方便的实现增加修改删 除功能 二 关系型和非关系型数据库的介绍 关系
  • 深度学习数字仪表盘识别_一种改进的卷积神经网络的数显仪表识别方法

    数显仪表 就是一种显示数字的仪器 便于人们了解相关信息 目前 数显仪表被广泛的应用于航天 农业 工业等各个行业中 但出于工作条件和成本控制等原因 仍有很多的仪表无法直接获得读数 大多由人工读取 但是人工无法长时间且实时记录 还有些地方工人不
  • Deepin操作系统丨一台10年前的家用联想台式机重装国产Linux系统,制作成生信服务器

    本篇笔记是利用个人电脑搭建Linux系统 deepin 20 8 的教程 包括系统下载 映像刻录 启动盘制作 电脑BIOS设置 安装系统 故障解决 驱动更新 软件下载 conda配置 R语言和Rstudio server配置 远程SSH配置
  • 【深度学习】入门的25个概念

    神经网络基础 1 神经元 Neuron 就像形成我们大脑基本元素的神经元一样 神经元形成神经网络的基本结构 想象一下 当我们得到新信息时我们该怎么做 当我们获取信息时 我们一般会处理它 然后生成一个输出 类似地 在神经网络的情况下 神经元接
  • R ggplot2坐标轴设置相关函数

    1 设置坐标轴标签 xlab ylab labs x NULL y NULL 2 设置坐标轴刻度范围 xlim ylim 3 添加标题 ggtitle 4 theme 控制字体 坐标轴刻度 背景以及背景上的线条 4 1 在theme 内部有
  • 出现—passwd:Authentication token manipulation error—错误的解决办法

    在服务器安装过程中 经常遇到软件安装失败 有一部分原因是在linux系统禁止添加新的用户和修改原有用户 在网上找了一篇解决此问题的文章 特转到此处作为参考 文章 http blog sina com cn s blog 70c9c4b401
  • AI数字人:基于VITS-fast-fine-tuning构建多speaker语音训练

    1 VITS模型介绍 VITS Variational Inference with adversarial learning for end to end Text to Speech 是一种语音合成方法 它使用预先训练好的语音编码器 v
  • import cv2 ImportError: /opt/ros/kinetic/lib/python2.7/dist-packages/cv2.so: undefined symbol: P

    usr bin python3 5 home utryjc PycharmProjects qtWayPionts ui main py Traceback most recent call last File home utryjc Py
  • 【PTA】斐波那契数列第n项

    求斐波那契数列的第n项 f n f n 1 f n 2 其中f1 f2 1 import java util public class Main public static void main String args Scanner sca
  • 加密so文件中指定的函数

    加密so文件中指定的函数 作者 0n1y3nd丶 分类 逆向学习 发布时间 2014 09 04 22 24 61条评论 前言 上一篇文章中详细分析了对so文件中自定义section的加密 这一篇来分析下对so文件中自定义函数的加密 原文地