前言

这段时间开始接触安卓逆向,说一下我的大致的学习步骤:

  1. 学一个新知识之前需要对这个知识有一定的概念,比如js逆向,肯定要抓接口,然后看参数,搜参数,打断点等。那么拿到一个APP下一步要干些什么也需要有一些概念。如何积累概念:多看一些逆向的文章,某些地方没看懂不重要,重要的是流程你有个大概了。
  2. 找一些简单的例子练练手,比如吾爱破解论坛的:https://www.52pojie.cn/thread-408645-1-1.html。另外吾爱破解的移动安全区也有很多的文章可以看看。还有一些Crackme拿来练手,看看评论和描述,先挑简单的尝试。
  3. 看一些网上的课,免费的付费的都行。没什么推荐的,我也是刚接触不太懂哪个好。
  4. 接着你会觉得,每下载一个APP都想抓个包,然后把它拖到jeb反编译一下,这样久了就越加熟练了。

涉及软件

模拟器推荐逍遥模拟器,因为雷电4无法抓包,夜神无法安装xposed,只有逍遥啥毛病没有,我用的去广告绿色版。下载链接:https://www.52pojie.cn/forum.php?mod=viewthread&tid=1353837&highlight=%E5%D0%D2%A3

JEB和Android Killer等:https://down.52pojie.cn/Tools/Android_Tools/

抓包工具我比较喜欢charles:https://www.52pojie.cn/forum.php?mod=viewthread&tid=1350618&highlight=charles

新人练手项目

样本APP:https://wwx.lanzoui.com/iEatQmeg8je

这个APP属于比较简单的逆向,没有混淆没有加壳,虽然加密调用了so,so里面也是用的java加密库。

准备工作

打开模拟器,安装APP。在模拟器上安装charles证书,因为Android7.0不在信任用户安装的证书,所以https可能无法正常抓到包或者无法正常显示数据包。我的解决办法:先正常安装证书,然后将安装好的证书直接移动到系统证书目录去。

  1. 证书获取:charles->help->SSL proxying->save …(保存到一个目录改个名字就行)
  2. 复制到模拟器:
    adb devices 查看adb设备
    adb connect 127.0.0.1:21503 连接逍遥模拟器
    adb push 电脑证书目录 /sdcard 将证书复制到模拟器sd卡根目录
  3. 安装证书:打开设置->安全->从sd卡安装证书
  4. 移动证书到系统目录:下载安装es文件管理器或者re管理器将安装的证书移动到系统目录,用户证书目录:/data/misc/user/0/cacerts-added。系统证书目录:/system/etc/security/cacerts。
  5. 关闭模拟器重新打开,设置代理后,当模拟器有http请求时,charles就会弹出验证,问你是否允许charles记录来自某某ip的包,点允许就会有数据包了。代理设置电脑局域网IP即可,并不需要模拟器使用桥接模式。
  6. 如果还是没有包,可以看看proxy->SSL proxying settings里Enable SSL proxying有没有勾选,并且在include里add一个*:*(host和port都填*就行)
  7. 还是没有的话就只能百度了

温馨提示:在抓APP的时候可以关闭charles抓Windows的包,点击proxy->Windows proxy就可以停止抓Windows的包,当然你在Windows设置里取消全局代理一样的效果。

打开APP,查看数据包

只看首页一些栏目列表的数据包,随便搜索一下首页出现的文章的标题就能找到是哪条请求了,

URL:https://v6-gw.m.163.com/nc/api/v1/feed/dynamic/normal-list

参数有很多,可以多刷新几个请求看看哪些参数是变化的,测试发现就ts和sign是变化的,不过devId、devIdOD、lat和lon明显都有可能被加密,我们先处理sign,ts明显是时间戳。另外请求头也有很多加密值。

打开jeb,分析代码

将apk拖入到jeb的窗口,然后一直点是就行。接着直接在反编译的smali代码里搜索链接中的一部分比如normal-list。Ctrl+f是搜索快捷键,搜索的时候勾选环绕搜索和区分大小写。

接着你就会发现只搜索到了一个地方,你再次点击寻找的时候还是在原来的地方。点击normal-list所在的那行代码右键->解析,就会反编译出java代码。如图:



g.d很有可能就是链接的前一部分了,那么com.netease.newsreader.common.constant.g.l.c就是完整的链接了,点击c,c的背景色会变成黄色,说明被选中了,然后右键->交叉引用,就能知道这个变量在什么地方被调用了。

可以看到有两个地方引用了这个变量,点进去看会发现第二个其实就是上面的代码,所以第一个应该就是发送请求的位置了。点进去看看代码,

代码很长,就看后面那一部分,前面的应该只是判断参数是否正常。正常就执行后面那个,能发出请求说明肯定正常。

a.a(b.b(l.c, new NGRequestVar().setSize(Integer.valueOf(arg4)).setFn(Integer.valueOf(arg5)).setOffset(Integer.valueOf(arg3)).addExtraParam(new c("from", arg2))), arg6);

把代码分开来看

t1 = new NGRequestVar().setSize(Integer.valueOf(arg4)).setFn(Integer.valueOf(arg5)).setOffset(Integer.valueOf(arg3)).addExtraParam(new c("from", arg2)));
t2 = b.b(l.c, t1);
a.a(t2, arg6)

t1应该就是一个请求参数的容器对象,可以点进去看看,addExtraParam是添加额外参数的方法,其他几个方法有啥用就不分析了。

接着看b.b这个方法,双击点进去看看。

看到这些参数就知道这就是拼接参数的地方,而我们要找的ts和sign都在这里生成的。ts确实是10位的时间戳,虽然他还调用了一个com.netease.newsreader.common.utils.a.a.a,但是抓包的结果和System.currentTimeMillis() / 1000L;是一样的,就不用关心他里面的逻辑了,估计就是整型转字符串的方法。

String v0 = d.a();
long v1_4 = System.currentTimeMillis() / 1000L;
String v0_1 = v0 + v1_4;
sign = b.a(com.netease.newsreader.framework.e.a.c.b(v0_1))

而点进去d.a()可以看到v0,就是下面的代码生成的也就是v0_1

public static String a() {Object v0 = d.v.get("nrcommon_sys_1");if(v0 != null && ((v0 instanceof String))) {return (String)v0;}String v0_1 = ((IGalaxyApi)b.a(IGalaxyApi.class)).a(Core.context());if(!TextUtils.isEmpty(v0_1) && (TextUtils.isEmpty(a.a()))) {a.a(v0_1);}d.a("nrcommon_sys_1", v0_1);return v0_1;
}

IGalaxyApi是自定义的一个类,点进去看b.a和b.a().a这两个方法都挺麻烦的,就不看了下去了。等下可以直接调试一下看看这个值会不会变,上面的代码将他放到一个容器里去获取,如果容器没有才生成,虽然不知道具体算法,但这说明这个值在APP启动之后就是个固定值了。

先把他当成固定值来看,也就是说v0_1是一个固定的字符串+时间戳。我们在看c.b这个方法

 public static String b(String arg2) {if(TextUtils.isEmpty(arg2)) {return arg2;}try {return c.a(MessageDigest.getInstance("MD5").digest(c.a(arg2, Charset.forName("UTF-8"))), false);}catch(NoSuchAlgorithmException v2) {throw new AssertionError(v2);}}

应该只是做个简单的md5,至于到底是不是可以调试一下看看输入和输出是不是md5的结果。最后就是b.a这个方法了。

private static String a(String arg0) {return com.netease.newsreader.support.utils.k.b.a(Encrypt.getEncryptedParams(arg0));}

com.netease.newsreader.support.utils.k.b.a这个方法只是做个简单的url编码,可以不用看,关键就是Encrypt.getEncryptedParams(arg0)
点进去看,关键代码就是这个:
Encrypt.encrypt(arg0, arg1, arg2);arg0是Core.context(),不清楚是什么,arg1就是上面传入的参数,arg2是0.而你在追encrypt方法时发现:

 private static synchronized native byte[] encrypt(Context arg0, String arg1, int arg2) {}

