原文:http://blog.zhaiyifan.cn/2016/02/09/android-reverse-1/

本系列文章是《Android软件安全与逆向分析》的实践笔记(一些工具的版本和用法已经不同了),同时笔者希望系统性地记录一下逆向方面的文字和想法。

逆向工程(又称反向工程),是一种技术过程,即对一项目标产品进行逆向分析及研究,从而演绎并得出该产品的处理流程、组织结构、功能性能规格等设计要素,以制作出功能相近,但又不完全一样的产品。逆向工程源于商业及军事领域中的硬件分析。其主要目的是,在不能轻易获得必要的生产信息下,直接从成品的分析,推导出产品的设计原理。

逆向工程可能会被误认为是对知识产权的严重侵害,但是在实际应用上,反而可能会保护知识产权所有者。例如在集成电路领域,如果怀疑某公司侵犯知识产权,可以用逆向工程技术来寻找证据。 —— [ 维基百科 ]

第一篇作为开始,先讲讲简单的反编译。
因为没带kvm回来,mbpr屏幕太小,所以下文环境为windows。

反编译

让我们从实战开始,先实践一下怎么去反编译一个apk,看看某些功能的实现,甚至打一个包吧。毕竟没有实践的原理都是耍流氓。

准备

工具
- Apktool
- jadx

安装包
- 手机QQ 6.2.3 (目标就设定为看看口令红包是怎么做的吧)

Apktool的使用

首先确保你安装了java 7或以上,并能直接在命令行调用java

  1. 下载 windows用wrapper脚本 (mac使用这个)。
  2. 下载最新的apktool。
  3. 重命名上面下载的apktool jar文件为apktool.jar
  4. 把apktool.bat和apktool.jar放在同一个目录下,并加入PATH环境变量。
  5. 现在你可以直接通过命令行调用apktool并查看使用方式了。
Apktool v2.0.3 - a tool for reengineering Android apk files
with smali v2.1.0 and baksmali v2.1.0usage: apktool-advance,--advanced   prints advance information.-version,--version    prints the version then exitsusage: apktool if|install-framework [options] <framework.apk>-p,--frame-path <dir>   Stores framework files into <dir>.-t,--tag <tag>          Tag frameworks using <tag>.usage: apktool d[ecode] [options] <file_apk>-f,--force              Force delete destination directory.-o,--output <dir>       The name of folder that gets written. Default is apk.out-p,--frame-path <dir>   Uses framework files located in <dir>.-r,--no-res             Do not decode resources.-s,--no-src             Do not decode sources.-t,--frame-tag <tag>    Uses framework files tagged by <tag>.usage: apktool b[uild] [options] <app_path>-f,--force-all          Skip changes detection and build all files.-o,--output <dir>       The name of apk that gets written. Default is dist/name.apk-p,--frame-path <dir>   Uses framework files located in <dir>.

jadx的使用

  1. 下载jadx。
  2. 运行gradlew dist编译。
  3. jadx\jadx-gui\build\install\jadx-gui\bin下有可运行的gui
  4. jadx\jadx-cli\build\install\jadx\bin是命令行程序
  5. 可以都加入PATH环境变量,以便直接命令行调用。

分析APK文件

First Try

虽然我们可以用jadx直接打开apk傻瓜式地去查看源代码,但是为了更理解反编译的过程和工作原理,以便以后在碰到一些问题(比如加壳)的时候可以自己解决,这里我们先装逼一下,使用Apktool去进行分析。

