代码还原小试牛刀(一):魔改的MD5

news2024/11/15 17:39:17

一、目标

2023年了,MD5已经是最基础的签名算法了,但如果你还只是对输入做了简单的MD5,肯定会被同行们嘲笑。加点盐(salt)是一种基本的提升,但在这个就业形势严峻的时代,仅仅加盐肯定不够了。

今天我们就来讲一讲魔改的MD5,让这个算法高大上起来。

1、菜卷

最简单的魔改方法就是改变MD5的初始参数,

     context->state[0] = 0x67452301;
     context->state[1] = 0xEFCDAB89;
     context->state[2] = 0x98BADCFE;
     context->state[3] = 0x10325476;

把这四个参数修改一下就行了。通过修改这些参数,我们可以改变MD5的运算结果。但这种方法实在太简单了,卷不起来。

接下来,我们要介绍更高级的卷法。

2、肉卷

md5会进行64轮运算,每轮运算都会用到一个常量,组成一个常量表K。

K原始值的计算方式是 2^32 * |sin i |,而后取其整数部分。

那么有理想的同学就可以更改这个K值,比如把 sin改成 cos或者tan之类的,这样就可以卷起来了。

3、卷中卷

//F,G,H,I四个非线性变换函数
#define F(x,y,z) ((x & y) | (~x & z))
#define G(x,y,z) ((x & z) | (y & ~z))
#define H(x,y,z) (x^y^z)
#define I(x,y,z) (y ^ (x | ~z))
//x循环左移n位的操作
#define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))

要真正卷起来,我们需要改变MD5中的四个非线性变换函数F、G、H、I。我们可以加上 异或 或者 减少 操作,整个算法就换了个面貌。这种高级卷法可以忽悠住老板,让算法高大上起来。

我们今天的目标是尝试还原一个魔改之后的MD5算法,通过这次实践来了解算法还原的基本方法。

这个样本我们的入参是字符串: “1677038066553”

返回值是32个字符: “DD89CA684D91818B970710F75A75743D”

二、步骤

第一步

我们需要用Unidbg跑通算法,比起上古时期用ida调试的前辈,Unidbg的出现直接把算法还原的难度降了一个数量级。

第二步

我们需要把结果Z通过反向推导一步一步回到原始输入A。这种方法叫做倒果为因,是逆向分析的一种基本套路。

我们假设这个样本是MD5或者是魔改的MD5,我们可以用以下几种方法来还原算法:

1、调试断点

2、条件断点

3、数据打印

4、Trace内存读写

5、Trace代码

1、调试断点

逆向分析是经验科学,虽然有一些基本套路,但是还是以试为主,先用IDA打开 libnative-lib.so,从 Exports 导出表里面找到导出函数 Java_com_littleq_cryptography_md5_MainActivity_sign

这个函数的开始地址在0x1234, 结束地址在0x12B4,但是主要的代码逻辑在函数sub_A3C里面, 我们先在sub_A3C函数的末端下个断点试试,

