This series of articles is a practice note for Android Software Security and Reverse Analysis (versions and uses of some of the tools have changed), and I want to systematically document my thoughts and words on reverse analysis.
Reverse engineering (also known as reverse engineering) is a technical process, that is, reverse analysis and research of a target product, so as to deduce and obtain the processing process, organizational structure, functional performance specifications and other design elements of the product, so as to produce products with similar functions but not exactly the same. Reverse engineering has its roots in hardware analysis in business and military fields. Its main purpose is to derive product design principles directly from the analysis of finished products when necessary production information is not readily available.
Reverse engineering may be mistaken for a serious infringement of intellectual property, but in practice it may actually protect IP owners. In the field of integrated circuits, for example, if a company is suspected of infringing intellectual property rights, reverse engineering can be used to find evidence. — [Wikipedia]
To get started, I’ll talk about simple decompilation. Decompiler usually has several purposes: learn from each other, borrow from each other, hey hey (dry you, also divided into small dry similar to wechat red envelope, and dry dry change other people’s APK help him put on the shelf).
Since THE MBPR screen is too small without KVM, the following environment is Windows.
Let’s get the ball rolling and decompile an APK to see some functionality in action. After all, there is no practice of the principle is playing rogue. Here we keep the mentality of learning from each other, so it is the first purpose of friendship, hee hee.
To prepare
The installation package
- Mobile QQ 6.2.3 (the goal is to see how the password red envelope is done)
The use of Apktool
First make sure you have Java 7 or above installed and can call Java directly from the command line.
- Download the Wrapper script for Windows (MAC uses this).
- Download the latest ApkTool.
- Rename the apkTool JAR file downloaded above to
apktool.jar
. - Bat and apktool.jar in the same directory and add the PATH environment variable.
- Now you can call it directly from the command line
apktool
And see how it’s used.
Apktool v2.0.3 -a tool for Reengineering Android APK files with smali v2.1.0 and Baksmali v2.1.0 Usage: apktool -advance,--advanced prints advance information. -version,--version prints the version then exits usage: apktool if|install-framework [options] -p,--frame-path Stores framework files into . -t,--tag Tag frameworks using . usage: apktool d[ecode] [options] -f,--force Force delete destination directory. -o,--output The name of folder that gets written. Default is apk.out -p,--frame-path Uses framework files located in . -r,--no-res Do not decode resources. -s,--no-src Do not decode sources. -t,--frame-tag Uses framework files tagged by . usage: apktool b[uild] [options] -f,--force-all Skip changes detection and build all files. -o,--output The name of apk that gets written. Default is dist/name.apk -p,--frame-path Uses framework files located in .Copy the code
The use of jadx
- Download jadx.
- Run gradlew dist compilation.
jadx\jadx-gui\build\install\jadx-gui\bin
There is a running GUI underjadx\jadx-cli\build\install\jadx\bin
It’s a command line program- You can add the PATH environment variable for direct command line invocation.
Analyze APK files
First Try
Although we can use JADX directly open APK foolproof to view the source code, but in order to better understand the decomcompiling process and working principle, so that in the future when we encounter some problems (such as shell) can be solved by themselves, here we first install force, using Apktool to analyze.
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/name at 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)Copy the code
Multiple res specs: Attr /name, on the Internet to find information, should be Tencent use Apktool bug to shell, in addition to add the same name ID also made a number of reinforcement, good, you malicious, our next article for Tencent shell to analyze and modify Apktool, this time using JADX to try.
Second Try
If you open QQ apK directly with JadX-GUI, you will find that the card is dead. Yeah, it’s stuck because it’s so big…
Set JAVA_OPTS= -server-xMS1024m -XMx8192m -xx :PermSize=256m -xx :MaxPermSize=1024m
Much the same way we speed up AS/IDEA so that it opens smoothly (which may take longer).
String method
To find our target, the red envelope, we first try a string search: Resources -> resources.arsc -> res -> values -> strings.xml to find the corresponding password red envelope
Password a red envelopeCopy the code
Crtl+Shift+F Text Search… Didn’t find it.
Let’s use the resource ID method again and find it directly in resources.arsc
0x7F0A0E5A (2131365466) = string. qb_HBDetail_command_WORD: password red packetCopy the code
Search again. Okay, you’re so… Still not. I lost.
Large method for class/function names
The second killer is class/function/variable name search.
Class names are usually more narrow, so just use Class first.
RedPacket (class name so R and P capitals)
Okay, we got a dozen. Let’s go through them. Number oneRedPacketInfo
Click on it to see the VO class that contains various fields for UI, skip, and look at the next one, from the package namecom.tencent.mobileqq.data
It looked like it was going to happen,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; . Serialization, read and write, build methods, etc., can be ignored.Copy the code
Redtype specifies the red envelope type.
We used the keyword redtype to search again. This time, we selected Code and only searched within the Code. However, the result was found to be wrong.
Search QQWalletRedPacketMsg using Field, excluding the Class itself, only the only result: 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; .Copy the code
We found a constant field that visually describes whether the password is a red envelope. This field is also found in the class search
public static boolean isCommandRedPacketMsg(MessageRecord messageRecord) { if (messageRecord ! = null && (messageRecord instanceof MessageForQQWalletMsg) && ((MessageForQQWalletMsg) messageRecord).messageType == MSG_TYPE_COMMAND_REDPACKET) { return true; } return false; }Copy the code
Sure enough, we went on to look for MSG_TYPE_COMMAND_REDPACKET and isCommandRedPacketMsg, respectively, The TroopMessageManager found only a reference to the method isCommandRedPacketMsg in a piece of code that failed to decompress:
L_0x0100:
r2 = com.tencent.mobileqq.data.MessageForQQWalletMsg.isCommandRedPacketMsg(r25);
if (r2 == 0) goto L_0x011e;Copy the code
Here the red envelope will continue if it is the password, but if it is not, it will jump to L_0x011e.
From the name of the class, TroopMessageManager should refer to the group message manager, should be right, after all, red envelope is also a group of messages.
So we’re just going to have to patiently watch this amazing goto-filled code. After a bit of a giddy-up, you can probably see the various logical judgments and calls to Msgproxyutils.java to handle message processing logic and caching. And then it was gone… All right, you son of a bitch. I’ll try something else.
Constant solution
In fact, the constant method can also be regarded as a string search, but do not search XML, but use the Chinese character string converted to Unicode to search. Do your own search for Unicode convertor to find Online Convertor.
U53e3 \ u4EE4 \ U7eA2 \ U5305 “:
Find 2 classes with 3 code references.
The last class has an interesting name, 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);
}Copy the code
When translated into Chinese, this string of Unicode is “anonymous can’t rob password red envelopes”.
Here we look again at the top of the class and see that there is a method called log under onDestroy that is amazing:
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 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; }Copy the code
IsColorLevel visual is a debug with the tag, which may be some users will open some environment, and we usually play the paper from the log log habits, this method should be called openPasswdRedBagByPassword, the second parameter is the password. Finally found it. The logic is to load all the red packets from the outside into the class’s various hashmaps and lists, and then find the corresponding passwdRedBagInfo from the inside according to the password. Set the result tag and call
b(sessionInfo.a, sessionInfo.f1757a, passwdRedBagInfo.a.string_redbag_id.get().toStringUtf8());
a(sessionInfo, passwdRedBagInfo);Copy the code
Let’s not worry about what these two methods do. Moving on to the next method, it goes straight to:
public long[] b(SessionInfo sessionInfo, String str) {
if (QLog.isColorLevel()) {
QLog.d(f2197a, (int) h, "openPasswdRedBagById, id = " + str);
}Copy the code
OpenPasswdRedBagById Opens the red envelope with an ID, guessing that the ID is the redPacketId field in the structure we first saw.
This method is also called
b(sessionInfo.a, sessionInfo.f1757a, str);
a(sessionInfo, passwdRedBagInfo);Copy the code
Take a look at these two methods:
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); }}}}Copy the code
Found the first method seems to directly send request, it seems that as long as the call to here, is can get a red envelope. So how did you get here in the first place? We search for references to these two methods in PasswdRedBagManager to find 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) {// call the following method! 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; 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; }Copy the code
It can be seen that every time we input the message to send, we have made a judgment, we will check whether it is the password of the red envelope, if it is, we will directly send a request to get the red envelope and then continue, otherwise we will directly send as a common message to continue. So if you want to do automatic red envelope grab, in fact, as long as you directly receive the message, call PasswdRedBagManager open method, even simulate UI, generate requests, send messages are not necessary, we no longer have to say password with conscience.
After the above, we successfully decompile mobile QQ, and trace the data structure and judgment process of mobile QQ red packets. Experienced several times during the useless, but reverse engineering is all about, especially the static analysis, if not in time to look the other way, and bore tip all the way to see all the way from a clue, is likely to be deeper, tracking the process of this paper is constantly in the drilled hole was a little child, and then to find the path of the other, the last to quickly find the want to see.
As for the next issue, maybe smali, maybe how to modify Apktool, maybe jADX source code analysis, depending on the mood, haha.
Reference: www.mak-blog.com/tencent-she… www.kanxue.com/bbs/showthr…
Original text: blog. Zhaiyifan. Cn / 2016/02/09 /…