zyjsuper 发表于 2019-8-7 20:20:17

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













xie83544109 发表于 2019-8-8 08:20:49

{:lol:}
多谢楼主分享哟
虽然喜欢看视频教程

zyjsuper 发表于 2019-8-8 17:04:10

感谢飞树大神的评分,感激涕零。

小七柒 发表于 2019-8-9 10:19:19

支持支持一下。

cfanhacker 发表于 2019-8-9 15:38:36

支持支持一下。

ssda 发表于 2019-8-11 16:17:16

谢谢,支持一下。。。

bettering 发表于 2019-8-16 08:59:08

教程不错,非常详细,特别是贴上去链接的视频,操作一边能学到很多

liuhaiqi 发表于 2019-8-19 16:03:34

回复一下升级

wtuxwyg 发表于 2019-8-20 09:31:19


不错,继续加油!

fishso 发表于 2019-12-4 00:00:32

感谢分享,收藏学习
页: [1] 2
查看完整版本: Hopper+LLDB+Radare2 动态调试教程