首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
Android安全
发新帖
1
3
[原创]libsalt.so sign 算法分析
发表于: 2025-8-10 23:29
1716
[原创]libsalt.so sign 算法分析
沈聽白
2025-8-10 23:29
1716
> ## <font style="color:#DF2A3F;">在开始这篇文章之前,请阅读一下注意事项</font> > ## <font style="color:#DF2A3F;">本文章仅用于学习研究,如有侵犯贵司权益,请联系告知,会立即做出下架删除处理。</font> > ## <font style="color:#DF2A3F;">本文章不针对任何网站,APP ,禁止用于商业,转载,违法等用途。</font> > ## <font style="color:#DF2A3F;">观看则默认同意本约定,未经允许禁止任何形式的转载,保留追究法律责任的权利 !</font> ## Unidbg 模板 ```java package com; import com.github.unidbg.AndroidEmulator; import com.github.unidbg.Emulator; import com.github.unidbg.LibraryResolver; import com.github.unidbg.Module; import com.github.unidbg.arm.backend.Unicorn2Factory; import com.github.unidbg.debugger.Debugger; import com.github.unidbg.file.FileResult; import com.github.unidbg.file.IOResolver; import com.github.unidbg.linux.android.AndroidEmulatorBuilder; import com.github.unidbg.linux.android.AndroidResolver; import com.github.unidbg.linux.android.dvm.*; import com.github.unidbg.memory.Memory; import com.github.unidbg.virtualmodule.android.AndroidModule; import com.github.unidbg.virtualmodule.android.JniGraphics; import com.github.unidbg.virtualmodule.android.MediaNdkModule; import com.github.unidbg.virtualmodule.android.SystemProperties; import java.io.File; import java.util.ArrayList; import java.util.List; public class 模板 extends AbstractJni implements IOResolver { private static final String PackName = "APP 包名"; private static final String AppPath = "传入 APP路径"; private static final String[] SoName = {"SO名字或外部路径"}; // 可能会出现多个SO文件加载的情况 private final AndroidEmulator emulator; private final VM vm; private final Module module; final Memory memory; @Override public FileResult resolve(Emulator emulator, String pathname, int oflags) { System.out.println("Load File: " + pathname); return null; } private static LibraryResolver createLibraryResolver() { return new AndroidResolver(23); } private static AndroidEmulator createARMEmulator() { return AndroidEmulatorBuilder.for64Bit() .setProcessName(PackName) .addBackendFactory(new Unicorn2Factory(false)) .build(); } 模板 () { emulator = createARMEmulator(); // 创建模拟器的内存映射 emulator.getSyscallHandler().addIOResolver(this); // 获取模拟器的内存操作接口 memory = emulator.getMemory(); // 设置系统类库解析 23 memory.setLibraryResolver(createLibraryResolver()); // 创建Android虚拟机,传入APK,Unidbg可以替我们做部分签名校验的工作 vm = emulator.createDalvikVM(new File(AppPath)); // 虚拟模块部分 new AndroidModule(emulator,vm).register(memory); new MediaNdkModule(emulator,vm).register(memory); new JniGraphics(emulator,vm).register(memory); // 设置JNI vm.setJni(this); // 打印日志 vm.setVerbose(true); // 加载目标SO DalvikModule dm = vm.loadLibrary(SoName[0], true); //获取本SO模块的句柄,后续需要用它 module = dm.getModule(); // 调用JNI OnLoad dm.callJNI_OnLoad(emulator); }; public static void main(String[] args) { 模板 action = new 模板(); action.Call(); } public void Call () { List<Object> args = new ArrayList<>(); args.add(vm.getJNIEnv()); // JNIEnv * args.add(0); args.add(vm.addLocalObject(new StringObject(vm, "123456"))); // 输入字符串 args.add(vm.addLocalObject(new StringObject(vm, ""))); // 输入字符串 Number result = module.callFunction(emulator, 0x236c, args.toArray()); DvmObject<?> returnObj = vm.getObject(result.intValue()); String encrypted = ((StringObject) returnObj).getValue(); System.out.println("Encrypted String: " + encrypted); } } ``` ## 逻辑分析 > 从这章开始, 算法不会只用 Unidbg 或者是 看伪 C 进行分析 > 加密输出结果如下:  > 该 So 就是静态注册的方法 Java_com_nice_main_helpers_utils_NiceSignUtils_getSignRequest > > 静态注册的方式 Java_开头 完成静态注册 > > 动态注册的方法 在 JNI_OnLoad 方法中完成的动态注册 > 第一步咱们进去函数后发现一个名为 **nice_sign_v3** 的函数 这个就非常可疑 咱们进去看下  进入 **nice_sign_v3** 函数之后我们看到了 **MD5** 加密 这里可能是加密的核心位置 我们去用 **unidbg** **hook** 一下他的入参  我们下面可以清晰的看到他的入参可以分为三个参数,`**v16=body,v20=设备did(md5加密过的),v11=随机16位字符串**`  上面代码可以看到 他进行两次 **nice_md5** 那么我们去 **hook** 下 入参值 --- ### Nice_MD5 第一次 Nice_MD5 的入参 我们可以通过图片看到 他的入参值就是 v20 的前半部分和后半部分**颠倒位置**  ```plain 颠倒前: 66e465b27596fe73ac148ce8993c9eaf 颠倒后: ac148ce8993c9eaf66e465b27596fe73 ``` 第二次 Nice_MD5 的入参 我们可以通过图片看到 他的入参值就是 v11+第一次 Nice_MD5 的入参结果 +8a5f746c1c9c99c0b458e1ed510845e5  ```plain [zflq7vua9alemnwb] + [66e465b27596fe73ac148ce8993c9eaf] + [8a5f746c1c9c99c0b458e1ed510845e5] ``` ```plain 颠倒前: b56ebb1f9af72cfed5eaacb028eb05cd 颠倒后: d5eaacb028eb05cdb56ebb1f9af72cfe ``` 然后对第二次 Nice_MD5 的入参 的结果进行前半部分和后半部分**颠倒位置** ### 明文处理部分以及 nice_sha1 **第一步 我们通过图片可以看到几个函数 cJSON_Parse, sub_25B0, nice_sha1 通过代码可以看到 入参的明文数据传入cJSON_Parse 进行 JSON 解析操作得到了 v38 sub_25B0 函数传入了 v38 得到 v40 的结果 那么我 们现在去 hook 下 sub_25B0 返回的结果**  **v40** = **sub_25B0** 返回的结果入下:  通过结果可以看到 我们的明文参数从 **JSON** 格式变成了 **application/x-www-form-urlencoded** 格式并进行**升降**排序 ```plain v40 = "block_version=0&device_model=MI 9&items=config=&post=&proto_version=0.1" ``` `根据下面图片我们可以看到 v40 进行了一系列运算给了 v21 下面我们先去 HOOK 下 nice_sha1 之前的 v21 长什么样子`  `根据下面图片我们可以看到这个时候 v21 已经变成乱码和第二次 Nice_MD5 的入参 前半部分和后半部分``**颠倒位置**`  `从上面代码我们呢可以看到 **v21[46]=0** 就代表他可能是在这个位置做了截断` `接下来我们就用 **py** 把这个 **nice_sha1** 加密之前部分先给他还原了 看看加密出来的参数一样不`  通过上面图片我们可以清晰看到一模一样 下面我们就进行 **nice_sha1** 操作对比下结果   我们通过图片对比发现 **nice_sha1** 加密已经对比上了 但是和开头的结果还是不一样 我们继续去看代码  我们可以看到他从** v48+8** 的位置才开始获取 然后又进行了一次前半部分和后半部分**颠倒位置** 我们现在用 **py** 写个代码试试看看  我们发现已经和开头已经对上了 下面我们贴出部分的代码 ```python import hashlib def process_strings(v40, v67): v21 = bytearray(1024) v41 = 0 if len(v40) < 2: v46 = 32 else: v42_idx = 1 v43 = 0x2000000000 loop_count = len(v40) >> 1 while v41 < loop_count: if v42_idx - 1 >= len(v40) or v42_idx >= len(v40): break v44 = ord(v40[v42_idx - 1]) v45 = ord(v40[v42_idx]) combined = (v44 & 0xF0) | (v45 & 0xF) v21[v41] = combined v41 += 1 v42_idx += 2 v43 += 0x100000000 v46 = v43 >> 32 v67_bytes = v67.encode('utf-8') for i in range(32): if i < len(v67_bytes): v21[v41 + i] = v67_bytes[i] else: v21[v41 + i] = 0 if v46 < len(v21): v21[v46] = 0 else: pass print("Output:", v21) result = v21.split(b'\x00', 1)[0] return result def sha1_bytes(data: bytes) -> str: hash_obj = hashlib.sha1(data) return hash_obj.digest().hex()[8:] def swap(s): n = len(s) mid = n // 2 return s[mid:] + s[:mid] v40 = "block_version=0&device_model=MI 9&items=config=&post=&proto_version=0.1" v67 = "d5eaacb028eb05cdb56ebb1f9af72cfe" output = process_strings(v40, v67) print("Output:", output) digest_bytes = sha1_bytes(output) print("Final Output (bytes):", digest_bytes) print("Final Output (bytes):", swap(digest_bytes)) ``` ## 算法还原 > 在开始关键的算法还原的时候 我们在去整理一下全部的思路 这里我用一张逻辑图去表示出来 >  :::success 这里 我就避免一些不必要的情况,大家都是交流学习的 没必要搞事情 没有任何恶意哈 保命 我i就不贴出来完整的算法了总体难度评 2 分 ⭐ ⭐ 现在再回头看整个算法 难么? 不难 ! ::: --- ## 末尾 ## 后续 也会推出一系列的文章,包括但不限于 魔改算法,VMP 系列,小众算法 ### 欢迎加 V:<font style="color:rgb(77, 77, 77);">ass7752321 </font> 会给大家拉一个交流群 互相交流一下思路
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
#逆向分析
收藏
・
1
点赞
・
3
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
Yangser
非常支持你的观点!
2025-9-28 22:10
sinker_
为你点赞!
2025-8-13 16:52
frankyxu
感谢你的贡献,论坛因你而更加精彩!
2025-8-11 18:30
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
2
)
iBa0
雪 币:
1560
活跃值:
(5833)
能力值:
( LV4,RANK:40 )
在线值:
发帖
6
回帖
482
粉丝
37
关注
私信
iBa0
2
楼
可以老铁666
2025-8-11 10:44
0
Yangser
雪 币:
1953
活跃值:
(4173)
能力值:
( LV4,RANK:50 )
在线值:
发帖
11
回帖
129
粉丝
217
关注
私信
Yangser
3
楼
大佬怎么加群呢
2025-10-5 11:56
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
沈聽白
7
发帖
18
回帖
0
RANK
关注
私信
他的文章
[原创][原创]LibCrypt.so 魔改MD5
4881
[原创]libkwsgmain.so Sig64 位
2050
[原创]libtalaccsecurity.so sign与pwd算法分析
1747
[原创]libsalt.so sign 算法分析
1716
[原创]Libve04.so TEA 加密
1369
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部