
com.smali.secretchallenge包中实现SecretBootReceiver类,使得这个应用程序能在安卓开机时自动运行,并且自动启动一个后台程序。 在com.smali.secretchallenge中实现SecretService类,这个被SecretBootReceiver启动。这个类的功能包括:获取设备的GPS位置,每3秒显示把位置的经纬度显示到屏幕上。
在应用程序运行时前,需要获得位置权限。在com.smali.secretchallenge包的MainActivity类中需要实现权限获取的请求。
更改线程中的UI。(不允许在另一个线程中更改UI,只能在主线程中更改它。)
需要在单击按钮时创建一个对话框,并显示刚才在文本中输入的内容。
关键字private的目的是保护文件或方法不被类外访问。但由于Java Reflection,这并不一定有效。
实现以下 *** 作:在包classes.jar的PoRELab类中访问私有成员变量curStr;调用私有成员函数privateMethod。
生成应用程序签名。
1.1.2 broadcastReceiver 实验中的SecretBootReceiver继承了androID.content.broadcastReceiver。broadcastReceiver是安卓四大组件之一,用以接受广播信息,并启动相应处理机制。broadcastReceiver需要重写onReceive方法来接收以Intent对象为参数的消息。实验中创建了一个Intent(context, MainActivity.class)并通过context.startActivity来启动MainActivity。
记得在AndroIDManifest.xml中注册receiver:
<receiver androID:name=".SecretBootReceiver"> <intent-filter> <action androID:name="androID.intent.action.BOOT_COMPLETED" /> <category androID:name="androID.intent.category.LAUNCHER" /> </intent-filter></receiver>1.1.3 Service Service是一个后台运行的组件,执行长时间运行且不需要用户交互的任务。
Service包括两种状态:Started、Bound。通过startService()启动了服务,则Service处于Started状态。一旦启动,Service可以在后台无限期运行,即使启动它的组件已经被销毁。当AndroID的应用程序组件通过bindService()绑定了Service,则Service是Bound状态。Bound状态的服务提供了一个客户服务器接口来允许组件与服务进行交互,如发送请求,获取结果,甚至通过IPC来进行跨进程通信。
Service的生命周期:
程序中SecretService继承Service类,重写了onStartCommand方法。onStartCommand中开启了一个新线程,线程执行一个死循环,循环内部调用locationUpdates函数获取GPS信息并输出,然后线程陷入3秒睡眠。于是该应用程序可以实现每3s一次的GPS信息输出。
locationUpdates中使用了androID.location中的方法获取经纬度。通过androID.Widget.Toast将位置信息输出到屏幕上。
这边暂时Toast使用不了,用<uses-permission androID:name="androID.permission.RECEIVE_BOOT_COMPLETED" />开启了权限也不行。
睡了一觉,Toast就可以了。【挠头】:
记得在AndroIDManifest.xml中注册service:
<service androID:name=".SecretService" androID:enabled="true" androID:exported="true" ></service>1.1.4 AppCompatActivity Activity代表了一个具有用户界面的单一屏幕,如 Java 的窗口或者帧。
AndroID 系统初始化它的程序是通过Activity中的 onCreate() 回调的调用开始的。
Activity的生命周期如下图:
实验中MainActivity类继承了AppCompatActivity这个类,并重写onCreate,onRequestPermissionsResult函数。
onCreate中先调用父类onCreate,然后获取位置权限。获取权限后,开启一个Intent,通过startService启动SecretService这个类。
AndroID UI机制:UI *** 作只有一个主线程,app只有这个线程可以处理UI;长时间的代码需要放后台,不能阻塞UI。
可以使用Handler来完成子线程对主线程UI的更改。
Handler:很多功能或 *** 作是不能都放在Activity当中,否则会出现长时间没响应,甚至会出现ANR之类的错误(即5秒内没响应)。把那些费时费力的 *** 作放在另外一个线程 *** 作当中,这样就能够和主线程(UI)线程同步 *** 作,不会出现长时间等待或没响应的 *** 作,使得用户体验大大提高。Handler就是实现上面的功能的一个东西,它接受子线程发送来的数据,并用此数据配合主线程更新UI。
Looper:“循环者”,它被设计用来使一个普通线程变成Looper线程,即不断循环。
AlertDialog:d窗。通过AlertDialog.Builder创建一个d窗。
通过以上三者,并使用Handler.post进行消息传递。
结果如下:
1.1.6 私有成员变量、函数的访问 Java的反射机制:Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。在程序运行时动态加载类并获取类的详细信息,从而 *** 作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
导入jar包:将classes.jar包拷贝到app/src/main/libs中,这时候还未导入,需要在这个jar上右键,然后单机add as library,成功导入后如图:
可以看到这个jar包内有BuildConfig和PoRELab两个类,包名是com.pore.mylibrary。通过Class.forname("com.pore.mylibrary.PoRELab")创建一个Class对象,然后写入以下代码:
Method method = c.getDeclaredMethod("privateMethod", String.class, String.class);method.setAccessible(true);FIEld tag = c.getDeclaredFIEld("curStr");tag.setAccessible(true);Object obj = c.newInstance();method.invoke(obj, "hello", tag.get(obj)); 即可访问curStr变量,以及调用privateMethod函数(参数是“”hello“)。最终结果可以在Logcat中看到:
为什么要生成签名?
AndroID系统要求每一个AndroID应用程序必须要经过数字签名才能够安装到系统中,也就是说如果一个AndroID应用程序没有经过数字签名,是没有办法安装到系统中的!AndroID通过数字签名来标识应用程序的作者和在应用程序之间建立信任关系,不是用来决定最终用户可以安装哪些应用程序。这个数字签名由应用程序的作者完成,并不需要权威的数字证书签名机构认证,它只是用来让应用程序包自我认证的。
主要看了这篇博文生成签名:https://blog.csdn.net/YuEOrange/article/details/86018718。
Event Log如下:
1.1.8 其它1. 调试问题:目前采用输出调试的方法。调用`androID.util.Log`将调试信息写入log中,可以在logcat中通过设置filter来观察调试结果。 Log.d("deBUG", "information");1.2 Java2Smali1.2.1 任务描述使用smali写一个选择排序的程序。使用smali写一个获得主机IP的程序。1.2.2 smali选择排序Smali是用于Dalvik(AndroID虚拟机)的反汇编程序实现。汇编工具(将Smali代码汇编为dex文件)为smali.jar,与之对应的baksmali.jar则是反汇编程序。
smali、dex、java、class的关系:
Smali文件结构一个Smali文件对应的是一个Java的类,更准确的说是一个.class文件,如果有内部类,需要写成
Classname$InnerClassA、Classname$InnerClassB…这样的形式
基本类型
类型关键字 对应Java中的类型说明 V voID,只能用于返回类型 Z boolean B byte S short C char I int J long (64 bits) F float D double (64 bits)
对象Object类型,即引用类型的对象,在引用时,使用L开头,后面紧接着的是完整的包名,比如:
java.lang.String对应的Smali语法则是Ljava/lang/String
数组一维数组在类型的左边加一个方括号,比如:
[I等同于Java的int[]
方法声明及调用官方Wiki中给出的Smali引用方法的模板如下:
Lpackage/name/Objectname;->Methodname(III)Z
.method和.end method 类似Java大括号{}
.locals 指定方法中非参寄存器总数,出现在方法第一行
.registers 指定方法中寄存器总数
.prologue 表示代码开始
.line 表示java源码行号,用于调试
Dalvik字节码手册
首先写一个Java版本的选择排序:
public static voID select_sort(int[] a) { for(int i=0;i<a.length;i++) { int minIndex = i; for(int j=0;j<a.length;j++) { if(num[j] < num[minIndex]) { minIndex = j; } } int temp = num[i]; num[i] = num[minIndex]; num[minIndex] = temp; }}翻译成smali:
.method public static select_sort([I)V .register .prologue array-length v0, p0 const/4 v1, 0x1 #常量赋值 if-ge v1,v0:cond_1 const smali和dex的相互转换需要smali-2.5.2.jar和baksmali-2.5.2.jar这两个包。下载地址。
smali转dex:
java -jar .\smali-2.5.2.jar a Select.smali -o sean_Select.dex进入安卓系统:
adb shell创建目录:
mkdir /data/local/tmp/selectSort回到windows终端,将sean_Select.dex发送给到安卓系统/data/local/tmp/selectSort路径上:
PS D:\YC\study\系统软件安全\lab1> adb push sean_Select.dex /data/local/tmp/selectSortsean_Select.dex: 1 file pushed, 0 skipped. 0.1 MB/s (1048 bytes in 0.014s)在安卓系统终端运行Select.dex:
dalvikvm -cp sean_Select.dex Select 3 4 1结果如下:
1.2.3 smali获取主机ipJava的获取IP的程序:
import java.net.InetAddress;import java.net.UnkNownHostException;public class GetIP{ public static voID main(String args[]) { StringBuffer res = null; try { res = foo(args[0]); } catch (UnkNownHostException e) { e.printstacktrace(); } System.out.println(res); } public static StringBuffer foo(String name) throws UnkNownHostException { StringBuffer res = new StringBuffer(); InetAddress address = InetAddress.getByname(name); String hostname = address.getHostname(); String hostAddress = address.getHostAddress(); res.append(hostname).append("=").append(hostAddress); return res; }}翻译成smali程序。因为太懒了,没有自己翻。上网找了下java转smali的方式:java->class->dex->smali。
javac GetIP.javajava -jar dx.jar --dex --output=GetIP.dex GetIP.classjava -jar baksmali.jar GetIP.dex其中dx.jar在androID-sdk\build-tools.0.1\lib里面,输入上述命令后会生成一个out目录,目录里面的就是翻译好的GetIP.smali:
.class public LGetIP;.super Ljava/lang/Object;.source "GetIP.java"# direct methods.method public constructor <init>()V .registers 1 .prologue .line 4 invoke-direct {p0}, Ljava/lang/Object;-><init>()V return-voID.end method.method public static foo(Ljava/lang/String;)Ljava/lang/StringBuffer; .registers 5 .annotation system Ldalvik/annotation/Throws; value = { Ljava/net/UnkNownHostException; } .end annotation .prologue .line 15 new-instance v0, Ljava/lang/StringBuffer; invoke-direct {v0}, Ljava/lang/StringBuffer;-><init>()V .line 16 invoke-static {p0}, Ljava/net/InetAddress;->getByname(Ljava/lang/String;)Ljava/net/InetAddress; move-result-object v1 .line 17 invoke-virtual {v1}, Ljava/net/InetAddress;->getHostname()Ljava/lang/String; move-result-object v2 .line 18 invoke-virtual {v1}, Ljava/net/InetAddress;->getHostAddress()Ljava/lang/String; move-result-object v1 .line 19 invoke-virtual {v0, v2}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer; move-result-object v2 const-string v3, "=" invoke-virtual {v2, v3}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer; move-result-object v2 invoke-virtual {v2, v1}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer; .line 20 return-object v0.end method.method public static main([Ljava/lang/String;)V .registers 3 .prologue .line 6 const/4 v0, 0x0 .line 8 const/4 v1, 0x0 :try_start_2 aget-object v1, p0, v1 invoke-static {v1}, LGetIP;->foo(Ljava/lang/String;)Ljava/lang/StringBuffer; :try_end_7 .catch Ljava/net/UnkNownHostException; {:try_start_2 .. :try_end_7} :catch_e move-result-object v0 .line 12 :goto_8 sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V .line 13 return-voID .line 9 :catch_e move-exception v1 .line 10 invoke-virtual {v1}, Ljava/net/UnkNownHostException;->printstacktrace()V goto :goto_8.end methodwindows终端:
java -jar .\smali-2.5.2.jar a GetIP.smali -o sean_GetIP.dexadb push sean_GetIP.dex /data/local/tmp/GetIP安卓终端:
dalvikvm -cp sean_GetIP.dex GetIP weibo.com结果如下:
1.3 Smali2Java1.3.1 任务描述将smali文件夹中的文件汇编为Box.dex,然后运行。阅读Checker.smali代码,看懂它的意思。然后写一个能实现其功能的java代码。运行Box.dex,输入StudentID。然后得到一个Encode msg,再跑一遍Box.dex,输入StudentID,会返回True。阅读并理解Encoder.smali代码,然后写一个能实现其功能的java代码。1.3.2 step1windows终端:
java -jar .\smali-2.5.2.jar a smali -o Box.dexadb push Box.dex /data/local/tmp/Box安卓终端:
阅读checker.smali,一边读一遍翻译成java:
public class Checker { private String secret; public Checker(){secret="key";}; private boolean checkStr1(String S1) { char[] c1 = S1.tochararray(); int sum = 0, first = 0, second = 0; for(int i = 0;i < c1.length;i++) { if(c1[i] != 0x78) continue; sum ++; if(sum == 1) first = i; if(sum == 2) second = i; } if(sum != 2 || second-first != 4 || c1[0] != 0x30 || c1[c1.length-1] != 0x39) return false; String S2 = S1.substring(0, first); if(S2.contains(this.secret)) return true; return false; } private int count(String S1) { char[] c1 = S1.tochararray(); int sum = 0; for(int i = 0;i < c1.length;i++) { if(c1[i] == 0x31) sum ++; } return sum; } private int func(int n) { if(n<=1) return 1; else return func(n-1)*n; } public boolean check(String S1) { if(S1.length() < 0xc || S1.length() > 0x10) return false; String S2 = S1.substring(0,0xa); String S3 = S1.substring(0xa,S1.length()); int x2 = this.count(S3); int x3 = this.func(x2); if(x2 != x3 || this.checkStr1(S2) == false) return false; return true; }}看懂了它的意思,部分注释已经写在代码上了:返回为true的串由长度由S1和S2组成,S1=“0keyx???x9”,其中"?"可为任何非"x"的字符,S2中应包含2个"1"且S2长度不大于6。
测试结果:
1.3.3 step2先看看跑Box.dex的效果:
然后阅读Encode.smali,一边理解一边写java:
public class Encoder{ private String algorithm; private String charSet; private final String[] hexDigits; Encoder() { String[] S1 = new String[0x10]; for(int i = 0;i < 16;i++) { S1[i] = Integer.toHexString(i); } hexDigits = S1; algorithm = "MD5"; charSet = "utf-8"; } private String byteArrayToHexString(byte[] B1) { StringBuffer ans = new StringBuffer(); for(int i = 0;i < B1.length;i++) { ans.append(bytetoHexString(B1[i])); } return ans.toString(); } private String bytetoHexString(byte B1) { int B = B1; if(B1 < 0) B &= 0xff; StringBuilder ans = new StringBuilder(); ans.append(hexDigits[B / 0x10]).append(hexDigits[B % 0x10]); return ans.toString(); } private String getSalt() { StringBuilder ans = new StringBuilder(0x10); for(int i = 0; i < 16; i++) { if(new Random().nextBoolean()) ans.append("1"); else ans.append("0"); } return ans.toString(); } public boolean check(String S1, String S2) { char[] C1 = new char[0x20]; char[] C2 = new char[0x10]; if(S2.length() != 0x30) return false; for(int i = 0;i < 0x30;i += 0x3) { C1[i / 0x3 * 0x2] = S2.charat(i); C1[i / 0x3 * 0x2 + 1] = S2.charat(i + 0x2); C2[i / 0x3] = S2.charat(i + 0x1); } String S3 = new String(C2); StringBuilder S4 = new StringBuilder(); S4.append(S1).append(S3); String S5 = S4.toString(); try { MessageDigest dig = MessageDigest.getInstance(algorithm); StringBuilder str2 = new StringBuilder(); str2.append(""); str2.append(byteArrayToHexString(dig.digest(S5.getBytes(charSet)))); String str1 = new String(C1); return str1.equals(str2.toString()); } catch(Exception e) { e.printstacktrace(); } return false; } public String enCoding(String S1) { String S2 = getSalt(); String S6 = null; String ans = null; StringBuilder S3 = new StringBuilder(); S3.append(S1).append(S2); String S4 = S3.toString(); try{ MessageDigest dig = MessageDigest.getInstance(algorithm); StringBuilder S5 = new StringBuilder(); S5 = S5.append(""); S5.append(byteArrayToHexString(dig.digest(S4.getBytes(charSet)))); S6 = S5.toString(); }catch(Exception e) { e.printstacktrace(); } try{ char[] C1 = new char[0x30]; for(int i = 0;i < 0x30;i += 3) { C1[i] = S6.charat(i / 0x3 * 0x2); C1[i + 1] = S2.charat(i / 0x3); C1[i + 2] = S6.charat(i / 0x3 * 0x2 + 1); } ans = new String(C1); }catch(Exception e) { ans = S6; e.printstacktrace(); } }}运行结果:
1.4 reversing and Repacking1.4.1 任务描述 通过apk解包、反编译、重新打包、apk签名完成安卓逆向。
各种工具的下载地址
1.4.2 Task 1 Knock the door 首先下载解包工具apktool_2.4.1.jar,然后命令行输入java -jar apktool_2.4.1.jar d lab.apk解包。
解包之后可以看到文件的组织吧,但主程序是smali格式,是比较难看懂的:
反编译成java需要Jeb工具,Jeb工具要在jdk1.8环境下才能工作,所以jdk环境也要配置好。
Jeb我下载的是破解版的jeb-2.2.7.201608151620_crack_qtfreet00,用记事本打开jeb_wincon.bat,加上一句set JAVA_HOME=C:\Program files\Java\jdk1.8.0_121就配置好了。
用Jeb直接打开lab.apk:
点击Bytecode,查看Bytecode/HIErarchy,可以看到主程序。选择要看的程序,右键->DecomplIE即可查看其反汇编的java代码。主要的方法是这两个:
public MainActivity() { super(); this.l0 = 0; this.l1 = 999999;}public voID buttonClick(VIEw arg5) { int v5 = this.l0; if(v5 != this.l1) { ++v5; this.l0 = v5; this.t2.setText(String.format("%d / %d", Integer.valueOf(v5), Integer.valueOf(this.l1))); } else { this.t2.setText(2131427372); this.t3.setText(PlayGame.getFlag(this.te.getText().toString(), this.ctx)); }} 意思大概是点击button 1000000次才能运行PlayGame。要攻击的话,把对应的smali文件中buttonClick方法if-eq改为if-ne即可。
然后回编译java -jar apktool_2.4.1.jar b lab,报了个错:
error: No resource IDentifIEr found for attribute 'compileSdkVersion' in package 'androID'。
不知道这是什么错。最后用java -jar apktool_2.4.1.jar b lab -p framework -o lab.apk强制回编译了QAQ。参考文章。
然后是安装openssl。官网下载地址。装好了之后把bin路径添加到环境变量里面。
然后是给apk签名:
openssl genrsa -3 -out testkey.pem 2048openssl req -new -x509 -key testkey.pem -out testkey.x509.pem -days 10000openssl pkcs8 -in testkey.pem -topk8 -outform DER -out testkey.pk8 -nocrypt 把lab.apk, apksigner.jar, testkey.pk8, testkey.x509.pem,testkey.pem放到同一目录,打包成签好名的lab_signed.apk:
java -jar apksigner.jar sign --cert testkey.x509.pem --key testkey.pk8 --in lab.apk --out lab_signed.apk然后把新的apk传到安卓设备上:adb install lab_signed.apk,运行:
只需要点击1次button就成功啦~~~
1.4.3 Task 2 Give me your token Task2写在PlayGame这个类里面:
public static String getFlag(String arg8, Context arg9) { StringBuilder v9 = new StringBuilder("pore"); StringBuilder v1 = new StringBuilder("pore"); StringBuilder v2 = new StringBuilder("pore"); StringBuilder v3 = new StringBuilder("pore"); v9.setCharat(0, ((char)(v9.charat(0) - 4))); v9.setCharat(1, ((char)v9.charat(1))); v9.setCharat(2, ((char)(v9.charat(2) + 5))); v9.setCharat(3, ((char)(v9.charat(3) - 1))); v1.setCharat(0, ((char)(v1.charat(0) + 4))); v1.setCharat(1, ((char)(v1.charat(1) + 10))); v1.setCharat(2, ((char)(v1.charat(2) - 13))); v1.setCharat(3, ((char)(v1.charat(3) + 7))); v2.setCharat(0, ((char)(v2.charat(0) - 4))); v2.setCharat(1, ((char)(v2.charat(1) - 6))); v2.setCharat(2, ((char)(v2.charat(2) - 11))); v2.setCharat(3, ((char)(v2.charat(3) + 3))); v3.setCharat(0, ((char)(v3.charat(0) + 2))); v3.setCharat(1, ((char)(v3.charat(1) - 10))); v3.setCharat(2, ((char)(v3.charat(2) + 1))); v3.setCharat(3, ((char)(v3.charat(3) + 14))); if(arg8.equals("".concat(v2.toString()).concat(v1.toString()).concat(v9.toString()).concat(v3.toString()))) { return "You got it! Task2 finished.\nTry to call sth here"; } return "Welcome to task2"; } 翻译以下,Flag是lightyellowdress~~淡黄的长裙哈哈哈哈。
验证一下:
1.4.4 Task 3 Call to the NPC Task2中给的hint:Try to call sth here。应该是要我们调用下面这个函数:
public static native String skdaga(String arg0) {} 在PlayGame.smali的return前面加两行:
invoke-static {p1}, Lcom/pore/play4fun/PlayGame;->skdaga(Ljava/lang/String;)Ljava/lang/String;move-result-objext p0 然后再次打包QAQ…
成功拿到Flag啦啊啊啊泪目。。。
Flag是flag{SmalIISCoolll}哈哈。
[1] AndroidStudio怎样导入jar包
[2] 一篇文章帮你搞定Android中java、class、dex、smali、jar、apk之间的转换关系
[3] Smali基础知识
[4] Dalvik 字节码
[5] 教我兄弟学Android逆向番外02 jeb工具的使用
总结以上是内存溢出为你收集整理的SSSL Lab1 : Android逆向全部内容,希望文章能够帮你解决SSSL Lab1 : Android逆向所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)