怎么什么都没有,百度了一下发现这是个native方法,方法逻辑在so里面,在前面有段System.loadLibrary("random");就是加载so文件。文件名叫librandom.so。可以在jeb左侧的工程管理器看到

Libraries目录下的就有(arm64-v8a和armeabi这两个里面的so基本逻辑是一样的,选择哪个都行),可以选中这个so右键导出,然后用IDA查看,左侧红框对应的就是java的函数。

这样反编译的c代码基本看不懂,不过可以看到一些关键的字符串。当然也可以导入jni.h头文件,可以让代码更像代码。具体参考:https://www.52pojie.cn/thread-732955-1-1.html。

AES/ECB/PKCS7Padding,javax/crypto/Cipher,doFinal这些字符足以说明他是调用了java的加密库,用的AES的加密。既然是调用的java加密库,我们可以用frida hook一下java的库,来获取key,然后验证一下结果对不对。后面测试发现确实就是这样一个加密。

jeb调试代码

准备工作
上面只是静态分析了代码,虽然知道他用的什么算法,但不确定他有没有魔改算法,或者做些手脚。所以我们需要获取参数的中间值来验证一下。这里介绍一下jeb的调试。

要想APP能被调试,需要APP AndroidManifest文件中包含 android:debuggable=“true”,而一般的APP肯定不会有这个,需要修改后重打包,很麻烦一般不会去这么做。这篇文章说明了所有开启调试的方法,写的很好:https://blog.csdn.net/qq_38851536/article/details/100026480。

我采用的是mprop来修改ro.debuggable这个值,不过模拟器关闭之后需要重新修改。
mprop x86版本:https://github.com/jedy/mprop
adb push mprop /data/local/tmp将mprop复制到模拟器的这个目录
adb shell
su
cd /data/local/tmp
chmod 755 mprop
./mprop ro.debuggable 1就可以了

为了方便我直接使用es文件浏览器mprop移动到了/system/bin下了,这样就可以全局使用了,不用cd到目录。

下断点

String v0_1 = v0 + v1_4;
if(!TextUtils.isEmpty(v0_1)) {arg6.add(new com.netease.newsreader.framework.d.a.c("sign", b.a(com.netease.newsreader.framework.e.a.c.b(v0_1))));}

我们主要是需要v0_1的值,和c.b计算之后的值,来验证一下c.b是不是普通的md5,还有v0这个值是不是固定的。右键String v0_1 = v0 + v1_4;这行代码点击解析就会跳到smali代码,

对照一下java代码很容易知道v0_1和c.b所在的位置。所以我们在图上框中的两行下面一行下两个断点,断点快捷键为Ctrl+b。左侧有红点表示断点下成功了。

这个浅蓝色背景是程序断在这一行的时候才会有的。图中我还没附加进程,应该是上次打断点留下的bug,不用管。

确认一下adb devices下有模拟器设备,没有的话adb connect 127.0.0.1:21503重新连接一下。然后打开APP,接着点击下图红框的那个按钮。


会出现这样一个界面

双击红框的那行就是附加了这个进程,这个是APP的包名,其他三个应该是这个APP的一些服务。

附加之后在模拟器里操作APP向下滑动加载新的文章就会触发断点。可能会多次出现下图这种情况,点击等待即可。

程序断下之后,我们关注的是v0的值,又知道它的数据类型是string,可以直接把int改成string,复制他的值备用,复制完记得改回int

点击第二个按钮运行,跳到下一个断点。同样改成string复制值之后改回int。

这样我们就得到两个值,计算一下上面的值md5确实就是下面的值,这也就验证了c.b只是做了md5的计算:
string@13512:“CQlkYTE0MDdjZmM5NTYyZjUzCTYwNTkxOTMw1614743514”
string@13513:“683c1311ef69f993fe29df30d7508fcf”


去抓包工具看一下sign,不知道哪条请求可以对比一下后面的时间戳。也可以直接在smali里面下断点获取。如何知道683c1311ef69f993fe29df30d7508fcf怎么得到sign(s1DyoPiJ5EPZK8gVtknxrQbP0ITFkr7O102aFhxwfIN48ErR02zJ6/KXOnxX046I)呢?

