-
-
[原创]Linux符号混淆和LD_PRELOAD技术的动态补丁破解linux动态库(98750.so)
-
发表于: 2017-3-23 21:34 14094
-
以下为本人原创,未经允许请勿随意转载。
Part 1 - 利用IDA、objdump、gdb等静态分析或调试器优先加载符号表的特性,从而通过修改节.symtab中符号字符信息来欺骗这些工具)
这个样本是某个主程序调用的动态库,我之前分析了很多 次,用IDA打开查看导出函数所有的函数名称都是乱起八糟没有意义的,之前以为是所有的函数名称都被加密混淆了。同时我也用IDA和gdb调试了很多次,知道这个样本中的代码是加密的,运行的时候会动态解密,解密函数名称为tf_rkg_vavg, 这个函数内部会调用mprotect并递归调用自己来解密代码,解密成功后这个函数在末尾会跳转到已解密的ragel_byq去执行。其中ragel_byq函数是被加密的,反汇编显示的是乱起八糟的汇编指令,如下图所示。
ragel_byq函数解密前
之前调试的时候一直都是在主程序下调试,然后这个动态库里面下断点,由于主程序很大所以很不方便,于是想直接写个Demo去调用这个动态库,然后去解密看是否可行。
vim unpack.c 源码如下: #include #include 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 |
网上查了一下查看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 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 ... Report bugs to <http://www.sourceware.org/bugzilla/> |
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 。。。 [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 |
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) 。。。。 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 。。。 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 。。。 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调试的时候只能看到被混淆的乱起八糟的函数了。
(TDOO:)那么问题来了,能不能让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 。。。 [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 #include 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 |
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> |
破解方法一:DUMP出来分析,然后修复重定位项、修改入口地址等导致运行错误的问题
破解方法二:给程序打补丁,由于程序是动态解密的,因此必须代码解密后动态给程序打内存补丁。下面part 2可以用
补充:
(查看so动态库导出函数)方法三:objdump -T 98750.so (详细自己看说明了)
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] errcode=1 |
²̄馳_ext_init() ok. configBlock->CKInterval=20 configBlock->SwitchITEX=1 configBlock->guildwaritemid=28889 configBlock->SongRate=1.000000 configBlock->Song2Rate=10.000000 configBlock->PosX1=218 configBlock->PosY1=-554 configBlock->PosX2=328 configBlock->PosY2=-488 configBlock->SongExRate=30.000000 configBlock->PosExX1=2143 configBlock->PosExY1=2400 configBlock->PosExX2=2206 configBlock->PosExY2=2463 configBlock->HourEx1=20 configBlock->HourEx2=21 configBlock->HourEx3=0 configBlock->SwitchRTEX=1 configBlock->AddPointBase=1 configBlock->DropColor=4 configBlock->SimithSC=8 configBlock->SimithFL=11 configBlock->SwitchLogin=0 configBlock->DBAccount= configBlock->lxcmd=xiaojiba |
root@cjy-virtual-machine:~/test# gdb ./unpack (gdb) b write Function "write" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (write) pending. (gdb) r Starting program: /root/test/unpack Breakpoint 1, 0xb7efb1b0 in write () from /lib/i386-linux-gnu/libc.so.6 (gdb) c Continuing. Breakpoint 1, 0xb7efb1b0 in write () from /lib/i386-linux-gnu/libc.so.6 (gdb) c Continuing. Breakpoint 1, 0xb7efb1b0 in write () from /lib/i386-linux-gnu/libc.so.6 (gdb) c Continuing. ²̄馳_ext_init() ok. Breakpoint 1, 0xb7efb1b0 in write () from /lib/i386-linux-gnu/libc.so.6 (gdb) c Continuing. Breakpoint 1, 0xb7efb1b0 in write () from /lib/i386-linux-gnu/libc.so.6 (gdb) c Continuing. MCODEn: [0x68,0x65,0x43,0x43,0x99,0xAC,0xAE,0xC7,0x71,0xCD,0xD3,0x66,0xB1,0x23] Breakpoint 1, 0xb7efb1b0 in write () from /lib/i386-linux-gnu/libc.so.6 (gdb) bt #0 0xb7efb1b0 in write () from /lib/i386-linux-gnu/libc.so.6 #1 0xb7e8fb05 in _IO_file_write () from /lib/i386-linux-gnu/libc.so.6 #2 0xb7e8f9e4 in ?? () from /lib/i386-linux-gnu/libc.so.6 #3 0xb7e9105e in _IO_do_write () from /lib/i386-linux-gnu/libc.so.6 #4 0xb7e91415 in _IO_file_overflow () from /lib/i386-linux-gnu/libc.so.6 #5 0xb7e905a3 in _IO_file_xsputn () from /lib/i386-linux-gnu/libc.so.6 #6 0xb7e62504 in vfprintf () from /lib/i386-linux-gnu/libc.so.6 #7 0xb7e6bc2f in printf () from /lib/i386-linux-gnu/libc.so.6 #8 0xb7e1af25 in dopredoinfo () from ./98750.so #9 0xb7e1b16c in entry_old () from ./98750.so #10 0xb7e1bb3e in gs_ext_init () from ./98750.so #11 0x08048757 in main () at unpack.c:51 (gdb) set disassembly-flavor intel (gdb) disas dopredoinfo Dump of assembler code for function dopredoinfo: 0xb7e1accc <+0>: push ebp 0xb7e1accd <+1>: mov ebp,esp 0xb7e1accf <+3>: push edi 0xb7e1acd0 <+4>: push esi 0xb7e1acd1 <+5>: push ebx 0xb7e1acd2 <+6>: sub esp,0xbc 0xb7e1acd8 <+12>: mov DWORD PTR [ebp-0x1c],0x0 。。。。。 0xb7e1ad5e <+146>: mov DWORD PTR [esp+0x4],eax 0xb7e1ad62 <+150>: mov DWORD PTR [esp],0xb7e1e718 0xb7e1ad69 <+157>: call 0xb7f4e770 0xb7e1ad6e <+162>: test eax,eax // 未破解,$eax=-1 0xb7e1ad70 <+164>: je 0xb7e1b015 0xb7e1ad76 <+170>: movzx eax,BYTE PTR ds:0xb7e1e725 0xb7e1ad7d <+177>: movzx edi,al 0xb7e1ad80 <+180>: movzx eax,BYTE PTR ds:0xb7e1e724 0xb7e1ad87 <+187>: movzx eax,al 。。。。。。 0xb7e1b002 <+822>: mov DWORD PTR [esp],0xb7e1cabd 0xb7e1b009 <+829>: call 0xb7e866a0 0xb7e1b00e <+834>: mov eax,0x1 0xb7e1b013 <+839>: jmp 0xb7e1b01a 0xb7e1b015 <+841>: mov eax,0x0 //正常的用户从函数164偏移处直接跳到这边 0xb7e1b01a <+846>: add esp,0xbc 0xb7e1b020 <+852>: pop ebx 0xb7e1b021 <+853>: pop esi 0xb7e1b022 <+854>: pop edi 0xb7e1b023 <+855>: pop ebp 0xb7e1b024 <+856>: ret End of assembler dump. |
从以上信息知道,最简单的patch方法有,在偏移162处eax的值改成0,或者将164将jz改成jnz。先将162处eax改成0看是否能破解成功。
我们编写一个动态库导出同名函数名称entry_old,然后将库地址赋值给LD_PRELOAD环境变量,这样我们就hook了程序中的entry_old函数,当程序调用entry_old函数的时候,自动变成调用我们的entry_old函数,因为程序调用entry_old时所有的加密代码都已经解密,所以直接在我们的entry_old里面patch目标破解代码,然后再调用原始的entry_old入口函数即可。
这边我们直接将test eax,eax 两个自己的指令换成 xor eax,eax两个字节的指令,这两个指令对应的16进制数据在OD里面输入后,结果是xor eax,eax对应的指令汇编为:33C0或者31C0, 如下图所示:
编写代码测试如下:
vim libpatch.c #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include int entry_old(void) { printf("My Patch Success!\n"); void* handle = (void*)dlopen("./98750.so", RTLD_NOW | RTLD_GLOBAL | RTLD_NOLOAD); //不确定entry_oly是否会被多次调用,先RTLD_NOLOAD标识判断是否已经加载,注意RTLD_NOLOAD不能单独使用,必须与其他标识或操作 if (handle == NULL) { handle = dlopen("./98750.so", RTLD_NOW | RTLD_GLOBAL); //如果没有加载过,这边就加载 if (handle == NULL) { printf("Load library error!\n"); return 0; } } uint8_t code_orig1[2]; void* dopredoinfo_addr = NULL; dopredoinfo_addr=dlsym(handle, "dopredoinfo"); if (dopredoinfo_addr == NULL) { printf("get dopredoinfo address failed!\n"); } uint8_t code1[] = {0x31,0xc0}; //=xor eax,eax memcpy(code_orig1, dopredoinfo_addr+162, 2); memcpy(dopredoinfo_addr+162,code1, 2); //打补丁 int (* f)()=(int(*)(void)) dlsym(handle, "entry_old"); //调用原始的认证函数 int ret = f(); dlclose(handle); memcpy(dopredoinfo_addr+162,code_orig1, 2); //还原为原始函数防止后期程序中有代码校验 return ret; } gcc -g3 -fPIC -shared -o libpatch.so libpatch.c LD_PRELOAD=./libpatch.so ./unpack 输出如下(报了另外一个错误): My Patch Success! ²̄馳_ext_init() ok. Gethostname error, Connection timed out check error! errcode=2 |
以下根据输出信息gdb调试跟踪第二处需要破解的地方。
root@cjy-virtual-machine:~/test# LD_PRELOAD=./libpatch.so gdb ./unpack (gdb) b write Function "write" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (write) pending. (gdb) r Starting program: /root/test/unpack Breakpoint 1, 0xb7ef71b0 in write () from /lib/i386-linux-gnu/libc.so.6 (gdb) c Continuing. My Patch Success! Breakpoint 1, 0xb7ef71b0 in write () from /lib/i386-linux-gnu/libc.so.6 (gdb) c Continuing. ²̄馳_ext_init() ok. Breakpoint 1, 0xb7ef71b0 in write () from /lib/i386-linux-gnu/libc.so.6 (gdb) c Continuing. Breakpoint 1, 0xb7ef71b0 in write () from /lib/i386-linux-gnu/libc.so. (gdb) print *(char**)($esp+8) $3 = 0xbfffcd18 "Connect Error:Connection refused\n c.9133xy.com." (gdb) x/s *(char**)($esp+8) 0xbfffcd18: "Connect Error:Connection refused\n c.9133xy.com." (gdb) x/s *(int*)($esp+8) 0xbfffcd18: "Connect Error:Connection refused\n c.9133xy.com." (gdb) print "%s\n",*(char**)($esp+8) $5 = 0xbfffcd18 "Connect Error:Connection refused\n c.9133xy.com." (gdb) bt #0 0xb7ef71b0 in write () from /lib/i386-linux-gnu/libc.so.6 #1 0xb7e8bb05 in _IO_file_write () from /lib/i386-linux-gnu/libc.so.6 #2 0xb7e8b9e4 in ?? () from /lib/i386-linux-gnu/libc.so.6 #3 0xb7e8c6fa in _IO_file_xsputn () from /lib/i386-linux-gnu/libc.so. #4 0xb7e62f07 in ?? () from /lib/i386-linux-gnu/libc.so.6 #5 0xb7e5db85 in vfprintf () from /lib/i386-linux-gnu/libc.so.6 #6 0xb7e67bef in fprintf () from /lib/i386-linux-gnu/libc.so.6 #7 0xb7e0fad5 in http_request () from ./98750.so #8 0xb7e0fd91 in sn_time_chk () from ./98750.so #9 0xb7e18179 in entry_old () from ./98750.so #10 0xb7fd85de in entry_old () at libpatch.c:32 #11 0xb7e18b3e in gs_ext_init () from ./98750.so #12 0x08048757 in main () at unpack.c:51 从上面的堆栈看出错误输出在 entry_old ()->sn_time_chk()-> http_request() (gdb) set disassembly-flavor intel (gdb) disas http_request Dump of assembler code for function http_request: 0xb7e0f8fc <+0>: push ebp 0xb7e0f8fd <+1>: mov ebp,esp 0xb7e0f8ff <+3>: push ebx 0xb7e0f900 <+4>: sub esp,0x184 0xb7e0f906 <+10>: mov DWORD PTR [ebp-0x20],0x0 0xb7e0f90d <+17>: mov DWORD PTR [ebp-0x15f],0x2e63696c 0xb7e0f917 <+27>: mov DWORD PTR [ebp-0x15b],0x33333139 0xb7e0f921 <+37>: mov DWORD PTR [ebp-0x157],0x632e7978 0xb7e0f92b <+47>: mov WORD PTR [ebp-0x153],0x6d6f 。。。 。。。 0xb7e0fd20 <+1060>: add eax,0x4 0xb7e0fd23 <+1063>: mov DWORD PTR [esp+0x4],eax 0xb7e0fd27 <+1067>: mov eax,DWORD PTR [ebp+0x8] 0xb7e0fd2a <+1070>: mov DWORD PTR [esp],eax 0xb7e0fd2d <+1073>: call 0xb7f4e280 0xb7e0fd32 <+1078>: mov eax,0x0 0xb7e0fd37 <+1083>: add esp,0x184 0xb7e0fd3d <+1089>: pop ebx 0xb7e0fd3e <+1090>: pop ebp 0xb7e0fd3f <+1091>: ret (gdb) disas sn_time_chk Dump of assembler code for function sn_time_chk: 0xb7e0fd40 <+0>: push ebp 0xb7e0fd41 <+1>: mov ebp,esp 0xb7e0fd43 <+3>: sub esp,0x38 0xb7e0fd46 <+6>: call 0xb7f00800 0xb7e0fd4b <+11>: mov DWORD PTR [ebp-0x18],eax 0xb7e0fd4e <+14>: mov DWORD PTR [ebp-0x14],0xb7e0f106 0xb7e0fd55 <+21>: mov DWORD PTR [ebp-0x10],0x0 0xb7e0fd5c <+28>: mov DWORD PTR [ebp-0xc],0x0 0xb7e0fd63 <+35>: mov DWORD PTR [esp+0x8],0xe 0xb7e0fd6b <+43>: mov DWORD PTR [esp+0x4],0xb7e1b718 0xb7e0fd73 <+51>: lea eax,[ebp-0x26] 0xb7e0fd76 <+54>: mov DWORD PTR [esp],eax 0xb7e0fd79 <+57>: call 0xb7f4e280 0xb7e0fd7e <+62>: lea eax,[ebp-0x26] 0xb7e0fd81 <+65>: mov DWORD PTR [esp+0x4],0xe 0xb7e0fd89 <+73>: mov DWORD PTR [esp],eax 0xb7e0fd8c <+76>: call 0xb7e0f8fc 0xb7e0fd91 <+81>: test eax,eax 0xb7e0fd93 <+83>: je 0xb7e0fda8 0xb7e0fd95 <+85>: mov DWORD PTR [esp],0xb7e18e8a 0xb7e0fd9c <+92>: call 0xb7e826a0 ---Type 0xb7e0fda1 <+97>: mov eax,0x1 0xb7e0fda6 <+102>: jmp 0xb7e0fe12 0xb7e0fda8 <+104>: mov eax,DWORD PTR [ebp-0x14] 0xb7e0fdab <+107>: mov edx,0x0 0xb7e0fdb0 <+112>: div DWORD PTR [ebp-0x18] 0xb7e0fdb3 <+115>: imul eax,DWORD PTR [ebp-0x18] 0xb7e0fdb7 <+119>: mov DWORD PTR [esp+0x8],0x7 0xb7e0fdbf <+127>: mov edx,DWORD PTR [ebp-0x18] 0xb7e0fdc2 <+130>: mov DWORD PTR [esp+0x4],edx 0xb7e0fdc6 <+134>: mov DWORD PTR [esp],eax 0xb7e0fdc9 <+137>: call 0xb7f03f10 0xb7e0fdce <+142>: mov DWORD PTR [ebp-0x10],0x0 0xb7e0fdd5 <+149>: jmp 0xb7e0fe07 0xb7e0fdd7 <+151>: mov eax,DWORD PTR [ebp-0x10] 0xb7e0fdda <+154>: add eax,DWORD PTR [ebp-0x14] 0xb7e0fddd <+157>: mov edx,DWORD PTR [ebp-0x10] 0xb7e0fde0 <+160>: add edx,DWORD PTR [ebp-0x14] 0xb7e0fde3 <+163>: movzx ecx,BYTE PTR [edx] 0xb7e0fde6 <+166>: mov edx,DWORD PTR [ebp-0xc] 0xb7e0fde9 <+169>: movzx edx,BYTE PTR [ebp+edx*1-0x26] 0xb7e0fdee <+174>: xor edx,ecx 0xb7e0fdf0 <+176>: mov BYTE PTR [eax],dl ---Type 0xb7e0fdf2 <+178>: add DWORD PTR [ebp-0xc],0x1 0xb7e0fdf6 <+182>: cmp DWORD PTR [ebp-0xc],0xe 0xb7e0fdfa <+186>: jne 0xb7e0fe03 0xb7e0fdfc <+188>: mov DWORD PTR [ebp-0xc],0x0 0xb7e0fe03 <+195>: add DWORD PTR [ebp-0x10],0x1 0xb7e0fe07 <+199>: cmp DWORD PTR [ebp-0x10],0x1f 0xb7e0fe0b <+203>: jle 0xb7e0fdd7 0xb7e0fe0d <+205>: mov eax,0x0 0xb7e0fe12 <+210>: leave 0xb7e0fe13 <+211>: ret End of assembler dump. 简单分析一下http_request和sn_time_chk函数,可以知道,sn_time_chk调用http_request,并且http_request必须返回0,才正确,此时sn_time_chk也返回0 |
即,将如下函数结尾汇编代码
0xb7e0fd32 <+1078>: mov eax,0x0
0xb7e0fd37 <+1083>: add esp,0x184
0xb7e0fd3d <+1089>: pop ebx
0xb7e0fd3e <+1090>: pop ebp
0xb7e0fd3f <+1091>: ret
0xb7e0f8fc <+0>: push ebp
0xb7e0f8fd <+1>: mov ebp,esp
0xb7e0f8ff <+3>: push ebx
0xb7e0f900 <+4>: sub esp,0x184
这样子函数返回0,堆栈也是平衡的。于是在原来patch的地方再增加一个patch,最终修改代码如下:
vim libpatch.c #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include int entry_old(void) { printf("My Patch Success!\n"); void* handle = (void*)dlopen("./98750.so", RTLD_NOW | RTLD_GLOBAL | RTLD_NOLOAD); //不确定entry_oly是否会被多次调用,先RTLD_NOLOAD标识判断是否已经加载,注意RTLD_NOLOAD不能单独使用,必须与其他标识或操作 if (handle == NULL) { handle = dlopen("./98750.so", RTLD_NOW | RTLD_GLOBAL); //如果没有加载过,这边就加载 if (handle == NULL) { printf("Load library error!\n"); return 0; } } int (* f)() = NULL; int ret = 0; int patched = 0; uint8_t code_orig1[2]; uint8_t code_orig2[14]; void* dopredoinfo_addr = NULL; void* http_request_addr = NULL; dopredoinfo_addr=dlsym(handle, "dopredoinfo"); http_request_addr = dlsym(handle, "http_request"); if (dopredoinfo_addr == NULL) { printf("get dopredoinfo address failed!\n"); goto ENTRY_OLD; } if (http_request_addr == NULL) { printf("get http_request address failed!\n"); goto ENTRY_OLD; } uint8_t code1[] = {0x31,0xc0}; //=xor eax,eax memcpy(code_orig1, dopredoinfo_addr+162, 2); //备份原始数据 memcpy(code_orig2, http_request_addr+10,14); memcpy(dopredoinfo_addr+162,code1, 2); //打补丁 memcpy(http_request_addr+10,http_request_addr+1078,14); patched = 1; ENTRY_OLD: f = (int(*)(void)) dlsym(handle, "entry_old"); //调用原始的认证函数 ret = f(); dlclose(handle); if (patched) { memcpy(dopredoinfo_addr+162,code_orig1, 2); //还原为原始函数防止后期程序中有代码校验 memcpy(http_request_addr+10,code_orig2, 14); } return ret; } gcc -g3 -fPIC -shared -o libpatch.so libpatch.c root@cjy-virtual-machine:~/test# LD_PRELAOD=./libpatch.so ./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,0xC7,0x2E,0x8E,0xAE,0xC7,0x71,0xCA,0xC1,0x96,0xDF,0x21] errcode=1 root@cjy-virtual-machine:~/test# LD_PRELOAD=./libpatch.so ./unpack My Patch Success! ²̄馳_ext_init() ok. configBlock->CKInterval=20 configBlock->SwitchITEX=1 configBlock->guildwaritemid=28889 configBlock->SongRate=1.000000 configBlock->Song2Rate=10.000000 configBlock->PosX1=218 configBlock->PosY1=-554 configBlock->PosX2=328 configBlock->PosY2=-488 configBlock->SongExRate=30.000000 configBlock->PosExX1=2143 configBlock->PosExY1=2400 configBlock->PosExX2=2206 configBlock->PosExY2=2463 configBlock->HourEx1=20 configBlock->HourEx2=21 configBlock->HourEx3=0 configBlock->SwitchRTEX=1 configBlock->AddPointBase=1 configBlock->DropColor=4 configBlock->SimithSC=8 configBlock->SimithFL=11 configBlock->SwitchLogin=0 configBlock->DBAccount= configBlock->lxcmd=xiaojiba hcolor=3 hrates:100,100,100, maxsml=10 lrates:100,100,100,80,70,70,60,50,40,35,10,10,10,10,10, sublev:0,0,0,0,0,0,0,0,0,0,5,5,5,5,5, latt1:0.020000,0.040000,0.060000,0.080000,0.120000,0.160000,0.200000,0.300000,0.400000,0.600000,0.600000,0.700000,0.800000,0.900000,1.000000, latt2:0.020000,0.040000,0.060000,0.080000,0.120000,0.160000,0.200000,0.300000,0.400000,0.600000,0.600000,0.700000,0.800000,0.900000,1.000000, latt3:20,40,60,80,120,160,200,300,400,500,500,500,500,500,500, latt4:20,40,60,80,120,160,200,300,400,500,500,500,500,500,500, st_1:10,30,50,80,110,150,90,130,180, st_2:10,30,50,80,110,150,90,130,180, st_3:0.010000,0.010000,0.020000,0.030000,0.040000,0.070000,0.030000,0.040000,0.070000, st_4:0.000000,0.000000,0.000000,0.010000,0.010000,0.030000,0.010000,0.010000,0.030000, st_5:10,30,50,80,110,150,90,130,180, st_6:10,20,30,100,200,400,200,300,600, st_7:10,30,50,80,110,150,90,130,180, lh_01:100,120,150,180,210,240,270, lh_02:10,12,15,18,21,24,27, lh_03:100,120,150,180,210,240,270, lh_04:1,2,3,4,5,6,7, lh_05:100,120,150,180,210,240,270, lh_06:10,12,15,18,21,24,27, lh_11:10,12,15,18,21,24,27, lh_12:1,2,3,4,5,6,7, 段错误 (核心已转储) root@cjy-virtual-machine:~/test# |
Part3 完全终极破解
先给出最终破解的LD_PREOLOAD动态库hook源程序
vim libx.c #include #include #include #include int http_request(void* mac, size_t size) { int i; for (i = 0; i <= 13; ++i) { *((uint8_t*)mac + i) ^= (uint8_t)0x32u; } return 0; } int GetMac(char *s) { uint8_t mac[14] = {0x00,0x0c,0x29,0x36,0x27,0x4a,0xc0,0xa8,0x01,0xbc,0xb3,0xe5,0xab,0x54}; memcpy(s, mac, 14); return 0; } gcc -shared -o libx.so -fPIC libx.c root@speedlinux:/home/test# LD_PRELOAD=./libx.so ./unpack ²̄馳_ext_init() ok. configBlock->CKInterval=20 configBlock->SwitchITEX=1 configBlock->guildwaritemid=28889 configBlock->SongRate=1.000000 configBlock->Song2Rate=10.000000 configBlock->PosX1=218 configBlock->PosY1=-554 configBlock->PosX2=328 configBlock->PosY2=-488 configBlock->SongExRate=30.000000 configBlock->PosExX1=2143 configBlock->PosExY1=2400 configBlock->PosExX2=2206 configBlock->PosExY2=2463 configBlock->HourEx1=20 configBlock->HourEx2=21 configBlock->HourEx3=0 configBlock->SwitchRTEX=1 configBlock->AddPointBase=1 configBlock->DropColor=4 configBlock->SimithSC=8 configBlock->SimithFL=11 configBlock->SwitchLogin=0 configBlock->DBAccount= configBlock->lxcmd=xiaojiba root@speedlinux:/home/test# |
借助IDA进行分析步骤如下:
(1)IDA本地打开去除符号的98750.so
(2)将IDA目录下dbgsrv子目录下的linux_server拷贝到linux, chmod +x linux_server 并运行起来
(3)IDA->Debugger->Process Options..设置调试参数
(4)在Exports导出函数窗口中找到entry_old函数,并然后找到引用该函数的地方,并F2下断点
(5)F9运行起来,提示运行程序风险,选择Yes, 然后弹出的Add map..窗口,全部选择Cancel( 当然也可以将对应用到的动态库从linux拷贝到windows目录下,然后指定映射关系,这版其他的库我们不关心也不用在IDA打开里面分析,全部取消即可)
遇到98750.so会提示是否是一样的动态库,选择same
运行来到断点处,并且程序代码全部解密完成
(6)F7步入函数显示的都是乱码,如下所示
(7)解决乱码,转化汇编代码或伪代码
光标停在函数的第一行,然后菜单->Edit->Begin selection
向下不断拖动,知道看到另外一个函数的名称,说明这个函数结束了,然后按一下C,并选择Analyze,将选择的区域转化成代码(当然也可以一点一点按C将指定区域转化成代码但是这样子太慢了)
转化后仍然有部分没有正确转化成代码,被分隔线分隔,如下是开头的分隔线
如下是函数结尾部分的分割线
将分隔线之间的数据选中,按C选择Force强制转化成代码
此时又将大部分的数据转化成为了代码,多了更多的分隔线,但仍然还有部分的分隔线间的数据没有被转化成为代码,重复以上转化,知道该函数内的所有的数据转化成代码。(如果数据只有一小块,并没有多余一个屏幕,可以按Shift选择,然后按C转化,或者直接将光标定位到数据的开头部分,按C,通常IDA会自动将后面的数据转化成代码)
全部转化成代码后,重新将光标定位到entry_old函数开头部分(或者public entry_old)字符名称上,然后右键->Create function...创建函数
此时通常会出现局部变量或者参数
然后光标停在函数内部,按F5查看函数的伪代码,如下函数显示不全啊
返回到汇编函数,观察函数提早结束的地方,entry_old函数尾部被IDA识别错误,提早结束了,如下所示
解决办法就是将光标停在正真函数结尾的地方,retn指令上,然后菜单->Edit->Functions->Set function end 设置真正的函数结束位置
设置后立即多了函数结束标识符
然后重新按F5,查看伪代码,函数仍然提早结束了,如下所示
查看伪代码发现原来是分隔线导致(TODO:这个分割线是什么,是如何产生,是如何消除)
这个应该是反编译插件的BUG了,目前没有比较好的解决办法,按照如下方法有时候可以去除这个分隔线。
将分割线选中后者选中前一个汇编指令,然后Undefine,然后再次按C转化成代码,如果无法去除就没办法了,部分分隔线也不会影响反汇编,如果遇到影响反汇编的分割线又去不掉的话,那就直接看汇编窗口,不要看伪代码吧。
通过undefine并code去掉部分分隔线之后,伪代码如下。
可以看出dopredoinfo和sn_time_chk都必须返回0, 另外mac地址信息也参数运算。
下面重点分析dopredoinfo和sn_time_chk这个两个信息和所有对mac地址的引用。
注:对于其他的无法显示的函数汇编代码,错误识别成数据的部分全部可以采用上面的方法进行处理。后面不在描述处理过程,都是转化后的代码。
(8)在分析前先获取解密后的程序快照,这样子下次就不用重新运行动态解密代码,就可以直接分析了。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
他的文章
谁下载
无
看原图
赞赏
雪币:
留言: