首页
社区
课程
招聘
[原创]实战分析之符号混淆和LD_PRELOAD技术的动态补丁破解(98750.so)- 图片和附件修复版
发表于: 2022-5-2 12:53 17068

[原创]实战分析之符号混淆和LD_PRELOAD技术的动态补丁破解(98750.so)- 图片和附件修复版

2022-5-2 12:53
17068

2017年发表过这篇文章,当时图片全部上传kanxue.com,然后在bbs.pediy.com中引用图片既然不显示,然后超过2年帖子不能编辑了,于是重发补图和附件。

Part 1 - 利用IDA、objdump、gdb等静态分析或调试器优先加载符号表的特性,从而通过修改节.symtab中符号字符信息来欺骗这些工具)

 

这个样本是某个主程序调用的动态库,我之前分析了很多  次,用IDA打开查看导出函数所有的函数名称都是乱起八糟没有意义的,之前以为是所有的函数名称都被加密混淆了。同时我也用IDA和gdb调试了很多次,知道这个样本中的代码是加密的,运行的时候会动态解密,解密函数名称为tf_rkg_vavg, 这个函数内部会调用mprotect并递归调用自己来解密代码,解密成功后这个函数在末尾会跳转到已解密的ragel_byq去执行。其中ragel_byq函数是被加密的,反汇编显示的是乱起八糟的汇编指令,如下图所示。

ragel_byq函数解密前

之前调试的时候一直都是在主程序下调试,然后这个动态库里面下断点,由于主程序很大所以很不方便,于是想直接写个Demo去调用这个动态库,然后去解密看是否可行。

vim unpack.c

源码如下:

#include<stdio.h>

#include<dlfcn.h>

int main()

{

    void *handle1 = dlopen("./98750.so", RTLD_NOW); //程序虽然在同一个目录,必须带./,否则找不到

    if (NULL == handle1)

        fprintf(stderr, "Failed to open!\n");

    int (* f)()=(int(*)()) dlsym(handle1, "tf_rkg_vavg");

    if (f != NULL)

        f();

    dlclose(handle1);

    return 0;

}

编译

gcc -g -o unpack unpack.c -ldl

调试以上这个程序,发现dlsym返回空,特别奇怪,当时觉得是不是C++ mangling之类的符号改变,导出符号没写对导致。

网上查了一下查看ELF动态库导出函数方法

(查看so动态库导出函数)方法一: readelf -DsW 98750.so [| cat -n | grep tf_rkg_vavg ]

该命令不仅显示符号表信息,还会显示.gnu.hash节信息

输出如下:

root@speedlinux:/home/test# readelf -Ds 98750.so | cat -n 

     1    

     2    Symbol table for image:

     3      Num Buc:    Value  Size   Type   Bind Vis      Ndx Name

     4       58   0: 00015564     4 OBJECT  GLOBAL DEFAULT  23 CGameCreature_GetMoney

     5      213   0: 00015608     4 OBJECT  GLOBAL DEFAULT  23 CGuild_GetMemberOnlineCou

     6      264   0: 00015618     4 OBJECT  GLOBAL DEFAULT  23 ItemBag_RemoveItem

     7      168   2: 0000f26d    51 FUNC    GLOBAL DEFAULT  12 KickFix2

     8      220   9: 00007ddc   267 FUNC    GLOBAL DEFAULT  12 guser_subpoint

     9      181  10: 000153e4     4 OBJECT  GLOBAL DEFAULT  23 pdbr

    10      160  11: 000153d0     4 OBJECT  GLOBAL DEFAULT  23 lastDidList

    11      195  11: 0000b6c3    63 FUNC    GLOBAL DEFAULT  12 MyCreEnterWorld

    12      135  12: 00007ba0    78 FUNC    GLOBAL DEFAULT  12 freedbret

    13      201  12: 0000a203   156 FUNC    GLOBAL DEFAULT  12 InsertCode1

    14      108  13: 00007d17   197 FUNC    GLOBAL DEFAULT  12 guser_addpoint

    15      202  13: 0000a2b3   175 FUNC    GLOBAL DEFAULT  12 InsertCode2

    16      204  14: 0000a3a6    67 FUNC    GLOBAL DEFAULT  12 InsertCode3

    17      102  15: 0001555c     4 OBJECT  GLOBAL DEFAULT  23 timenow