首先我们已经知道了他是AES/ECB/PKCS7Padding的加密模式,可以百度到这个加密模式只需要秘钥key,不需要iv。所以只要获取一下key,验证结果对不对了。

frida hook AES获取key

frida的环境就不说了,百度一下很简单的,pip装个包,在下载个x86版本的frida-server复制到模拟器改个权限运行就行。

直接拷贝官方文章中的一段代码,做些修改就可以了:https://frida.re/docs/examples/android/

修改之后,因为key是个java的对象,需要转成js可以输出的形式,一般是16进制或者base64:

import frida, sysdef on_message(message, data):if message['type'] == 'send':print("[*] {0}".format(message['payload']))else:print(message)jscode = """
function bytesToHex(arr) {var str = '';var k, j;for (var i = 0; i < arr.length; i++) {k = arr[i];j = k;if (k < 0) {j = k + 256;}if (j < 16) {str += "0";}str += j.toString(16);}return str;
}
Java.perform(function () {var Cipher = Java.use('javax.crypto.Cipher');var Exception = Java.use('java.lang.Exception');var Log = Java.use('android.util.Log');var init = Cipher.init.overload('int', 'java.security.Key');init.implementation = function (opmode, key) {var result = init.call(this, opmode, key);var bytes_key = key.getEncoded();console.log('Cipher.init() opmode:', opmode, 'key:', bytesToHex(bytes_key));////console.log(stackTraceHere());return result;};function stackTraceHere() {return Log.getStackTraceString(Exception.$new());}
});
"""process = frida.get_usb_device().attach('com.netease.newsreader.activity')
script = process.create_script(jscode)
script.on('message', on_message)script.load()
sys.stdin.read()

将上面的代码命名为hookaes.py,然后命令行运行python hookaes.py,在APP里面翻页查看输出。

有多个key,但是只有最后两个是我翻页的时候才出现的,其他的应该是另外的接口使用的。
我们找个在线AES加密的网站验证一下这个key是不是对的。注意: key是16进制(hex)格式的,不是字符串

https://the-x.cn/cryptography/Aes.aspx

结果和上面的是一样的,也就是说,到现在sign的加密已经分析完了。

frida hook获取中间值

上面获取v0_1是使用jeb进行调试来获取的,其实用frida获取更简单。
arg6.add(new com.netease.newsreader.framework.d.a.c("sign", b.a(com.netease.newsreader.framework.e.a.c.b(v0_1))));

想要获取v0_1和c.b的执行结果,直接hook c.b。代码如下:

import frida, sysdef on_message(message, data):if message['type'] == 'send':print(message['payload'])else:print(message)jscode = """
Java.perform(function () {send("注入成功!");var c = Java.use('com.netease.newsreader.framework.e.a.c');var b = c.b;b.overload('java.lang.String').implementation = function (v) {send(v);var result = b.call(this, v);send(result);return result;};});
"""process = frida.get_usb_device().attach('com.netease.newsreader.activity')
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
sys.stdin.read()

打开APP之后,运行这个Python脚本,然后在APP滑动加载即可显示参数和返回值,也就是我们要的。

按道理来说我也可以hook b.a来获取参数和返回值,但是试了一下没有效果,也不知道什么原因。如果有知道的,希望能指教下。