D:\dev\reverse>apktool d -o qq mobileqq_android_6.2.3.apk
I: Using Apktool 2.0.3 on mobileqq_android_6.2.3.apk
I: Loading resource table...
Exception in thread "main" brut.androlib.AndrolibException: Multiple res specs: attr/nameat brut.androlib.res.data.ResTypeSpec.addResSpec(ResTypeSpec.java:78)at brut.androlib.res.decoder.ARSCDecoder.readEntry(ARSCDecoder.java:248)at brut.androlib.res.decoder.ARSCDecoder.readTableType(ARSCDecoder.java:212)at brut.androlib.res.decoder.ARSCDecoder.readTableTypeSpec(ARSCDecoder.java:154)at brut.androlib.res.decoder.ARSCDecoder.readTablePackage(ARSCDecoder.java:116)at brut.androlib.res.decoder.ARSCDecoder.readTableHeader(ARSCDecoder.java:78)at brut.androlib.res.decoder.ARSCDecoder.decode(ARSCDecoder.java:47)at brut.androlib.res.AndrolibResources.getResPackagesFromApk(AndrolibResources.java:544)at brut.androlib.res.AndrolibResources.loadMainPkg(AndrolibResources.java:63)at brut.androlib.res.AndrolibResources.getResTable(AndrolibResources.java:55)at brut.androlib.Androlib.getResTable(Androlib.java:66)at brut.androlib.ApkDecoder.setTargetSdkVersion(ApkDecoder.java:198)at brut.androlib.ApkDecoder.decode(ApkDecoder.java:96)at brut.apktool.Main.cmdDecode(Main.java:165)at brut.apktool.Main.main(Main.java:81)

竟然报错了,Multiple res specs: attr/name,在网上找了找资料,应该是腾讯利用Apktool的bug去进行了加壳,除了添加同名id外还做了若干加固,好,你狠,我们下篇文章针对腾讯的壳来分析并修改Apktool,这次先用jadx来试试。

Second Try

如果直接用jadx-gui打开QQ的apk,你会发现,卡死了。不错,就是卡死了,因为太大了…

我们打开jadx-gui文件(其实就是个启动的script),加上:
set JAVA_OPTS=-server -Xms1024m -Xmx8192m -XX:PermSize=256m -XX:MaxPermSize=1024m

就跟我们加速as/idea差不多,这样就能顺利地打开了(可能会需要比较久的时间)。

字符串大法

为了找到我们的目标,红包,我们首先尝试用字符串搜索大法:在Resources -> resources.arsc -> res -> values -> strings.xml找到口令红包对应的

<string name="qb_hbdetail_command_word">口令红包</string>

然后Crtl+Shift+F进行Text Search,结果…没找到。

我们再使用资源id大法,直接在resources.arsc找到

0x7f0a0e5a (2131365466) = string.qb_hbdetail_command_word: 口令红包

再搜,好,你狠。。。还是没有。是在下输了。

类/函数名大法

我们再祭出第二大杀器,类/函数/变量名大法搜索大法。

通常类名符合的范围更小,所以先只使用Class。
试试看红包的英语:RedPacket(类名命名所以R和P大写)

OK,我们找到了十几条,开始逐一排查,第一条RedPacketInfo点进去一看就是个包含了各种field的ui用的vo类,跳过,再看下一个,从包名com.tencent.mobileqq.data看上去,似乎有戏,QQWalletRedPacketMsg:

package com.tencent.mobileqq.data;import android.text.TextUtils;
import com.tencent.mobileqq.hotpatch.NotVerifyClass;
import cooperation.qzone.util.WiFiDash;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import tencent.im.msg.im_msg_body.QQWalletAioBody;/* compiled from: ProGuard */
public class QQWalletRedPacketMsg {public String authkey;private int channelId;public int conftype;public QQWalletTransferMsgElem elem;public String envelopeName;public int envelopeid;public boolean isOpened;public int msgFrom;public String redPacketId;public int redtype;private int resend;public int templateId;...串行化、读写、构建方法等,可以无视。

从field名来看,这里还是比较可疑的,猜测redtype是不是描述红包类型的。

我们再次使用关键词redtype进行搜索,这次选择Code,只进行代码内搜索,结果却发现貌似不对,找到相关的字符串是”查看详情”,貌似是描述红包领取状态的。

不放弃,继续抓住QQWalletRedPacketMsg这个类进行搜索,看看是不是有外面包着这个类的Class,搜索QQWalletRedPacketMsg,范围使用Field,排除掉类本身外,只有唯一的结果:MessageForQQWalletMsg:

public class MessageForQQWalletMsg extends ChatMessage {// 哦哦?COMMAND_REDPACKET?口令红包public static final int MSG_TYPE_COMMAND_REDPACKET = 6;public static final int MSG_TYPE_COMMON_REDPACKET = 2;public static final int MSG_TYPE_COMMON_THEME_REDPACKET = 4;public static final int MSG_TYPE_INDIVIDUAL_REDPACKET = 2001;public static final int MSG_TYPE_LUCY_REDPACKET = 3;public static final int MSG_TYPE_LUCY_THEME_REDPACKET = 5;public static final int MSG_TYPE_PUBLIC_ACCOUNT_REDPACKET = 2002;public static final int MSG_TYPE_TRANSFER = 1;...

我们找到了一个常量字段,目测就是这个描述了是否是口令红包了。在该类搜索此字段还找到

public static boolean isCommandRedPacketMsg(MessageRecord messageRecord) {if (messageRecord != null && (messageRecord instanceof MessageForQQWalletMsg) && ((MessageForQQWalletMsg) messageRecord).messageType == MSG_TYPE_COMMAND_REDPACKET) {return true;}return false;
}

果然,我们再接着分别查找MSG_TYPE_COMMAND_REDPACKETisCommandRedPacketMsg,结果只在TroopMessageManager里面找到了一段没成功反编译的代码中对方法isCommandRedPacketMsg的引用:

L_0x0100:r2 = com.tencent.mobileqq.data.MessageForQQWalletMsg.isCommandRedPacketMsg(r25);if (r2 == 0) goto L_0x011e;

这里如果是口令红包会继续走下去,而如果不是则会跳到L_0x011e。

而从类的名字来看,TroopMessageManager应该是指群消息管理者,应该没错,毕竟红包也是群消息的一种。

于是我们只能耐心地看下去这段神奇的充满goto的代码。晕着看完后大概看到就是各种逻辑判断和调用MsgProxyUtils.java去处理消息处理逻辑和缓存。然后就没了…好,你屌,是在下输了。我再试试别的。

常量大法

常量大法其实也可以算是字符串搜索的一种,只是不去搜索xml里的,而是使用中文转化为unicode后的字符串去进行查找。自行搜索Unicode编码转化可以找到online convertor。

口令红包对应的是”\u53e3\u4ee4\u7ea2\u5305”:

找到2个类共3处代码引用。

最后那个类的起名有点耐人寻味,PasswdRedBagManager,密码红包管理器,有点意思:

public void b(String str) {((TroopTipsMsgMgr) this.f2203a.getManager(80)).a(str, "\u533f\u540d\u4e0d\u80fd\u62a2\u53e3\u4ee4\u7ea2\u5305\u54e6", NetConnInfoCenter.getServerTime(), BaseConstants.DEFAULT_QUICK_HEARTBEAT_TIMEOUT, f);
}

这里”\u533f\u540d\u4e0d\u80fd\u62a2\u53e3\u4ee4\u7ea2\u5305\u54e6”转换成中文后是”匿名不能抢口令红包哦”,原来还有这种逻辑,产品经理你真是够了。

这里我们重新从该类的上面看下来,大致扫一扫,发现onDestroy下面有一个方法打的log很神奇:

public long[] m883a(SessionInfo sessionInfo, String str) {if (QLog.isColorLevel()) {QLog.d(f2197a, (int) h, "openPasswdRedBagByPassword, passwd = " + str);}long[] jArr = new long[]{0, 0};if (sessionInfo == null) {return jArr;}if (TextUtils.isEmpty(str)) {return jArr;}c();List<String> list = (List) this.f2206a.get(str);if (list == null || list.isEmpty()) {return jArr;}PasswdRedBagInfo passwdRedBagInfo;String str2 = a(sessionInfo.a) + "_" + sessionInfo.f1757a;for (String str3 : list) {HashMap hashMap = (HashMap) this.f2209b.get(str3);if (hashMap != null) {passwdRedBagInfo = (PasswdRedBagInfo) hashMap.get(str2);if (!(passwdRedBagInfo == null || a(str3))) {jArr[g] = passwdRedBagInfo.a.uint64_creator_uin.get();if (!b(str3)) {if (!c(str3)) {hashMap.put(str2, passwdRedBagInfo);jArr[f] = 1;break;}jArr[f] = 3;} else {jArr[f] = 2;}}}}passwdRedBagInfo = null;if (passwdRedBagInfo == null) {return jArr;}b(sessionInfo.a, sessionInfo.f1757a, passwdRedBagInfo.a.string_redbag_id.get().toStringUtf8());a(sessionInfo, passwdRedBagInfo);return jArr;
}

isColorLevel目测是某种debug用的tag,可能某些环境下部分用户会打开,而从log结合我们平时打log习惯来看,这个方法应该就叫openPasswdRedBagByPassword了,第二个参数就是password。终于找到了。看一下逻辑大致是从外面load进来所有红包信息到本类的各种hashmap和list(有一个tag,只会加载第一次,本类多个方法都会调用这个方法),然后根据password从里面找到对应passwdRedBagInfo,设置result tag,然后调用了

b(sessionInfo.a, sessionInfo.f1757a, passwdRedBagInfo.a.string_redbag_id.get().toStringUtf8());
a(sessionInfo, passwdRedBagInfo);

我们先不急看这两个方法是做什么的。再往下看下一个方法,直接就有:

public long[] b(SessionInfo sessionInfo, String str) {if (QLog.isColorLevel()) {QLog.d(f2197a, (int) h, "openPasswdRedBagById, id = " + str);}

openPasswdRedBagById用id打开红包,猜测该id就是我们最早看到的结构里的redPacketId字段。

而该方法同样调用了

b(sessionInfo.a, sessionInfo.f1757a, str);
a(sessionInfo, passwdRedBagInfo);

看看这两个方法:

public void a(SessionInfo sessionInfo, PasswdRedBagInfo passwdRedBagInfo) {if (sessionInfo != null && passwdRedBagInfo != null) {Object obj = (sessionInfo.a == 0 || sessionInfo.a == h || sessionInfo.a == Action.ACTION_REGISTNEWACCOUNT_COMMITSMS || sessionInfo.a == Action.ACTION_LOGIN) ? g : null;String str = sessionInfo.f1757a;String valueOf = String.valueOf(passwdRedBagInfo.a.uint64_creator_uin.get());if (obj != null) {str = valueOf.equals(this.f2213d) ? sessionInfo.f1757a : this.f2213d;}JSONObject a = QQWalletMsgItemBuilder.a(this.f2203a, sessionInfo, passwdRedBagInfo.a.string_redbag_id.get().toStringUtf8(), passwdRedBagInfo.a.string_authkey.get().toStringUtf8(), str, "appid#1344242394|bargainor_id#1000030201|channel#msg", "graphb", null);Bundle bundle = new Bundle();bundle.putString("json", a.toString());bundle.putString("callbackSn", jbi.a);Intent intent = new Intent(this.f2200a, PayBridgeActivity.class);intent.putExtras(bundle);intent.addFlags(268435456);intent.putExtra("pay_requestcode", 5);this.f2200a.startActivity(intent);}
}public void b(int i, String str, String str2) {if (!TextUtils.isEmpty(str2)) {HashMap hashMap = (HashMap) this.f2209b.get(str2);if (hashMap != null) {PasswdRedBagInfo passwdRedBagInfo = (PasswdRedBagInfo) hashMap.get(a(i) + "_" + str);if (passwdRedBagInfo != null && !passwdRedBagInfo.f4810a) {passwdRedBagInfo.f4810a = true;ThreadManager.a(new kmr(this, str2), h, null, true);}}}
}

发现第一个方法似乎就直接发请求了,看来只要调用到这里,就是可以领红包了。那最初又是如何来这里的呢?我们搜索对PasswdRedBagManager内这两个方法的引用找到BaseChatPie.java:

...
public PasswdRedBagManager f25190a;
...
public class EnterForSend implements OnKeyListener, OnEditorActionListener {...// 这里从方法名判断是每次输入后public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {if (i != BaseChatPie.dr) {return false;}String obj = this.a.f25220a.getText().toString();if (obj.length() > 0) {// 调用了下面的方法!long[] a = this.a.a(obj);SendMsgParams sendMsgParams = new SendMsgParams();sendMsgParams.b = this.a.dL;sendMsgParams.a = this.a.dJ;sendMsgParams.c = this.a.dN;sendMsgParams.f26863c = this.a.dL;...}return true;}
}// 这里调用了那2个openPasswdRedBagxxx方法
public long[] m5613a(String str) {long[] jArr = null;if (!AnonymousChatHelper.a().a(this.f25174a.a)) {if (TextUtils.isEmpty(this.f25269d) || !str.equals(this.f25278e)) {jArr = this.f25190a.a(this.f25174a, str);} else {jArr = this.f25190a.b(this.f25174a, this.f25269d);}if (jArr != null && jArr[s] == 1) {this.f25269d = QunUppUploadTask.QunUppAppId;this.f25278e = QunUppUploadTask.QunUppAppId;this.f25228a.sendEmptyMessage(dz);if (QLog.isColorLevel()) {QLog.d(PasswdRedBagManager.a, u, "passwdredbags result[0]=" + jArr[s] + ",result[1]=" + jArr[t] + ",send str=" + str);}}} else if (QLog.isColorLevel()) {QLog.d(PasswdRedBagManager.a, u, "current is in Anonymous, dont search passwdredbags");}return jArr;
}

可见每次我们输入消息发送时,都发生了判断,会去查询是不是红包口令,如果是则直接发请求拿红包然后继续,否则直接当做普通消息继续发送。

总结和下期预告

经过长久的跟踪,我们成功反编译了手机QQ,并追溯到手机QQ红包的数据结构和判断流程。期间经历过数次无用功,但逆向工程正是这么一回事,尤其是静态分析,如果不及时找其他的路,而一路钻牛角尖从一个线索一路去看,很可能会越陷越深,本文的跟踪流程正是不断在坑还小的时候钻出来,然后去找其他的路径,最后才快速地找到了想看的东西。

至于下一期,可能是smali,可能是怎么修改Apktool,也可能是jadx的源码分析,看心情吧,哈哈。


参考资料:
http://www.mak-blog.com/tencent-shell-crack.html
http://www.kanxue.com/bbs/showthread.php?p=1390763&langid=3

Android逆向分析(一) - 反编译看看手Q口令红包的实现原理相关推荐

  1. Android逆向分析(1) 反编译看看手Q口令红包的实现原理

    前言 本篇文章是作者MarkZhai的逆向分析系列的第一篇,已授权发布,并计划之后该系列的更新会第一时间发布在本公号上,敬请关注! 原文 本系列文章是<Android软件安全与逆向分析>的 ...

  2. jadx重新打包_Android反编译看看手Q口令红包的实现原理

    首篇作为开始,先讲讲简单的反编译.反编译通常有几种目的:互相学习.借来用用.嘿嘿(干你,又分为小干干类似微信红包,和大干干改别人的apk帮他上架). 因为没带kvm回来,mbpr屏幕太小,所以下文环境 ...

  3. Android逆向之旅—反编译利器Apktool使用教程(Apktool的安装使用)建议新手浏览

    文章目录 一.下载软件 第一步(apktool.bat) 第二步(apktool.jar) 二.安装软件 三.使用软件 解包 修改文件 重打包 签名 生成keystore文件 进行签名 四.使用 一. ...

  4. android逆向分析之从smali到java

    通过上一篇 android逆向分析之反编译,在dex2jar的前提下,我们获取到了源码,在apktool的前提下,我们可以获取到资源和smali文件,本篇主要讲述smali几个最基本的知识和smali ...

  5. linux反编译unity手游,Unity3D安卓手游逆向

    本文就是记录自己的一次瞎胡闹!! 随便在网上找了一款使用Unity3D 开发的Android 手游,其运行效果是这样的 Unity3D逆向工具 使用ApkDb 反编译apk 文件(在apk 上右键–& ...

  6. Android逆向去掉某功能,逆向教程之-反编译apk完全精简删除菜单功能(二)

    本帖最后由 liuxiaoxin 于 2020-12-3 19:00 编辑 授人以鱼,不如授人以渔! 本教程图文并茂,步骤非常详细,偏小白向,大佬请自觉屏蔽. 使用工具:MT管理器免费版 被修改的软件 ...

  7. Android 逆向分析大全

    转载:Android 逆向分析大全:https://www.jianshu.com/p/a12d04fc748f 1. 概述 1.1 分析步骤 通用逆向分析步骤 1. 了解该模块正向编程相关方法 2. ...

  8. android 常用编译工具,Android 抖音常用反编译工具

    Android 抖音常用反编译工具 常用反编译工具 apktool :反编译apk,重构. dex2jar :反编译apk,解压 classes.dex 文件 JD-GUI :将class文件反编译成 ...

  9. android逆向分析概述_Android存储概述

    android逆向分析概述 Storage is this thing we are all aware of, but always take for granted. Not long ago, ...

最新文章

  1. python重难点之装饰器详解
  2. 基于图像的三维模型重建——相机模型与对极几何
  3. 怎么能方便的进行数据库存储过程的版本管理?
  4. python读取excle表格数据,将数据编辑到图像上工程
  5. 学习逆向知识之用于游戏外挂的实现.第二讲,快速寻找植物大战僵尸阳光基址.以及动态基址跟静态基址的区别...
  6. Vue常用经典开源项目
  7. Flutter 常用的按钮组件
  8. JAVA CLASS混淆工具:Allatori 简单试用
  9. Visual Studio Code 取色器插件 取色选取 插件安装和使用
  10. 06-maven的profile和Spring boot 的profile整合
  11. pdf阅读器与迅捷pdf编辑器的使用方法
  12. 去除【CSDN论坛】【博客】所有广告的方法(非会员or非专家/版主 也可使用哦)
  13. Uncaught ReferenceError: is not defined
  14. 项目总结Word基本格式
  15. 关于激活函数的思考(zig-zagging dynamics)
  16. 计算机发展简史 计算机的发展历史介绍
  17. Java 数组元素倒序的三种方式
  18. 【论文精读3】MVSNet系列论文详解-P-MVSNet
  19. Ubuntu搜狗输入法乱码问题
  20. 计算机网络信息安全总结报告,信息安全自查工作总结报告

热门文章

  1. 手机上的电子词典:PPC版本金山词霸
  2. C++ Primer Plus Chapter 4 --复合类型(笔记)
  3. 安卓·设置navigation bar的颜色(setColor方法)
  4. WebContainers简介:在浏览器中原生运行Node.js(译文)
  5. java三维滑雪_知到电工电子学山东联盟第二单元章节测试答案
  6. 完整的从虚拟机安装到kali的安装(详细,必能上网)
  7. 本科毕业论文不会写怎么办?
  8. 联想E431笔记本wifi驱动安装
  9. 网站赞助打shang单页HTML源码
  10. leetcode738 单调递增的数字