。。。

   323    Symbol table of `.gnu.hash' for image:

   324      Num Buc:    Value  Size   Type   Bind Vis      Ndx Name

   325       48   0: 00015600     4 OBJECT  GLOBAL DEFAULT  23 constring

   326       49   2: 0000b70f   437 FUNC    GLOBAL DEFAULT  12 InsertCreEnterWorld

   327       50   4: 0000e403  1054 FUNC    GLOBAL DEFAULT  12 item_rnd_ling

   328       51   5: 00007f48    94 FUNC    GLOBAL DEFAULT  12 guser_offline

   329       52   7: 00007688    79 FUNC    GLOBAL DEFAULT  12 GetValue

   330       53  10: 00012025  1682 FUNC    GLOBAL DEFAULT  12 entry_old

   331       54  10: 0000b8fb   228 FUNC    GLOBAL DEFAULT  12 do_gameuser_getguild

   332       55  10: 000151e0    36 OBJECT  GLOBAL DEFAULT  22 st_1

   333       56  11: 00015220    36 OBJECT  GLOBAL DEFAULT  22 st_2

   334       57  12: 00015260    36 OBJECT  GLOBAL DEFAULT  22 st_3

   335       58  13: 00015564     4 OBJECT  GLOBAL DEFAULT  23 CGameCreature_GetMoney

   336       59  13: 000152a0    36 OBJECT  GLOBAL DEFAULT  22 st_4

   337       60  13: 0000d68d   423 FUNC    GLOBAL DEFAULT  12 IdentifyItem_addition

   338       61  14: 000152e0    36 OBJECT  GLOBAL DEFAULT  22 st_5

。。。

   588      311 255: 000082bb   539 FUNC    GLOBAL DEFAULT  12 add_newatt

   589      312 258: 000100a0    75 FUNC    GLOBAL DEFAULT  12 GactName

   590      313 259: 00015544     4 OBJECT  GLOBAL DEFAULT  23 ItemBag_GetItem

   591      314 259: 00015730     4 OBJECT  GLOBAL DEFAULT  23 CDBAgentResult_Clear

   592      315 260: 0000f2ef    36 FUNC    GLOBAL DEFAULT  12 CGS_SendPacketFackEnd

   593      316 260: 0001563c     4 OBJECT  GLOBAL DEFAULT  23 CGameWorld__GetGameSystem

   594      317 261: 00015044     4 OBJECT  GLOBAL DEFAULT  22 lingid

   595      318 262: 000155f4     4 OBJECT  GLOBAL DEFAULT  23 CChatItems_CChatItems

注意到-D选项说明,表示显示符号的时候使用动态节信息(即.dymbol)

readelf 帮助

Usage: readelf <option(s)> elf-file(s)

 Display information about the contents of ELF format files

 Options are:

  -a --all               Equivalent to: -h -l -S -s -r -d -V -A -I

  -h --file-header       Display the ELF file header

  -l --program-headers   Display the program headers

     --segments          An alias for --program-headers

  -S --section-headers   Display the sections' header

     --sections          An alias for --section-headers

  -g --section-groups    Display the section groups

  -t --section-details   Display the section details

  -e --headers           Equivalent to: -h -l -S

  -s --syms              Display the symbol table

     --symbols           An alias for --syms

  --dyn-syms             Display the dynamic symbol table

  -n --notes             Display the core notes (if present)

  -r --relocs            Display the relocations (if present)

  -u --unwind            Display the unwind info (if present)

  -d --dynamic           Display the dynamic section (if present)

  -V --version-info      Display the version sections (if present)

  -A --arch-specific     Display architecture specific information (if any).

  -c --archive-index     Display the symbol/file index in an archive

  -D --use-dynamic       Use the dynamic section info when displaying symbols

  -x --hex-dump=<number|name>

                         Dump the contents of section <number|name> as bytes

  -p --string-dump=<number|name>

                         Dump the contents of section <number|name> as strings

  -R --relocated-dump=<number|name>

                         Dump the contents of section <number|name> as relocated bytes

  -w[lLiaprmfFsoRt] or

  --debug-dump[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,

               =frames-interp,=str,=loc,=Ranges,=pubtypes,

               =gdb_index,=trace_info,=trace_abbrev,=trace_aranges]

                         Display the contents of DWARF2 debug sections

  --dwarf-depth=N        Do not display DIEs at depth N or greater

  --dwarf-start=N        Display DIEs starting with N, at the same depth

                         or deeper

  -I --histogram         Display histogram of bucket list lengths

  -W --wide              Allow output width to exceed 80 characters

  @<file>                Read options from <file>

  -H --help              Display this information

  -v --version           Display the version number of readelf

Report bugs to <http://www.sourceware.org/bugzilla/>

通过readelf -S 98750.s查看所有节信息,输出如下所示

root@speedlinux:/home/test# readelf -S 98750.so 

There are 28 section headers, starting at offset 0x14484:

Section Headers:

  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al

  [ 0]                   NULL            00000000 000000 000000 00      0   0  0

  [ 1] .note.gnu.build-i NOTE            000000f4 0000f4 000024 00   A  0   0  4

  [ 2] .hash             HASH            00000118 000118 000920 04   A  4   0  4

  [ 3] .gnu.hash         GNU_HASH        00000a38 000a38 000968 04   A  4   0  4

  [ 4] .dynsym           DYNSYM          000013a0 0013a0 0013f0 10   A  5   1  4

  [ 5] .dynstr           STRTAB          00002790 002790 001022 00   A  0   0  1

  [ 6] .gnu.version      VERSYM          000037b2 0037b2 00027e 02   A  4   0  2

  [ 7] .gnu.version_r    VERNEED         00003a30 003a30 000040 00   A  5   1  4

  [ 8] .rel.dyn          REL             00003a70 003a70 003708 08   A  4   0  4

  [ 9] .rel.plt          REL             00007178 007178 000018 08   A  4  11  4

  [10] .init             PROGBITS        00007190 007190 000030 00  AX  0   0  4

  [11] .plt              PROGBITS        000071c0 0071c0 000040 04  AX  0   0  4

  [12] .text             PROGBITS        00007200 007200 00b9c8 00  AX  0   0 16

  [13] .fini             PROGBITS        00012bc8 012bc8 00001c 00  AX  0   0  4

  [14] .rodata           PROGBITS        00012be4 012be4 000f0c 00   A  0   0  4

  [15] .eh_frame         PROGBITS        00013af0 013af0 000004 00   A  0   0  4

  [16] .ctors            PROGBITS        00014f04 013f04 000008 00  WA  0   0  4

  [17] .dtors            PROGBITS        00014f0c 013f0c 000008 00  WA  0   0  4

  [18] .jcr              PROGBITS        00014f14 013f14 000004 00  WA  0   0  4

  [19] .dynamic          DYNAMIC         00014f18 013f18 0000d0 08  WA  5   0  4

  [20] .got              PROGBITS        00014fe8 013fe8 00000c 04  WA  0   0  4

  [21] .got.plt          PROGBITS        00014ff4 013ff4 000018 04  WA  0   0  4

  [22] .data             PROGBITS        00015020 014020 000364 00  WA  0   0 32

  [23] .bss              NOBITS          000153a0 014384 00039c 00  WA  0   0 32

  [24] .comment          PROGBITS        00000000 014384 000025 01  MS  0   0  1

  [25] .shstrtab         STRTAB          00000000 0143a9 0000d8 00      0   0  1

  [26] .symtab           SYMTAB          00000000 0148e4 0016d0 10     27  47  4

  [27] .strtab           STRTAB          00000000 015fb4 001367 00      0   0  1

Key to Flags:

  W (write), A (alloc), X (execute), M (merge), S (strings)

  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)

  O (extra OS processing required) o (OS specific), p (processor specific)

试了直接单独-D选项是没有输出的,必须结合-s选项,但是,单独-s选项却可以输出,而且输出包含了tf_rkg_vavg函数名称,如下表所示,

从表中输出可以看出,readelf -s xxx.so 只是输出.dynsym和.symtab这两个节的的内容,而tf_rkg_vavg函数名称出现在.symtab节中。

root@speedlinux:/home/test# readelf -s 98750.so | cat -n | grep tf_rkg_vavg

   684       358: 000126b7  1168 FUNC    GLOBAL DEFAULT   12 tf_rkg_vavg

root@speedlinux:/home/test# readelf -s 98750.so | cat -n 

     1    

     2    Symbol table '.dynsym' contains 319 entries:

     3       Num:    Value  Size Type    Bind   Vis      Ndx Name

     4         0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 

     5         1: 00000000     0 FUNC    GLOBAL DEFAULT  UND getpagesize@GLIBC_2.0 (2)

     6         2: 00000000     0 FUNC    GLOBAL DEFAULT  UND __errno_location@GLIBC_2.0 (2)

。。。

   319       315: 0000f2ef    36 FUNC    GLOBAL DEFAULT   12 CGS_SendPacketFackEnd

   320       316: 0001563c     4 OBJECT  GLOBAL DEFAULT   23 CGameWorld__GetGameSystem

   321       317: 00015044     4 OBJECT  GLOBAL DEFAULT   22 lingid

   322       318: 000155f4     4 OBJECT  GLOBAL DEFAULT   23 CChatItems_CChatItems

   323    

   324    Symbol table '.symtab' contains 365 entries:

   325       Num:    Value  Size Type    Bind   Vis      Ndx Name

   326         0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 

   327         1: 000000f4     0 SECTION LOCAL  DEFAULT    1 

   328         2: 00000118     0 SECTION LOCAL  DEFAULT    2 

   329         3: 00000a38     0 SECTION LOCAL  DEFAULT    3 

   330         4: 000013a0     0 SECTION LOCAL  DEFAULT    4 

   331         5: 00002790     0 SECTION LOCAL  DEFAULT    5 

   332         6: 000037b2     0 SECTION LOCAL  DEFAULT    6 

   333         7: 00003a30     0 SECTION LOCAL  DEFAULT    7 

   334         8: 00003a70     0 SECTION LOCAL  DEFAULT    8 

。。。

   685       359: 0001504c    12 OBJECT  GLOBAL DEFAULT   22 uengrf

   686       360: 0000f313   354 FUNC    GLOBAL DEFAULT   12 VafregPTF_FraqCnpxrgSnpx

   687       361: 00007501   150 FUNC    GLOBAL DEFAULT   12 JevgrPbqrZrzbel

   688       362: 0000e391   114 FUNC    GLOBAL DEFAULT   12 HaFvtarqVgrz_ubbx

   689       363: 00015738     4 OBJECT  GLOBAL DEFAULT   23 PFxvyyPbagnvare_NqqFxvyy

   690       364: 00007190     0 FUNC    GLOBAL DEFAULT   10 _vavg

root@speedlinux:/home/test# 

(查看so动态库导出函数)方法二:nm -D 98750.so

这个方法同样没有包含tf_rkg_vavg这个函数

root@speedlinux:/home/test# nm -D 98750.so | cat -n          

     1    000153b4 B ACEXFdList

     2    00015534 B BroadcastMedia

     3    0000d5f5 T BroadcastMediaEx

     4    000154d0 B BroadcastMediaForScript

     5    00015524 B Bulletin

     6    000154cc B BulletinEx

     7    000156e0 B CAttribMgr_GetAttrib

     8    000155f4 B CChatItems_CChatItems

     9    00015538 B CChatItems_Clear

    10    00015648 B CChatItems_Push

    11    00015710 B CDBAgentClt_PushSQLCmd

    12    00015730 B CDBAgentResult_Clear

    13    0001554c B CDBAgentResult_NextRow

。。。

   311    000153ec B szXMLTemplate

   312    00015048 D szhid

   313             U time

   314    0001555c B timenow

   315    000153ac B tpo3

   316    000153a8 B tpos

   317    00007597 T trim

   318    0000988e T xorbuf

root@speedlinux:/home/test# nm -D 98750.so | cat -n | grep tf_rkg_vavg  

nm的帮助选项如下:

root@speedlinux:/home/test# nm -q 98750.so 

nm: invalid option -- 'q'

Usage: nm [option(s)] [file(s)]

 List symbols in [file(s)] (a.out by default).

 The options are:

  -a, --debug-syms       Display debugger-only symbols

  -A, --print-file-name  Print name of the input file before every symbol

  -B                     Same as --format=bsd

  -C, --demangle[=STYLE] Decode low-level symbol names into user-level names

                          The STYLE, if specified, can be `auto' (the default),

                          `gnu', `lucid', `arm', `hp', `edg', `gnu-v3', `java'

                          or `gnat'

      --no-demangle      Do not demangle low-level symbol names

  -D, --dynamic          Display dynamic symbols instead of normal symbols

      --defined-only     Display only defined symbols

  -e                     (ignored)

  -f, --format=FORMAT    Use the output format FORMAT.  FORMAT can be `bsd',

                           `sysv' or `posix'.  The default is `bsd'

  -g, --extern-only      Display only external symbols

  -l, --line-numbers     Use debugging information to find a filename and

                           line number for each symbol

  -n, --numeric-sort     Sort symbols numerically by address

  -o                     Same as -A

  -p, --no-sort          Do not sort the symbols

  -P, --portability      Same as --format=posix

  -r, --reverse-sort     Reverse the sense of the sort

      --plugin NAME      Load the specified plugin

  -S, --print-size       Print size of defined symbols

  -s, --print-armap      Include index for symbols from archive members

      --size-sort        Sort symbols by size

      --special-syms     Include special symbols in the output

      --synthetic        Display synthetic symbols as well

  -t, --radix=RADIX      Use RADIX for printing symbol values

      --target=BFDNAME   Specify the target object format as BFDNAME

  -u, --undefined-only   Display only undefined symbols

  -X 32_64               (ignored)

  @FILE                  Read options from FILE

  -h, --help             Display this information

  -V, --version          Display this program's version number

nm: supported targets: elf32-i386 a.out-i386-linux pei-i386 elf32-little elf32-big elf64-x86-64 elf32-x86-64 pei-x86-64 elf64-l1om elf64-k1om elf64-little elf64-big plugin srec symbolsrec verilog tekhex binary ihex trad-core

从nm的帮助看有--demangle(即-C)选项,显示C++改编前的函数名称,当时想以上查看导出函数的两个方法对于C++函数是不是不能能够显示出来呢,而98750.so中的tf_rkg_vavg和ragel_byq这两个函数正好是C++函数,也就是说98750.so正好是C++函数。同时看到nm中还有个-a显示调试符号的选项,于是是试了一下如下输出

root@speedlinux:/home/test# nm -a 98750.so | cat -n          

     1    000153a0 b .bss

     2    00000000 n .comment

     3    00014f04 d .ctors

     4    00015020 d .data

     5    00014f0c d .dtors

     6    00014f18 d .dynamic

     7    00002790 r .dynstr

     8    000013a0 r .dynsym

     9    00013af0 r .eh_frame

    10    00012bc8 t .fini

    11    00000a38 r .gnu.hash

    12    000037b2 r .gnu.version

    13    00003a30 r .gnu.version_r

    14    00014fe8 d .got

    15    00014ff4 d .got.plt

    16    00000118 r .hash

    17    00007190 t .init

    18    00014f14 d .jcr

    19    000000f4 r .note.gnu.build-id

    20    000071c0 t .plt

    21    00003a70 r .rel.dyn

    22    00007178 r .rel.plt

    23    00012be4 r .rodata

    24    00007200 t .text

    25    00015528 B BVQ2CGE

    26    00007bee T BaQOTrgCnlTQ

    27    00007fa6 T BaYbtvarqFpber

    28    000156ec B ErfSvaqAcp

    29    00015514 B FHfreCrgQngn_QryCrg

。。。

   353    00015620 B yu_12

   354    0001556c B yu_13

   355    0000902c T yvat_nqq

   356    00015044 D yvatvq

   357             U zcebgrpg@@TYVOP_2.0

   358    0000b8c4 T zl_tnzrhfre_trgthvyq

   359    0001505c D znkfzy

   360    00015718 B znp

   361             U znyybp@@TYVOP_2.0

   362             U zrzfrg@@TYVOP_2.0

   363             U zrzpcl@@TYVOP_2.0

   364             U zrzpzc@@TYVOP_2.0

root@speedlinux:/home/test# nm -a 98750.so | cat -n | grep tf_rkg_vavg

   322    000126b7 T tf_rkg_vavg

root@speedlinux:/home/test# nm -C 98750.so | cat -n 

     1    00015528 B BVQ2CGE

     2    00007bee T BaQOTrgCnlTQ

     3    00007fa6 T BaYbtvarqFpber

     4    000156ec B ErfSvaqAcp

     5    00015514 B FHfreCrgQngn_QryCrg

     6    00015548 B FHfreCrgQngn_SvaqCrg

     7    000155e4 B FHfreCrgQngn_TrgAhz

     8    00015728 B FHfreCrgQngn_TrgPheePneelvatCrg

     9    0001572c B FNggevoNqqgvba_TrgNqqPbhag

。。。。

   326    00015620 B yu_12

   327    0001556c B yu_13

   328    0000902c T yvat_nqq

   329    00015044 D yvatvq

   330             U zcebgrpg@@TYVOP_2.0

   331    0000b8c4 T zl_tnzrhfre_trgthvyq

   332    0001505c D znkfzy

   333    00015718 B znp

   334             U znyybp@@TYVOP_2.0

   335             U zrzfrg@@TYVOP_2.0

   336             U zrzpcl@@TYVOP_2.0

   337             U zrzpzc@@TYVOP_2.0

root@speedlinux:/home/test# nm -C 98750.so | cat -n | grep tf_rkg_vavg

   296    000126b7 T tf_rkg_vavg

root@speedlinux:/home/test# 

从以上输出看-a和-C确实可以显示出来,这两个函数名称也是从gdb和IDA里面看到并拷贝出来的,那么为什么objdump -D就显示不出来当时很是纳闷,到底跟C++编译器函数名称修饰的改编有没有关系,详细查了mangling和demangle的资料其实根本没有任何关系,C++改编也会有规律可循的,而且经过C++改编后是可以通过c++filt工具还原成原函数的,详细可以查阅c++filt的帮助说明,而且nm --demangle就是用来将符号中改编过的函数还原显示成原始函数的,

那么用dlsym去获取这个函数的地址返回结果为什么是空的,后来看了一下地址tf_rkg_vavg 在objdump -C中的地址为126b7,然后我就用这个地址过滤,结果如下:

root@cjy-virtual-machine:~/test# nm -C 98750.so | cat -n | grep 126b7

   296    000126b7 T tf_rkg_vavg

root@cjy-virtual-machine:~/test# nm -D 98750.so | cat -n | grep 126b7

   218    000126b7 T gs_ext_init

已经很明显了, gdb和IDA里面显示的函数都是错的(其实不是错的,跟IDA选择解析符号顺序有关系),当时分析这个动态库的时候,还以为所有函数都加密了呢?用gs_ext_init名称调用dlsym正常调用并解密代码,到底objdump -D选项有什么神奇之处,可以导致gdb,IDA同一个选项地址显示的结果不一样呢?而且gdb对gs_ext_init下断点根本找不到符号

nm -C 98750.so  | grep  126b7这个输出为什么没有显示gs_ext_init这个函数呢,但是用readelf -a 过滤就有效,如下所示:

root@speedlinux:/home/test# nm -C 98750.so | cat -n | grep 126b7

   296    000126b7 T tf_rkg_vavg

root@speedlinux:/home/test# readelf -a 98750.so | cat -n | grep 126b7

  1859    0001272c  00011c01 R_386_32          000126b7   gs_ext_init

  1860    00012760  00011c01 R_386_32          000126b7   gs_ext_init

  2160       284: 000126b7  1168 FUNC    GLOBAL DEFAULT   12 gs_ext_init

  2556       358: 000126b7  1168 FUNC    GLOBAL DEFAULT   12 tf_rkg_vavg

root@speedlinux:/home/test# readelf -a 98750.so | cat -n | grep tf_rkg_vavg

  2556       358: 000126b7  1168 FUNC    GLOBAL DEFAULT   12 tf_rkg_vavg

root@speedlinux:/home/test# readelf -a 98750.so | cat -n | grep gs_ext_init

  1859    0001272c  00011c01 R_386_32          000126b7   gs_ext_init

  1860    00012760  00011c01 R_386_32          000126b7   gs_ext_init

  2160       284: 000126b7  1168 FUNC    GLOBAL DEFAULT   12 gs_ext_init

readelf -a 98750.so | cat -n >readelf_a_98750.txt


通过查看readelf -a输出上下文,确认

    Line 1859:   1859    0001272c  00011c01 R_386_32          000126b7   gs_ext_init

    Line 1860:   1860    00012760  00011c01 R_386_32          000126b7   gs_ext_init

这两个包含gs_ext_init的关键字出现在动态库符号的重定位节'.rel.dyn'中,节开头内容如下

   102    Relocation section '.rel.dyn' at offset 0x3a70 contains 1761 entries:

   103     Offset     Info    Type            Sym.Value  Sym. Name

   104    00007781  00000008 R_386_RELATIVE   

   105    000077b3  00000008 R_386_RELATIVE       Line 1859:   1859    0001272c  00011c01 R_386_32          000126b7   gs_ext_init

    Line 1860:   1860    00012760  00011c01 R_386_32          000126b7   gs_ext_init

。。。。

 

    2160       284: 000126b7  1168 FUNC    GLOBAL DEFAULT   12 gs_ext_init

出现在动态库符号节'.dynsym',该节的开头内容如下

  1874    Symbol table '.dynsym' contains 319 entries:

  1875       Num:    Value  Size Type    Bind   Vis      Ndx Name

  1876         0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 

  1877         1: 00000000     0 FUNC    GLOBAL DEFAULT  UND getpagesize@GLIBC_2.0 (2)

  1878         2: 00000000     0 FUNC    GLOBAL DEFAULT  UND __errno_location@GLIBC_2.0 (2)

  1879         3: 00000000     0 FUNC    GLOBAL DEFAULT  UND sprintf@GLIBC_2.0 (2)

  1880         4: 00000000     0 FUNC    GLOBAL DEFAULT  UND srand@GLIBC_2.0 (2)

  1881         5: 00000000     0 FUNC    GLOBAL DEFAULT  UND connect@GLIBC_2.0 (2)

。。。。

而唯一包含tf_rkg_vavg这个函数名称的行,

2556       358: 000126b7  1168 FUNC    GLOBAL DEFAULT   12 tf_rkg_vavg

出现在'.symtab'符号表节中,该节的开头内容如下

  2196    Symbol table '.symtab' contains 365 entries:

  2197       Num:    Value  Size Type    Bind   Vis      Ndx Name

  2198         0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 

  2199         1: 000000f4     0 SECTION LOCAL  DEFAULT    1 

  2200         2: 00000118     0 SECTION LOCAL  DEFAULT    2 

  2201         3: 00000a38     0 SECTION LOCAL  DEFAULT    3 

  2202         4: 000013a0     0 SECTION LOCAL  DEFAULT    4 

。。。。

到这里终于恍然大悟,tf_rkg_vavg这个只是调试符号,所以只出现在.symtab调试符号节中,而调试符号只是程序在调试的时候在用到,是专门给调试器使用的,而程序运行时根本不需要调试符号,而IDA静态分析工具或者gdb调试器当程序中包含符号表的时候会优先用符号表去表示各个函数,就是说程序真正运行时用到的函数名称gs_ext_init被符号表给覆盖了,这也解释了为什么gdb中用gs_ext_init去下断点会提示找不到符号,于是作者利用这一点将所有符号表中的内容乱改一通,程序仍然能够正常运行,但当别人用IDA,gdb调试的时候只能看到被混淆的乱起八糟的函数了。

那么问题来了,能不能让IDA,gdb忽略程序中的调试符号表呢?

找到了一种IDA中显示原始名称的方法,就是在汇编窗口中定位到tf_rkg_vavg这个函数,光标指向它,然后

选择菜单->View->Print internal flags,

在控制台窗口中输出如下:

pub [tf_rkg_vavg] code func xrefd

function does not modify the stack

cmt: Alternative name is 'gs_ext_init'

最后想到用strip可以去除符号信息

root@speedlinux:/home/test# strip 98750.so 

root@speedlinux:/home/test# readelf -S 98750.so 

There are 26 section headers, starting at offset 0x14474:

Section Headers:

  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al

  [ 0]                   NULL            00000000 000000 000000 00      0   0  0

  [ 1] .note.gnu.build-i NOTE            000000f4 0000f4 000024 00   A  0   0  4

  [ 2] .hash             HASH            00000118 000118 000920 04   A  4   0  4

  [ 3] .gnu.hash         GNU_HASH        00000a38 000a38 000968 04   A  4   0  4

  [ 4] .dynsym           DYNSYM          000013a0 0013a0 0013f0 10   A  5   1  4

  [ 5] .dynstr           STRTAB          00002790 002790 001022 00   A  0   0  1

  [ 6] .gnu.version      VERSYM          000037b2 0037b2 00027e 02   A  4   0  2

  [ 7] .gnu.version_r    VERNEED         00003a30 003a30 000040 00   A  5   1  4

  [ 8] .rel.dyn          REL             00003a70 003a70 003708 08   A  4   0  4

  [ 9] .rel.plt          REL             00007178 007178 000018 08   A  4  11  4

  [10] .init             PROGBITS        00007190 007190 000030 00  AX  0   0  4

  [11] .plt              PROGBITS        000071c0 0071c0 000040 04  AX  0   0  4

  [12] .text             PROGBITS        00007200 007200 00b9c8 00  AX  0   0 16

  [13] .fini             PROGBITS        00012bc8 012bc8 00001c 00  AX  0   0  4

  [14] .rodata           PROGBITS        00012be4 012be4 000f0c 00   A  0   0  4

  [15] .eh_frame         PROGBITS        00013af0 013af0 000004 00   A  0   0  4

  [16] .ctors            PROGBITS        00014f04 013f04 000008 00  WA  0   0  4

  [17] .dtors            PROGBITS        00014f0c 013f0c 000008 00  WA  0   0  4

  [18] .jcr              PROGBITS        00014f14 013f14 000004 00  WA  0   0  4

  [19] .dynamic          DYNAMIC         00014f18 013f18 0000d0 08  WA  5   0  4

  [20] .got              PROGBITS        00014fe8 013fe8 00000c 04  WA  0   0  4

  [21] .got.plt          PROGBITS        00014ff4 013ff4 000018 04  WA  0   0  4

  [22] .data             PROGBITS        00015020 014020 000364 00  WA  0   0 32

  [23] .bss              NOBITS          000153a0 014384 00039c 00  WA  0   0 32

  [24] .comment          PROGBITS        00000000 014384 000025 01  MS  0   0  1

  [25] .shstrtab         STRTAB          00000000 0143a9 0000c8 00      0   0  1

Key to Flags:

  W (write), A (alloc), X (execute), M (merge), S (strings)

  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)

  O (extra OS processing required) o (OS specific), p (processor specific)

 对比之前的节信息,如下两个节的信息不见了

[26] .symtab           SYMTAB          00000000 0148e4 0016d0 10     27  47  4

[27] .strtab           STRTAB          00000000 015fb4 001367 00      0   0  1

再次放到IDA里面分析,所有的导出函数都正常了。

而解密后跳转函数ragel_byq的原始名称就是entry_old


最后修改主程序如下

vim unpack.c

源码如下:

#include<stdio.h>

#include<dlfcn.h>

int main()

{

    void *handle1 = dlopen("./98750.so", RTLD_NOW); //程序虽然在同一个目录,必须带./,否则找不到

    if (NULL == handle1)

        fprintf(stderr, "Failed to open!\n");

    int (* f)()=(int(*)()) dlsym(handle1, "gs_ext_init");

    if (f != NULL)

        f();

    dlclose(handle1);

    return 0;

}

编译

gcc -g -o unpack unpack.c -ldl

然后用gdb调试对gs_ext_init下断点啥的都正常了,如下图所示:

root@speedlinux:/home/test# vim unpack.c

root@speedlinux:/home/test# gcc -g -o unpack unpack.c -ldl

root@speedlinux:/home/test# gdb unpack

GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04

Copyright (C) 2012 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law.  Type "show copying"

and "show warranty" for details.

This GDB was configured as "i686-linux-gnu".

For bug reporting instructions, please see:

<http://bugs.launchpad.net/gdb-linaro/>...

Reading symbols from /home/test/unpack...done.

(gdb) b main

Breakpoint 1 at 0x804850d: file unpack.c, line 5.

(gdb) r

Starting program: /home/test/unpack 

Breakpoint 1, main () at unpack.c:5

5        void *handle1 = dlopen("./98750.so", RTLD_NOW); //程序虽然在同一个目录,必须带./,否则找不到

(gdb) n

6        if (NULL == handle1)

(gdb) n

8        int (* f)()=(int(*)()) dlsym(handle1, "gs_ext_init");

(gdb) n

9        if (f != NULL)

(gdb) p f

$1 = (int (*)()) 0xb7e1f6b7 <gs_ext_init>

(gdb) b gs_ext_init

Breakpoint 2 at 0xb7e1f6bb

(gdb) d 2

(gdb) b ragel_byq    

Function "ragel_byq" not defined.

Make breakpoint pending on future shared library load? (y or [n]) n

(gdb) b entry_old

Breakpoint 3 at 0xb7e1f025

(gdb) c

Continuing.

Breakpoint 3, 0xb7e1f025 in entry_old () from ./98750.so

(gdb) set disassembly-flavor intel

(gdb) disas

Dump of assembler code for function entry_old:

=> 0xb7e1f025 <+0>:    addr16 mov ebp,esp

   0xb7e1f028 <+3>:    push   ebx

   0xb7e1f029 <+4>:    sub    esp,0x34

   0xb7e1f02c <+7>:    mov    DWORD PTR [ebp-0x18],0x0

   0xb7e1f033 <+14>:    mov    DWORD PTR [ebp-0x10],0x0

   0xb7e1f03a <+21>:    mov    DWORD PTR [ebp-0xc],0x0

   0xb7e1f041 <+28>:    mov    DWORD PTR [esp],0xb7e20ac7

   0xb7e1f048 <+35>:    call   0xb7e8b830 <_IO_puts>

   0xb7e1f04d <+40>:    mov    DWORD PTR [esp],0x1000000

   0xb7e1f054 <+47>:    call   0xb7e9dec0 <__GI___libc_malloc>

   0xb7e1f059 <+52>:    mov    ds:0xb7e223b4,eax

   0xb7e1f05e <+57>:    mov    eax,ds:0xb7e223b4

   0xb7e1f063 <+62>:    mov    DWORD PTR [esp+0x8],0x1000000

   0xb7e1f06b <+70>:    mov    DWORD PTR [esp+0x4],0x0

   0xb7e1f073 <+78>:    mov    DWORD PTR [esp],eax

   0xb7e1f076 <+81>:    call   0xb7f597f0 <__memset_sse2_rep>

   0xb7e1f07b <+86>:    mov    DWORD PTR [esp],0x61c

   0xb7e1f082 <+93>:    call   0xb7e9dec0 <__GI___libc_malloc>

   0xb7e1f087 <+98>:    mov    ds:0xb7e223b8,eax

   0xb7e1f08c <+103>:    mov    eax,ds:0xb7e223b8

   0xb7e1f091 <+108>:    mov    DWORD PTR [esp+0x8],0x61c

   0xb7e1f099 <+116>:    mov    DWORD PTR [esp+0x4],0x0

   0xb7e1f0a1 <+124>:    mov    DWORD PTR [esp],eax

   0xb7e1f0a4 <+127>:    call   0xb7f597f0 <__memset_sse2_rep>

   0xb7e1f0a9 <+132>:    mov    DWORD PTR ds:0xb7e2264c,0x80564f4

。。。。

补充:可以用查看so动态库导出函数)方法三:objdump -T 98750.so (详细自己看说明了)

然后就是分析破解的问题了,破解方法有两种方法:

破解方法一:DUMP出来分析,然后修复重定位项、修改入口地址等导致运行错误的问题

破解方法二:给程序打补丁,由于程序是动态解密的,因此必须代码解密后动态给程序打内存补丁。


part 2 利用LD_PRELOAD先加载并且可以Hook同名函数来patch程序达到破解的目的

上面附件中有两个附加你中要用到的配置文件,gsext.ini和tzgs1.ini,将这两个配置文件、98750.so和unpack程序同一个目录下,然后运行unpack程序,运行输出报错如下:

root@cjy-virtual-machine:~/test# ./unpack 

 ²̄馳_ext_init() ok.

MCODEn: [0x68,0x65,0x43,0x43,0x99,0xAC,0xAE,0xC7,0x71,0xCD,0xD3,0x66,0xB1,0x23]

MCODE2=43996871CDC766D3B1

MCODE0: [0x68,0x65,0x43,0x5D,0x4B,0x27,0xAE,0xC7,0x71,0xCD,0xC1,0x96,0xDF,0x21]


[注意]APP应用上架合规检测服务,协助应用顺利上架!

最后于 2022-5-2 12:54 被cjycjw编辑 ,原因:
上传的附件:
收藏
免费 4
支持
分享
最新回复 (4)
雪    币: 576
活跃值: (2035)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享经验
2022-5-2 15:21
0
雪    币: 1
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
大神,解密后的能不能分享一份?万分感谢
2022-12-16 19:38
0
雪    币: 88
活跃值: (69)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
你说他是乱改的,他可不是乱改的啊doge 他这个函数名混淆 就是一个rot13
给楼主赞一个
2022-12-22 17:59
0
游客
登录 | 注册 方可回帖
返回
//