安卓逆向新人练手项目相关推荐

  1. python有趣的小项目-10 个最值得 Python 新人练手的有趣项目

    原标题:10 个最值得 Python 新人练手的有趣项目 作者 | Claire D. Costa 编译 | Wendy 有很多 Python 新手留言问:"Python 入门很久了,但项目 ...

  2. python练手经典100例-10 个最值得 Python 新人练手的有趣项目

    原标题:10 个最值得 Python 新人练手的有趣项目 作者 | Claire D. Costa 编译 | Wendy 有很多 Python 新手留言问:"Python 入门很久了,但项目 ...

  3. python游戏制作软件_10 个最值得 Python 新人练手的有趣项目

    原标题:10 个最值得 Python 新人练手的有趣项目 作者 | Claire D. Costa 编译 | Wendy 有很多 Python 新手留言问:"Python 入门很久了,但项目 ...

  4. python入门程序有趣例子_10 个最值得 Python 新人练手的有趣项目

    原标题:10 个最值得 Python 新人练手的有趣项目 作者 | Claire D. Costa 编译 | Wendy 有很多 Python 新手留言问:"Python 入门很久了,但项目 ...

  5. 安卓毕业设计-图书馆管理系统-新手练手项目

    图书馆管理系统 目的:1,用于毕业设计 2,用于用于新手的练手项目 开发环境:win7,android studio3.0.1,模拟器api:7.0.1 开发语言:Java 代码量:Java代码500 ...

  6. linux系统编程练手项目,精选 22 个 C++ 项目,编程小白练手首选!

    C/C++ 做为元老级的编程语言,任时光更迭依旧屹立不倒,哪怕现在煊赫一时的AI,其底层也是用其编写.linux 那么做为新手该如何快速上手 C++ 呢?固然是敲代码啊!一切不写代码的学编程都是瞎搞. ...

  7. 【干货分享】嵌入式学习路线公开!(书籍推荐+视频推荐+练手项目)

    哈喽,大家好,我是仲一.最近有小伙伴在微信私信我,如何学习嵌入式.一直想写一篇学习路线的文章,由于各种原因拖到了现在.趁着国庆节在家,终于肝完了这篇文章. 我个人是从STM32转到驱动开发的,在研二的 ...

  8. 70个Python练手项目列表 预祝大家 快乐

    小孩眺望远方,成人怀念故乡. 为此给大家分享一下珍藏的Python实战项目,祝大家节日快乐哦!!! Python 前言:不管学习哪门语言都希望能做出实际的东西来,这个实际的东西当然就是项目啦,不用多说 ...

  9. 一个适合于Python 初学者的入门练手项目

    随着人工智能的兴起,国内掀起了一股Python学习热潮,入门级编程语言,大多选择Python,有经验的程序员,也开始学习Python,正所谓是人生苦短,我用Python 有个Python入门练手项目, ...

最新文章

  1. .Net开源 Shuttle(飞梭)服务总线(ESB)入门
  2. [Linux] 编写Dockerfile文件自动构建镜像
  3. Delphi 关键 重启 注销
  4. 数控车椭圆编程实例带图_数控车床编程教程,图文实例详解
  5. 请假时碰到法定假期,实际请假几天?
  6. 广西计算机一级机试考试试题,2010年12月广西区计算机一级考试机试试题
  7. python学习笔记之读取pdf文件库pdfplumber(一)
  8. 半导体丨索尼推出世界最小监控用CMOS图像传感器IMX415
  9. 中兴c语言 面试题,中兴手机嵌入式开发面试题汇总(1)
  10. flowplayer播放需求
  11. pytorch函数详解 附带测试demo
  12. Python3:类和对象-烤地瓜
  13. 手动删除文件夹exe病毒并恢复原来文件夹
  14. 给一个不多于5位的正整数,要求:一、求它是几位数,二、逆序打印出各位数字。
  15. 超简单的vue3.0,必看文档
  16. wxpython入门(1)
  17. java.net.Url类的应用
  18. jsp页面如何调用本机的应用程序?例如c:/netterm.exe?(转载)
  19. SQL错误(1366):Incorrect String Value
  20. 在数据库插入大量不同数据

热门文章

  1. 【安装配置】安装适用于 Linux 的 Windows 子系统 WSL ,完成 Clion 中对内存泄漏检测工具 Valgrind 的配置,亲测可用
  2. python基础—列表元组作业题
  3. ValueError: Connection error, and we cannot find the requested files in the cached path. Please...
  4. c语言实现新建目录函数,C语言中改变目录的相关操作函数详解
  5. 在线sqli-labs 通关大全 Less-1
  6. Kubenetes学习笔记之Pod(下)
  7. 【qstock量化】技术形态与概念热点选股池
  8. AD如何生成Gerber文件和钻孔文件
  9. 拼多多API接口,Onebound数据
  10. 镜像画笔-第13届蓝桥杯Scratch国赛真题第2题