text:00000000000011D4 E0 07 40 F9                 LDR             X0, [SP,#0x110+var_108]
.text:00000000000011D8 03 00 00 90+                ADRL            X3, aSSSS ; "%s%s%s%s"
.text:00000000000011D8 63 EC 0A 91
.text:00000000000011E0 E4 83 01 91                 ADD             X4, SP, #0x110+var_B0
.text:00000000000011E4 E5 43 01 91                 ADD             X5, SP, #0x110+var_C0
.text:00000000000011E8 E6 03 01 91                 ADD             X6, SP, #0x110+var_D0
.text:00000000000011EC E7 C3 00 91                 ADD             X7, SP, #0x110+var_E0
.text:00000000000011F0 01 00 80 92                 MOV             X1, #0xFFFFFFFFFFFFFFFF
.text:00000000000011F4 02 08 80 52                 MOV             W2, #0x40 ; '@'

这个 0x11D8 很像是格式化字符串。

我们在Unidbg里面给 0x11D8 下个断点

    Debugger debugger = emulator.attach();
    debugger.addBreakPoint(module.base + 0x11D8);

运行一下,顺利的断下来了

debugger break at: 0x400011d8 @ Function64 address=0x40001234, arguments=[unidbg@0xfffe1640[libandroid.so]0x640, 1853170425, 2008362258]
>>> x0=0xbffff690(-1073744240) x1=0x0 x2=0x4 x3=0xbfffed20 x4=0x40230200 x5=0x402302c0 x6=0x1 x7=0xbffff708 x8=0x0 x9=0x0 x10=0x1 x11=0x0 x12=0x8 x13=0x8 x14=0x8
>>> x15=0x8 x16=0x40228d70 x17=0x40177ddc x18=0x8 x19=0x4cf3a208 x20=0x400012b8 x21=0x0 x22=0x68ca89dd x23=0x3d74755a x24=0x72e737bb x25=0xddf5ac1 x26=0xd0d5adc6 x27=0x8b81914d x28=0xf7100797 fp=0xbffff680
LR=RX@0x400011d4[libnative-lib.so]0x11d4
SP=0xbffff570
PC=RX@0x400011d8[libnative-lib.so]0x11d8
nzcv: N=0, Z=1, C=1, V=0, EL0, use SP_EL0
start + 0xae8
=> *[libnative-lib.so*0x011d8]*[03000090]*0x400011d8:*"adrp x3, #0x40001000"
    [libnative-lib.so 0x011dc] [63ec0a91] 0x400011dc: "add x3, x3, #0x2bb"
    [libnative-lib.so 0x011e0] [e4830191] 0x400011e0: "add x4, sp, #0x60"
    [libnative-lib.so 0x011e4] [e5430191] 0x400011e4: "add x5, sp, #0x50"
    [libnative-lib.so 0x011e8] [e6030191] 0x400011e8: "add x6, sp, #0x40"
    [libnative-lib.so 0x011ec] [e7c30091] 0x400011ec: "add x7, sp, #0x30"
    [libnative-lib.so 0x011f0] [01008092] 0x400011f0: "mov x1, #-1"
    [libnative-lib.so 0x011f4] [02088052] 0x400011f4: "mov w2, #0x40"
    [libnative-lib.so 0x011f8] [5bfdff97] 0x400011f8: "bl #0x40000764"

在Arm汇编里面,调用一个函数之前,会把入参存入到 x0,x1,x2 …​…​

从这段代码可以看出 地址 0x400011f8 会调用 0x40000764 函数,并且传入了 7个参数, 从x0,一直赋值到x7。

Unidbg的调试虽然有些简陋,但是已经够用了,有如此神器在手,你还要啥自行车?

调试命令先掌握以下几个:

s 单步步入,就是遇到函数调用会进入。

n 单步步过,遇到函数调用不会进入函数。

c 继续执行

b 下断点

r 取消当前断点

m 查看内存

我们先 s s s 几下,单步执行到 0x400011f8

debugger break at: 0x400011f8 @ Function64 address=0x40001234, arguments=[unidbg@0xfffe1640[libandroid.so]0x640, 1853170425, 2008362258]
>>> x0=0xbffff690(-1073744240) x1=0xffffffffffffffff x2=0x40 x3=0x400012bb x4=0xbffff5d0 x5=0xbffff5c0 x6=0xbffff5b0 x7=0xbffff5a0 x8=0x0 x9=0x0 x10=0x1 x11=0x0 x12=0x8 x13=0x8 x14=0x8
LR=RX@0x400011d4[libnative-lib.so]0x11d4
SP=0xbffff570
PC=RX@0x400011f8[libnative-lib.so]0x11f8
nzcv: N=0, Z=1, C=1, V=0, EL0, use SP_EL0
start + 0xb08
=> *[libnative-lib.so*0x011f8]*[5bfdff97]*0x400011f8:*"bl #0x40000764"

这个时间点,入参都已经准备好了,我们来一个一个看看这些入参。

mx7

>-----------------------------------------------------------------------------<
[10:40:26 646]x7=unidbg@0xbffff5a0, md5=d6c164ca9ef531557fc14e1bf7173663,
size: 112
0000: 35 41 37 35 37 34 33 44 00 B3 22 40 00 00 00 00    5A75743D.."@....
0010: 39 37 30 37 31 30 46 37 00 8D 09 40 00 00 00 00    970710F7...@....
0020: 34 44 39 31 38 31 38 42 00 77 12 40 02 00 00 00    4D91818B.w.@....
0030: 44 44 38 39 43 41 36 38 00 1B 17 40 02 00 00 00    DD89CA68...@....
0040: 31 36 37 37 30 33 38 30 36 36 35 35 33 80 00 00    1677038066553...
0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
^-----------------------------------------------------------------------------^

可以看到这次调用 函数 0x40000764, 基本就是在组装最后的结果了。

我们要做的就是找到这些结果生成的位置,来分析最终结果是如何计算出来的,也就是 Y → Z 的过程。

4、Trace内存读写

现在我们已经知道了结果Z的位置,下一步就是需要知道谁计算出了Z。

这就需要用到Unidbg的一个强大功能:内存读写监控

这一次我们先把调试断点下早一点,在 sub_A3C 函数开头就断下来。

debugger break at: 0x40000a3c @ Function64 address=0x40001234, arguments=[unidbg@0xfffe1640[libandroid.so]0x640, 1853170425, 2008362258]
>>> x0=0x40004000 x1=0xbffff690 x2=0x0 x3=0x1 x4=0x0 x5=0x1 x6=0x0 x7=0x0 x8=0xfffe0a70 x9=0x3002 x10=0x0 x11=0x1 x12=0x3 x13=0x40003018 x14=0x40003028
>>> x15=0x1 x16=0x40228910 x17=0x0 x18=0x17 x19=0xfffe1640 x20=0xbffff708 x21=0x0 x22=0x0 x23=0x0 x24=0x0 x25=0x0 x26=0x0 x27=0x0 x28=0x0 fp=0xbffff6f0
LR=RX@0x40001280[libnative-lib.so]0x1280
SP=0xbffff690
PC=RX@0x40000a3c[libnative-lib.so]0xa3c
nzcv: N=0, Z=0, C=1, V=0, EL0, use SP_EL0
start + 0x34c
=> *[libnative-lib.so*0x00a3c]*[ff8304d1]*0x40000a3c:*"sub sp, sp, #0x120"

traceWrite 0xbffff5d0 0xbffff5d8
Set trace 0xbffff5d0->0xbffff5d8 memory write success.
c
[11:41:41 656] Memory WRITE at 0xbffff5d8, data size = 1, data value = 0x0, PC=RX@0x40001168[libnative-lib.so]0x1168, LR=null
[11:41:41 657] Memory WRITE at 0xbffff5d0, data size = 8, data value = 0x0, PC=RX@0x4000116c[libnative-lib.so]0x116c, LR=null
[11:41:41 661] Memory WRITE at 0xbffff5d8, data size = 1, data value = 0x0, PC=RX@0x401b48cc[libc.so]0x648cc, LR=RX@0x401b48c8[libc.so]0x648c8

traceWrite 就是监控写内存命令。

看上去0xbffff5d0这段内存,写入 DD89CA68 数据的位置是: 0x116c

text:000000000000114C 14 00 00 90+                ADRL            X20, unk_12B8
.text:000000000000114C 94 E2 0A 91
.text:0000000000001154 C4 0A C0 5A                 REV             W4, W22
.text:0000000000001158 E0 83 01 91                 ADD             X0, SP, #0x110+var_B0
.text:000000000000115C 21 01 80 52                 MOV             W1, #9
.text:0000000000001160 22 01 80 52                 MOV             W2, #9
.text:0000000000001164 E3 03 14 AA                 MOV             X3, X20
.text:0000000000001168 FF A3 01 39                 STRB            WZR, [SP,#0x110+var_A8]
.text:000000000000116C FF 33 00 F9                 STR             XZR, [SP,#0x110+var_B0]
.text:0000000000001170 7D FD FF 97                 BL              sub_764

0x116c 的指令 STR XZR 是写入 没错,但是看上去不像是写入数据,而是把 SP,#0x110+var_B0 这个地址的数据清零。

那我们重来一次,(Unidbg的优点就是可以无限重放,比真机调试App方便了不知道多少倍。)

这次往前一点点,在 0x114C 下断点。

断下来之后,每s单步一次之后,就去查看 m0xbffff5d0。

最后发现跑完 0x1170 , 0xbffff5d0内存的值就改变成了, DD89CA68 。 这说明 0xbffff5d0 是 sub_764 函数去写的。

debugger break at: 0x40001170 @ Function64 address=0x40001234, arguments=[unidbg@0xfffe1640[libandroid.so]0x640, 1853170425, 2008362258]
>>> x0=0xbffff5d0(-1073744432) x1=0x9 x2=0x9 x3=0x400012b8 x4=0xdd89ca68 x5=0xe6cd8e62 x6=0x24523012 x7=0x29b9c389 x8=0x40 x9=0x40318041 x10=0xbffff5e0 x11=0x40 x12=0x3d5ebb2b x13=0x6450c165 x14=0xfc63b7e7
>>> x15=0x49ac16b x16=0xac6af723 x17=0xf3d1564b x18=0x18 x19=0x4cf3a208 x20=0x400012b8 x21=0x0 x22=0x68ca89dd x23=0x3d74755a x24=0x72e737bb x25=0xddf5ac1 x26=0xd0d5adc6 x27=0x8b81914d x28=0xf7100797 fp=0xbffff680
LR=null
SP=0xbffff570
PC=RX@0x40001170[libnative-lib.so]0x1170
nzcv: N=0, Z=1, C=1, V=0, EL0, use SP_EL0
start + 0xa80
=> *[libnative-lib.so*0x01170]*[7dfdff97]*0x40001170:*"bl #0x40000764"

不过回到 0x1170,我们发现了一串熟悉的数字 x4=0xdd89ca68 , 好吧,我们的问题又变成了 x4的值是怎么算出来的?

三、总结

首先要习惯看Arm汇编,一步一步单步调试,然后熟悉寄存器的变化。特别对一些关键数字要敏感。

要掌握Unidbg的基础调试命令。

常见的加密算法要熟悉一下,在开发环境里多调试几遍,熟悉它的算法流程。

ffshow

1:ffshow

多方分别,是非之窦易开;一味圆融,人我之见不立。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/391225.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

原腾讯QQ空间负责人,T13专家,黄希彤被爆近期被裁员,裁员原因令人唏嘘。。...

点击上方“码农突围”&#xff0c;马上关注这里是码农充电第一站&#xff0c;回复“666”&#xff0c;获取一份专属大礼包真爱&#xff0c;请设置“星标”或点个“在看这是【码农突围】的第 431 篇原创分享作者 l 突围的鱼来源 l 码农突围&#xff08;ID&#xff1a;smartyuge&…

【论文速递】WACV 2023 - 一种全卷积Transformer的医学影响分割模型

【论文速递】WACV 2023 - 一种全卷积Transformer的医学影响分割模型 【论文原文】&#xff1a;The Fully Convolutional Transformer for Medical Image Segmentation 【作者信息】&#xff1a;Athanasios Tragakis, Chaitanya Kaul,Roderick Murray-Smith,Dirk Husmeier 论…

app上架专用软著认证电子版权在主流应用商店的使用说明2023年最新版

软著认证电子版权在主流应用商店的使用说明 目录 一、 华为应用商店 二、 腾讯应用宝 三、 小米开放平台 小米应用提交&#xff1a; 小米游戏提交&#xff1a; 四、 OPPO开放平台 OPPO应用提交: OPPO游戏&#xff08;App&#xff09;提交: OPPO小游戏&#xff08;快应…

Python爬虫之用Selenium做爬虫

我们在用python做爬虫的时候&#xff0c;除了直接用requests的架构&#xff0c;还有Scrapy、Selenium等方式可以使用&#xff0c;那么今天我们就来聊一聊使用Selenium如何实现爬虫。 Selenium是什么&#xff1f; Selenium是一个浏览器自动化测试框架&#xff0c;是一款用于We…

2022年数维杯国际大学生数学建模挑战赛B题红VS蓝求解论文及程序

2022年数维杯国际大学生数学建模挑战赛 B题 红VS蓝 原题再现&#xff1a; 在现代战争中&#xff0c;攻守双方都需要引入有效的战争策略&#xff0c;以增加战争威胁并减少损失。只有形成相对稳定、平衡的战争态势&#xff0c;才能尽快实现达成共识的最终目标。 鉴于上述战争问…

代码随想录算法训练营第十七天 | 110.平衡二叉树、257. 二叉树的所有路径、404.左叶子之和

打卡第17天&#xff0c;补卡中&#xff0c;懒狗又歇了几天。 今日任务 110.平衡二叉树257.二叉树的所有路径404.左叶子之和 110.平衡二叉树 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a; 一个二叉树每个…

(全网最详细)Mysql下载安装和配置方法(看了必成功)

Mysql下载 MySQL官网下载地址&#xff1a;MySQL 点击进行下载 解压到你想要安装的目录 新建my.ini文件复制以下内容粘贴进去修改basedir安装的目录&#xff0c;datadir安装的目录\data [mysqld] #设置3306端口 port3306 #设置mysql的安装目录 basedir #设置mysql数据库的数据…

KubeSphere 社区双周报 | OpenFunction v1.0.0-rc.0 发布

KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者&#xff0c;并对近期重要的 PR 进行解析&#xff0c;同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为&#xff1a;2023.02.17-2023.…

idea插件推荐

idea插件推荐代码辅助GitHub CopilotAlibaba Cloud AI Coding AssistantTabnine AI Code Completion- JS Java Python TS Rust Go PHP & MoreiCodeJFormDesigner :图形用户界面生成器开发插件Mybatis HelperMaven HelperJPA Supportjava插件ptgGsonFormatPlusFastHotSwappe…

kibana查看日志

一、背景 kibana收集日志功能很强大&#xff0c;之前只是简单的使用&#xff0c;此次系统学习了解并分享一波 二、kibana查看日志的基本使用 1.选择查询的服务和日志文件 注意&#xff1a;每个应用配置了开发与生产环境&#xff0c;需要找到指定的应用 1.1选择对应的应用 1.…

wxpython设计GUI:wxFormBuilder工具常用布局结构介绍之布局四—面板拼接式

python借助wxFormBuilder工具搭建基础的GUI界面—wxFormBuilder工具使用介绍&#xff1a;https://blog.csdn.net/Logintern09/article/details/126685315 布局四&#xff1a;面板拼接式&#xff0c;先Panel面板构图&#xff0c;再使用程序代码在Frame框架上拼接面板 下面讲一下…

SurfaceFlinger模块

SurfaceFlinger是一个系统服务&#xff0c;作用就是接受不同layer的buffer数据进行合成&#xff0c;然后发送到显示设备进行显示。SurfaceFlinger进程是什么时候起来的&#xff1f;在之前的Android低版本手机上&#xff0c;SurfaceFlinger进程是在init.rc中启动的&#xff0c;在…

.Net Core WebApi 在Linux系统Deepin上部署Nginx并使用(一)

前言&#xff1a; Deepin最初是基于Ubuntu的发行版 2015年脱离Ubuntu开发&#xff0c;开始基于Ubuntu上游Debian操作系统 2019年脱离Debian&#xff0c;直接基于Linux开发&#xff0c;真正属于自己的上游Linux系统发行版 2022年8月&#xff0c;新版《Deepin V23》我下载开始了我…

Registry与DGC的攻击利用

0x01 2022-02-03写的一篇文章。 0x02 Registry Registry指的是RMI的注册表&#xff0c;攻击的目标是注册表所在的机器&#xff0c;一般注册表和RMI Server在同一个机器上&#xff0c;特殊情况下也会在不同机器上。 在我们通过LocateRegistry#getRegistry获取到目标开启的注…

Win32:C++其实早已支持中文编程

我们以前学习C/C的时候&#xff0c;对于变量和标识符的命名都有如下规则&#xff1a; 变量名必须由字母、数字、下划线构成只能以字母、下划线开头 似乎对中文不太友善啊&#xff0c;于是后来出现了一些中文编程的呼声&#xff0c;甚至还真的出现了一些中文编程语言。 其实在…

【微信小程序-原生开发】实用教程15 - 列表的排序、搜索(含云数据库常用查询条件的使用方法,t-search 组件的使用)

请先完成列表数据的分页、触底加载 【微信小程序-原生开发】实用教程14 - 列表的分页加载&#xff0c;触底加载更多&#xff08;含无更多数据的提醒和显示&#xff0c;自定义组件&#xff09; https://blog.csdn.net/weixin_41192489/article/details/129355396 效果预览 核心…

嵌入式 Linux进程间的通信--信号

目录 信号 信号的概述 信号类型 信号发送 1、kill 函数 2、raise函数 3、pause函数 信号处理 可以结合上一篇文章一起看&#xff1a; 嵌入式 Linux进程之间的通信_丘比特惩罚陆的博客-CSDN博客 信号 信号的概述 软中断信号&#xff08;signal&#xff0c;又简称为…

软聚类算法:模糊聚类 (Fuzzy Clustering)

前言 如果你对这篇文章感兴趣&#xff0c;可以点击「【访客必读 - 指引页】一文囊括主页内所有高质量博客」&#xff0c;查看完整博客分类与对应链接。 在介绍模糊聚类之前&#xff0c;我们先简单地列举一下聚类算法的常见分类&#xff1a; 硬聚类 (Hard Clustering) Connec…

NGINX学习笔记 - 一篇了解NGINX的基本概念(一)

NGINX是什么&#xff1f; NGINX是一款由俄罗斯人伊戈尔赛索耶夫使用C语言开发的、支持热部署的、轻量级的WEB服务器/反向代理服务器/电子邮件代理服务器&#xff0c;因为占用内存较少&#xff0c;启动极快&#xff0c;高并发能力强&#xff0c;所以在互联网项目中广泛应用。可…

十二、面向切面编程AOP

IoC使软件组件松耦合。AOP让你能够捕捉系统中经常使用的功能&#xff0c;把它转化成组件。 AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff1a;面向切面编程&#xff0c;面向方面编程。&#xff08;AOP是一种编程技术&#xff09; AOP是对OOP的补充延伸。 …