技术教程破解资源
第一次尝试粗浅的逆向破解golang程序
前言
这几天在我无聊逛 github 的时候,发现了一个写着能导出微信数据库密码和聊天记录的仓库,就想下载运行看看效果如何。
"https://static.52pojie.cn/static/image/filetype/zip.gif" border="0" class="vm" alt="" />app.zip2022-9-4 17:36 上传点击文件名下载附件
然而启动后且报错 pass error
,显然是需要个正确密码。
用 exeinfope 查了目标程序,无壳,64 位程序,用 go 语言写的。可以直接上 IDA 分析,或者用 x64dbg。
之前没分析过编译后的 go 程序,大概知道 golang 是静态链接的,封装层次高,有多返回值特点,gc、协程实现,标准库第三方库等等都嵌入了,文件体积贼大。
载入 IDA,能看到一大坨的函数,不可能一个个都去看的。后面干啃去调试时,函数间的跳转比较饶,加上有异步,容易跑飞。
像这种应该就是被 strip 掉了符号,go build 本身有命令可以去掉
go build ldflags="-s -w" main.go
也没找到 .gopclntab
区段
开局,搜索字符串
按老套路,最简单的搜索字符串,定位关键代码
由于启动密码的判断都写在程序的开头,如果能找到入口点,分析起来就简单不少,虽然这个程序没有调试符号,但一般情况就算 go 编译时去掉了,也还会包含一定量的信息,能还原出一些函数符号和源码文件路径。
我借助了 IDAPython
运行 go_parse 和 IDAGolangHelper
那些脚本来解析,希望能方便后续分析。
然而可惜的是,不是有报错就是解析不全面,字符串也没解析出来,没有看到 main_init
main_main
入口函数,当然可能是 go 版本较高,pclntab 结构有变化,一些工具还不支持。
不过至少还原了部分 runtime,这总比干啃好。
有些奇怪的是,解析之后不少函数名都是随机字符串。我也不确定这程序是经过了混淆保护了,还是 golang 本身有其他什么设置就能导致如此。
"https://img1.imgtp.com/2022/09/04/63scSIy7.png" alt="image" />"https://img1.imgtp.com/2022/09/04/EtiJgD5X.png" alt="image" />
IDA rebase 一下基址或导出 map 方便在调试时候对着看
"43921813_动态调试-api-断点回溯">动态调试 API 断点回溯
接着尝试从 API 下断入手,逼近关键代码,首先很容易想到获取启动参数的 API 会用到 GetCommandLineW
,go 用 fmt.println
写出到控制台的 API 则可能是 WriteFile
,WriteConsoleW
(64 位一般直接断 W),载入程序后下断点运行。
"https://img1.imgtp.com/2022/09/04/1zjtt0oQ.png" alt="image" />
中间遇到循环不用管,一直专注回溯
同时注意堆栈字符串的变化,println 是分成时间和 pass error
两段。
看到这里再往后一两次 ret 差不多就到了。
这地方就差不多到了,在函数头下断点,,如果不确定就多下 F2,重载运行,输入参数。
0000000000DE5F60 | 4C:8D6424 E8 | lea r12,qword ptr ss:[rsp-18] |
这种方法还是比较绕的,尤其是没有符号和不熟悉的情况,下面还有其他方法。
跟踪数据读写
命令行参数值始终是要判断的,用 go 写个 demo,大概写法就是:
package mainimport ( "fmt" "os")func main() { if len(os.Args) > 2 { if os.Args[1] == "123456" { fmt.Printf("os.Args[1]: %v\n", os.Args[1]) } else { fmt.Println("pass error") } } else { fmt.Println("error 1") }}
在没有符号的情况下,可以通过对比自己写的 demo 汇编结构和 go 源码来人肉识别某些库函数。
// These routines end in 'ln', do not take a format string,// always add spaces between operands, and add a newline// after the last operand.// Fprintln formats using the default formats for its operands and writes to w.// Spaces are always added between operands and a newline is appended.// It returns the number of bytes written and any write error encountered.func Fprintln(w io.Writer, a ...any) (n int, err error) { p := newPrinter() p.doPrintln(a) n, err = w.Write(p.buf) p.free() return}.........
os.Args 是一个全局切面变量,找到这个也可以定位。os.Args 是在 os.init 中初始化的。
启动后对 GetCommandLineW
下段,单步往下,对 rax 的第一个参数下硬件访问断点
"https://img1.imgtp.com/2022/09/04/THwHUB19.png" alt="1662194314120" />
循环取值到 edi
,然后赋值转移到 [rax+rbx*4]
,那也在这里下个硬件断点
接着运行
"https://img1.imgtp.com/2022/09/04/2CxWv5mt.png" alt="image" />"https://img1.imgtp.com/2022/09/04/5YKmwxav.png" alt="image" />
可以发现赋给了一个值
"https://img1.imgtp.com/2022/09/04/t7tDCauB.png" alt="image" />"43921813_patch-代码">patch 代码
很简单了,有字符串比较,跟一遍,只要 nop 掉两处跳转即可
"x86asm">0000000000DE58C0 | 49:3B66 10 | cmp rsp,qword ptr ds:[r14+10] |0000000000DE58C4 | 0F86 EE010000 | jbe target.DE5AB8 |0000000000DE58CA | 48:83EC 70 | sub rsp,70 |0000000000DE58CE | 48:896C24 68 | mov qword ptr ss:[rsp+68],rbp | [rsp+68]:"袩\r"0000000000DE58D3 | 48:8D6C24 68 | lea rbp,qword ptr ss:[rsp+68] | [rsp+68]:"袩\r"0000000000DE58D8 | 48:BA E4E18554A2A332C3 | mov rdx,C332A3A25485E1E4 |0000000000DE58E2 | 48:895424 54 | mov qword ptr ss:[rsp+54],rdx |0000000000DE58E7 | 48:BA A2A332C3BFD17EA1 | mov rdx,A17ED1BFC332A3A2 |0000000000DE58F1 | 48:895424 58 | mov qword ptr ss:[rsp+58],rdx |0000000000DE58F6 | 48:BA C5CDCF5350E17134 | mov rdx,3471E15053CFCDC5 |0000000000DE5900 | 48:895424 60 | mov qword ptr ss:[rsp+60],rdx |0000000000DE5905 | 0FB65424 55 | movzx edx,byte ptr ss:[rsp+55] |0000000000DE590A | 885424 53 | mov byte ptr ss:[rsp+53],dl |0000000000DE590E | 44:0FB64424 66 | movzx r8d,byte ptr ss:[rsp+66] |0000000000DE5914 | 44:884424 52 | mov byte ptr ss:[rsp+52],r8b |0000000000DE5919 | 44:0FB64C24 54 | movzx r9d,byte ptr ss:[rsp+54] |0000000000DE591F | 44:884C24 51 | mov byte ptr ss:[rsp+51],r9b |0000000000DE5924 | 44:0FB65424 56 | movzx r10d,byte ptr ss:[rsp+56] |0000000000DE592A | 44:885424 50 | mov byte ptr ss:[rsp+50],r10b |0000000000DE592F | 44:0FB65C24 5B | movzx r11d,byte ptr ss:[rsp+5B] |0000000000DE5935 | 44:885C24 4F | mov byte ptr ss:[rsp+4F],r11b |0000000000DE593A | 44:0FB66424 64 | movzx r12d,byte ptr ss:[rsp+64] |0000000000DE5940 | 44:886424 4E | mov byte ptr ss:[rsp+4E],r12b |0000000000DE5945 | 44:0FB66C24 57 | movzx r13d,byte ptr ss:[rsp+57] |0000000000DE594B | 44:886C24 4D | mov byte ptr ss:[rsp+4D],r13b |0000000000DE5950 | 44:0FB67C24 65 | movzx r15d,byte ptr ss:[rsp+65] |0000000000DE5956 | 44:887C24 4C | mov byte ptr ss:[rsp+4C],r15b |0000000000DE595B | 44:0FB66C24 58 | movzx r13d,byte ptr ss:[rsp+58] |0000000000DE5961 | 44:886C24 4B | mov byte ptr ss:[rsp+4B],r13b |0000000000DE5966 | 44:0FB66C24 5E | movzx r13d,byte ptr ss:[rsp+5E] |0000000000DE596C | 44:886C24 4A | mov byte ptr ss:[rsp+4A],r13b |0000000000DE5971 | 44:0FB66C24 67 | movzx r13d,byte ptr ss:[rsp+67] |0000000000DE5977 | 44:886C24 49 | mov byte ptr ss:[rsp+49],r13b |0000000000DE597C | 44:0FB66C24 62 | movzx r13d,byte ptr ss:[rsp+62] |0000000000DE5982 | 44:886C24 48 | mov byte ptr ss:[rsp+48],r13b |0000000000DE5987 | 44:0FB66C24 61 | movzx r13d,byte ptr ss:[rsp+61] |0000000000DE598D | 44:886C24 47 | mov byte ptr ss:[rsp+47],r13b |0000000000DE5992 | 44:0FB66C24 5C | movzx r13d,byte ptr ss:[rsp+5C] |0000000000DE5998 | 44:886C24 46 | mov byte ptr ss:[rsp+46],r13b |0000000000DE599D | 44:0FB66C24 60 | movzx r13d,byte ptr ss:[rsp+60] |0000000000DE59A3 | 44:886C24 45 | mov byte ptr ss:[rsp+45],r13b |0000000000DE59A8 | 44:0FB66C24 63 | movzx r13d,byte ptr ss:[rsp+63] |0000000000DE59AE | 44:886C24 44 | mov byte ptr ss:[rsp+44],r13b |0000000000DE59B3 | 44:0FB66C24 5F | movzx r13d,byte ptr ss:[rsp+5F] |0000000000DE59B9 | 44:886C24 43 | mov byte ptr ss:[rsp+43],r13b |0000000000DE59BE | 44:0FB66C24 59 | movzx r13d,byte ptr ss:[rsp+59] |0000000000DE59C4 | 44:886C24 42 | mov byte ptr ss:[rsp+42],r13b |0000000000DE59C9 | 44:0FB66C24 5A | movzx r13d,byte ptr ss:[rsp+5A] |0000000000DE59CF | 44:886C24 41 | mov byte ptr ss:[rsp+41],r13b |0000000000DE59D4 | 44:0FB66C24 5D | movzx r13d,byte ptr ss:[rsp+5D] |0000000000DE59DA | 44:886C24 40 | mov byte ptr ss:[rsp+40],r13b |0000000000DE59DF | 48:8D05 5AAE0000 | lea rax,qword ptr ds:[DF0840] | rax:"pass er"0000000000DE59E6 | 31DB | xor ebx,ebx |0000000000DE59E8 | 31C9 | xor ecx,ecx |0000000000DE59EA | 48:89CF | mov rdi,rcx | rdi:"pass er"0000000000DE59ED | BE 0A000000 | mov esi,A | A:'\n'0000000000DE59F2 | E8 89A4E9FF | call target.C7FE80 |0000000000DE59F7 | 0FB65424 53 | movzx edx,byte ptr ss:[rsp+53] |0000000000DE59FC | 44:0FB64424 52 | movzx r8d,byte ptr ss:[rsp+52] |0000000000DE5A02 | 44:29C2 | sub edx,r8d |0000000000DE5A05 | 8810 | mov byte ptr ds:[rax],dl | rax:"pass er"0000000000DE5A07 | 0FB65424 50 | movzx edx,byte ptr ss:[rsp+50] |0000000000DE5A0C | 44:0FB64424 51 | movzx r8d,byte ptr ss:[rsp+51] |0000000000DE5A12 | 44:31C2 | xor edx,r8d |0000000000DE5A15 | 8850 01 | mov byte ptr ds:[rax+1],dl | rax+1:"ass er"0000000000DE5A18 | 0FB65424 4F | movzx edx,byte ptr ss:[rsp+4F] |0000000000DE5A1D | 44:0FB64424 4E | movzx r8d,byte ptr ss:[rsp+4E] |0000000000DE5A23 | 44:29C2 | sub edx,r8d |0000000000DE5A26 | 8850 02 | mov byte ptr ds:[rax+2],dl | rax+2:"ss er"0000000000DE5A29 | 0FB65424 4D | movzx edx,byte ptr ss:[rsp+4D] |0000000000DE5A2E | 44:0FB64424 4C | movzx r8d,byte ptr ss:[rsp+4C] |0000000000DE5A34 | 44:29C2 | sub edx,r8d |0000000000DE5A37 | 8850 03 | mov byte ptr ds:[rax+3],dl | rax+3:"s er"0000000000DE5A3A | 0FB65424 4A | movzx edx,byte ptr ss:[rsp+4A] |0000000000DE5A3F | 44:0FB64424 4B | movzx r8d,byte ptr ss:[rsp+4B] |0000000000DE5A45 | 44:01C2 | add edx,r8d |0000000000DE5A48 | 8850 04 | mov byte ptr ds:[rax+4],dl | rax+4:" er"0000000000DE5A4B | 0FB65424 49 | movzx edx,byte ptr ss:[rsp+49] |0000000000DE5A50 | 44:0FB64424 48 | movzx r8d,byte ptr ss:[rsp+48] |0000000000DE5A56 | 44:29C2 | sub edx,r8d |0000000000DE5A59 | 8850 05 | mov byte ptr ds:[rax+5],dl | rax+5:"er"0000000000DE5A5C | 0FB65424 46 | movzx edx,byte ptr ss:[rsp+46] |0000000000DE5A61 | 44:0FB64424 47 | movzx r8d,byte ptr ss:[rsp+47] |0000000000DE5A67 | 44:31C2 | xor edx,r8d |0000000000DE5A6A | 8850 06 | mov byte ptr ds:[rax+6],dl |0000000000DE5A6D | 0FB65424 45 | movzx edx,byte ptr ss:[rsp+45] |0000000000DE5A72 | 44:0FB64424 44 | movzx r8d,byte ptr ss:[rsp+44] |0000000000DE5A78 | 44:29C2 | sub edx,r8d |0000000000DE5A7B | 8850 07 | mov byte ptr ds:[rax+7],dl |0000000000DE5A7E | 0FB65424 43 | movzx edx,byte ptr ss:[rsp+43] |0000000000DE5A83 | 44:0FB64424 41 | movzx r8d,byte ptr ss:[rsp+41] |0000000000DE5A89 | 44:29C2 | sub edx,r8d |0000000000DE5A8C | 8850 08 | mov byte ptr ds:[rax+8],dl |0000000000DE5A8F | 0FB65424 40 | movzx edx,byte ptr ss:[rsp+40] |0000000000DE5A94 | 44:0FB64424 42 | movzx r8d,byte ptr ss:[rsp+42] |0000000000DE5A9A | 44:31C2 | xor edx,r8d |0000000000DE5A9D | 8850 09 | mov byte ptr ds:[rax+9],dl |0000000000DE5AA0 | 48:8D4B 0A | lea rcx,qword ptr ds:[rbx+A] |0000000000DE5AA4 | 48:89C3 | mov rbx,rax | rax:"pass er"0000000000DE5AA7 | 31C0 | xor eax,eax |0000000000DE5AA9 | E8 D2D6E9FF | call target.C83180 |0000000000DE5AAE | 48:8B6C24 68 | mov rbp,qword ptr ss:[rsp+68] | [rsp+68]:"袩\r"0000000000DE5AB3 | 48:83C4 70 | add rsp,70 |0000000000DE5AB7 | C3 | ret |0000000000DE5AB8 | E8 63C0EAFF | call target.C91B20 |0000000000DE5ABD | 0F1F00 | nop dword ptr ds:[rax],eax |0000000000DE5AC0 | E9 FBFDFFFF | jmp target.DE58C0 |
没什么技术含量,以上纯属初次学习和分享。附件包含目标程序、和 patch 过后的程序。
debug 一小时,写文章好久了,有些图片截图的时间比较久了,后面重新换了,所以可能会有些输出对不上