Hopper+LLDB+Radare2 动态调试教程
本帖最后由 zyjsuper 于 2020-5-23 19:09 编辑目录结构如下,本帖之摘录了几个部分,完整版移步:
Hopper+LLDB+Radare2动态调试教程.pdf https://pan.wps.cn/l/s5RHeje4k
目录 教程背景: 2第一部分 Hopper disassember动态调试: 3第二部分 LLDB动态调试 14第三部分 使用Radare2动态调试 27第四部分 分析整理算法 32第五部分 使用Python编写keygen 34第六部分 使用Swift编写keygen 35
教程背景:
在Youtube闲逛时,无意间发现了飘云阁飞树大神(tree_fly)的频道,地址如下:
https://www.youtube.com/channel/UCcyUJWGHkxVtA59143ojUxA
其中一个教程感觉挺经典,链接地址为:https://www.youtube.com/watch?v=pfteHwOmQIM
教程中使用的Crackme地址为:https://reverse.put.as/wp-content/uploads/2010/05/3-Fox.zip
此教程中详细介绍了使用Hopper和LLDB来动态调试程序,爆破注册流程,并且给出如何提取算法以及编写注册机,因为算法相对来讲比较简单,所以适合菜鸟练手使用。于是乎我亲自操作了一遍,觉得有必要分享给菜菜们,本教程中Hopper和LLDB动态调试以及使用Swift编写注册机属完全拷贝大神的操作,如果飞树大神觉得我有侵权行为,请提示删帖。
第三部分 使用Radare2动态调试
使用r2命令调试程序,-d参数进入调试模式。
-A run 'aaa' command to analyze all referenced code
-d debug the executable 'file' or running process 'pid'
zyjsupers-MacBook-Air:~ zyjsuper$ r2 -Ad ~/Desktop/3-Fox/Fox.app/Contents/MacOS/Fox
= attach 2181 2181
bin.baddr 0x00001000
Using 0x1000
asm.bits 32
Analyze all flags starting with sym. and entry0 (aa)
[Warning: Invalid range. Use different search.in=? or anal.in=dbg.maps.x
Warning: Invalid range. Use different search.in=? or anal.in=dbg.maps.x
Analyze function calls (aac)
Analyze len bytes of instructions for references (aar)
Check for objc references
Check for vtables
[TOFIX: aaft can't run in debugger mode.ions (aaft)
Type matching analysis for all functions (aaft)
Use -AA or aaaa to perform additional experimental analysis.
-- ♥ --
> afl #显示函数列表
0x00002698 3 63 entry0
0x000026fa 1 9 main
0x00002bd4 1 6 sym.imp.exit
0x00004068 99 4716 -> 4803 sym..objc_class_name_FoxAppDelegate
0x00003104 6 3924 sym._NXArgc
0x00003100 6 3928 sym._NXArgv
0x000030f8 6 3936 sym.___progname
0x00001000 1 1 map.Fox.r_x
0x000026d8 1 20 sym.dyld_stub_binding_helper
0x000026ec 1 14 sym.__dyld_func_lookup
0x00002703 1 5 sym.public_int_FoxAppDelegate::applicationDidFinishLaunching_int
0x00002708 1 11 sym.public_int_FoxAppDelegate::window
0x00002713 1 14 sym.public_int_FoxAppDelegate::setWindow_int
0x00002721 1 76 sym.public_int_FoxAppDelegate::validateSerial_int__int
0x00002bda 1 6 sym.imp.objc_msgSend
0x0000276d 1 47 sym.public_int_FoxAppDelegate::awakeFromNib
0x0000279c 4 575 sym.public_int_FoxAppDelegate::openLicenseFile_int
0x000029db 1 60 sym.public_int_NSString_CocoaCryptoHashing_::sha1HexHash
0x00002a17 1 60 sym.public_int_NSString_CocoaCryptoHashing_::sha1Hash
0x00002a53 3 143 sym.public_int_NSData_CocoaCryptoHashing_::sha1Hash
0x00002bb6 1 6 sym.imp.CC_SHA1
0x00002bce 1 6 sym.imp.__stack_chk_fail
0x00002ae2 5 211 sym.public_int_NSData_CocoaCryptoHashing_::sha1HexHash
0x00002be0 1 6 sym.imp.sprintf
0x00002be6 3 36 sym._stub_helpers
0x00002bbc 1 6 sym.imp.NSApplicationMain
0x00002bc2 1 6 sym.imp.NSHomeDirectory
0x00002bc8 1 6 sym.imp.NSRunAlertPanel
> db sym.public_int_FoxAppDelegate::validateSerial_int__int #在函数起始位置设置断点。
> dc #运行程序
程序运行起来,打开license文件验证..........
.....................
/BuildRoot/Library/Caches/com.apple.xbs/Sources/AppleFSCompression/AppleFSCompression-96.200.3/Libraries/CompressData/CompressData.c:353: Error: Unknown compression scheme encountered for file '/System/Library/PrivateFrameworks/FinderKit.framework/Resources/Base.lproj/ColumnPreview.nib'
hit breakpoint at: 2721
> pdf #显示函数内容
-- eax:
;-- eip:
┌ (fcn) sym.public_int_FoxAppDelegate::validateSerial_int__int 76
│ sym.public_int_FoxAppDelegate::validateSerial_int__int (int32_t arg_10h, int32_t arg_14h);
│ ; arg int32_t arg_10h @ ebp+0x10
│ ; arg int32_t arg_14h @ ebp+0x14
│ ; var int32_t var_4h @ esp+0x4
│ ; var int32_t var_8h @ esp+0x8
│ 0x00002721 b 55 push ebp
│ 0x00002722 89e5 mov ebp, esp
│ 0x00002724 83ec18 sub esp, 0x18
│ 0x00002727 a108400000 mov eax, dword str.sha1HexHash ; =0x2e06 str.sha1HexHash
│ ; DATA XREF from segment.LINKEDIT (+0x9599)
│ 0x0000272c 89442404 mov dword , eax
│ 0x00002730 8b4514 mov eax, dword ; =-1 ; 20
│ 0x00002733 890424 mov dword , eax
│ 0x00002736 e89f040000 call sym.imp.objc_msgSend ; void *objc_msgSend(void *instance, char *selector)
│ 0x0000273b 8b1504400000 mov edx, dword ; =0x91464d06
│ 0x00002741 89542404 mov dword , edx
│ 0x00002745 890424 mov dword , eax
│ 0x00002748 e88d040000 call sym.imp.objc_msgSend ; void *objc_msgSend(void *instance, char *selector)
│ 0x0000274d 8b5510 mov edx, dword ; =-1 ; 16
│ 0x00002750 89542408 mov dword , edx
│ 0x00002754 8b1500400000 mov edx, dword map.Fox.rw ; =0x9018aad3
│ 0x0000275a 89542404 mov dword , edx
│ 0x0000275e 890424 mov dword , eax
│ 0x00002761 e874040000 call sym.imp.objc_msgSend ; void *objc_msgSend(void *instance, char *selector)
│ 0x00002766 84c0 test al, al
│ 0x00002768 0f95c0 setne al
│ 0x0000276b c9 leave
└ 0x0000276c c3 ret
> db 0x00002733 #在合适位置设置以下几个断点
> db 0x0000273b
> db 0x0000274d
> db 0x00002750
> db 0x00002766
> db #显示断点列表
0x00002721 - 0x00002722 1 --x sw break enabled cmd="" cond="" name="sym.public_int_FoxAppDelegate::validateSerial_int__int" module="/Users/zyjsuper/Desktop/3-Fox/Fox.app/Contents/MacOS/Fox"
0x00002733 - 0x00002734 1 --x sw break enabled cmd="" cond="" name="0x00002733" module="/Users/zyjsuper/Desktop/3-Fox/Fox.app/Contents/MacOS/Fox"
0x0000273b - 0x0000273c 1 --x sw break enabled cmd="" cond="" name="0x0000273b" module="/Users/zyjsuper/Desktop/3-Fox/Fox.app/Contents/MacOS/Fox"
0x0000274d - 0x0000274e 1 --x sw break enabled cmd="" cond="" name="0x0000274d" module="/Users/zyjsuper/Desktop/3-Fox/Fox.app/Contents/MacOS/Fox"
0x00002750 - 0x00002751 1 --x sw break enabled cmd="" cond="" name="0x00002750" module="/Users/zyjsuper/Desktop/3-Fox/Fox.app/Contents/MacOS/Fox"
0x00002766 - 0x00002767 1 --x sw break enabled cmd="" cond="" name="0x00002766" module="/Users/zyjsuper/Desktop/3-Fox/Fox.app/Contents/MacOS/Fox"
> dc
hit breakpoint at: 2733
> pf xxS @eax #显示eax寄存器的内存数据
0x08d7b2c0 = 0xa83f7870
0x08d7b2c4 = 0x0100078c
0x08d7b2c8 = 0x08d7b2c8 -> 0x77616805
> psz @ 0x08d7b2c8 #显示此参数的值,即用户名
hawke
> dc
hit breakpoint at: 273b
> pf xxS @ eax
0x00209f90 = 0xa83f7870
0x00209f94 = 0x0100078c
0x00209f98 = 0x00209f98 -> 0x37383528
> psz @ 0x00209f98 #用户名经过sha1HexHash 函数加密后的字符串
(587377f4518ef068e17b393074c0a2183cd055d2
> dc
hit breakpoint at: 274d
> pf xxS @eax
0x006569a0 = 0xa83f7870
0x006569a4 = 0x010007ad
0x006569a8 = 0x006569a8 -> 0x006cd3e0
> psz @ 0x006cd3e0
(587377F4518EF068E17B393074C0A2183CD055D2 #转换成大写字母
> dc
hit breakpoint at: 2750
> pf xxS @ edx
0x08da1180 = 0xa83f7870
0x08da1184 = 0x0100078c
0x08da1188 = 0x08da1188 -> 0x58585804
> psz @ 0x08da1188 #显示license文件中的序列号
XXXX
> dc
hit breakpoint at: 2766
> dr al
0x00000000
> dr al=1 #手动将寄存器al的值改成1
0x00000000 ->0x00000001
> dr al
0x00000001
> dc
程序验证license通过,至此,使用radare2动态调试分析爆破成功。
第四部分 分析整理算法
算法比较简单,此处引用加密与解密第四版中的介绍如下:
序列号保护的本质就是验证用户名和序列号之间的映射关系,越复杂的映射关系越难破解,根据映射关系的不同,程序检测序列号有以下几种方式:
序列号 = F (用户名)
如果把这个过程看作加密解密并进行密文对比的过程,那么用户名就是明文,而序列号则是密文,F函数就是加密算法了。这种保护方法虽然简单,但极为不安全,因为在程序运行的某一时刻,内存中一定会出现正确的序列号,也就是加密函数结束后。只要找到正确的时间点,甚至完全不用关心算法就可以完成验证。
用户名 = F (序列号)
这里是把序列号作为明文,用户名作为密文了,这种方式通常需要F函数是一种对称加密算法的解密函数,而官方生成序列号时则使用加密函数对用户名进行加密得到的,这样就不会出现内存中有正确序列号的情况。这种保护方式的关键就是解密算法了,一旦得到解密算法就有机会逆向出加密算法,这样也就完成了验证。
F1 (用户名) = F2 (序列号)
这种方式是上一种的扩展,它相当于多套了几层加密而已。验证方式是:用户名 = F( F2(序列号) )
特殊值 = F (用户名, 序列号)
这种保护方式的数学原理就比较复杂了,但保护效果相比前几种有了很大提升。不过在设计上有难度,并且可能出现用户名与序列号映射不唯一的情况。
此Crackme使用的算法逻辑为: 序列号 = F (用户名)
即Serial(序列号)=uppercaseString(sha1HexHash(name)),将用户名经过sha1算法加密得到的hash值,再将hash值中的字母转化成大写,就得到了用户名对应的序列号。得到了算法逻辑,我们就可以按照此算法去编写注册机。
第五部分 使用Python编写keygen
#!/usr/bin/env python2
#-*-coding:utf-8-*-
'''
@version: python2.7
@author: ‘zyjsuper‘
@license: Apache Licence
@software: PyCharm
@file: Crackme3.py
@time: $2019-08-06 18:27
'''
import string
import random
from hashlib import sha1
ch =string.ascii_letters
def gen_name():
result = ""
for i in range(random.randint(5,8)): #生成长度为5到8位的随机用户名
result = result+random.choice(ch)
return result
name = gen_name()
psw=sha1() #将用户名使用sha1加密
psw.update(name.encode('utf8'))
serial=string.upper(psw.hexdigest()) #将加密后的值转化为大写字母
print '-'*55
print("Name:%s" %name)
print("Serial:%s" %serial)
print '-'*55
{:lol:}
多谢楼主分享哟
虽然喜欢看视频教程 感谢飞树大神的评分,感激涕零。 支持支持一下。 支持支持一下。 谢谢,支持一下。。。 教程不错,非常详细,特别是贴上去链接的视频,操作一边能学到很多 回复一下升级
不错,继续加油! 感谢分享,收藏学习
页:
[1]
2