一、安卓开发初识 平台:Android Studio Giraffe(2022)
SDK : 33
安卓开发是基于移动端开发的app,通过Android Studio设计,连接真机或虚拟机发行后进行测试。
1.环境安装 (1) 首先官网安装Android Studio,之后安装sdk(连接移动端的桥梁,相当于java的jdk)
(2) 配置相关环境变量,将tools和platform-tools加入环境变量,没有的可以在AS的sdk tools中下载。
(3) 测试sdk中的adb,cmd命令行输入adb测试,adb是构建PC端和移动端的桥梁。
2.目录结构 创建完一个empty project(java)后
(1) 最重要的是gradle(相当于web开发的maven),是项目构建的工具,可以帮助拉取远程仓库代码,其中有两个build.gradle,一个是项目的,另一个是模块的,在模块中定义了一些sdk版本以及一些包的远程地址和版本。
(2) 在src.main.java中,定义了MainActivity,相当于启动类,并且该类(所有类)需要在AndroidMainfest.xml中声明,MainActivity中编写java代码实现逻辑处理。
(3) res资源包中,drawble一般存放图片资源,mipmap存放程序启动图标的相关设置(包括颜色,形状等),menu中存放定义应用菜单的内容,并通过R.menu类访问,value中存放颜色、主题和字符串等,layout中存放的是xml文件,主要构建页面布局,包括各个控件(内含各个标签),相当于前端设计。
(4) AndroidMainfest.xml中主要存放一些配置和java类的注册,
。。。。。
3.构建一个项目,实现注册功能。 1.首先是MainActivity类,其中存放java逻辑代码。 (1) 首先要继承App类,public class MainActivity extends AppCompatActivity,其中的onCreate方法会在加载页面时第一个调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private EditText edit_userName; private EditText edit_sn; private Button btn_register; @Override public void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); setTitle(R.string.unregister); edit_userName = (EditText) findViewById(R.id.edit_username); edit_sn = (EditText) findViewById(R.id.edit_sn); btn_register = (Button) findViewById(R.id.button_register); btn_register.setOnClickListener(new OnClickListener () { public void onClick (View v) { if (!checkSN(edit_userName.getText().toString().trim(), edit_sn.getText().toString().trim())) { Toast.makeText(MainActivity.this , ʾ R.string.unsuccessed, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this , R.string.successed, Toast.LENGTH_SHORT).show(); btn_register.setEnabled(false ); setTitle(R.string.registered); } } }); }
(2) 显示菜单
1 2 3 4 5 @Override public boolean onCreateOptionsMenu (Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true ; }
(3) 校验函数,判断注册码是否正确。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 private boolean checkSN (String userName, String sn) { try { if ((userName == null ) || (userName.length() == 0 )) return false ; if ((sn == null ) || (sn.length() != 16 )) return false ; MessageDigest digest = MessageDigest.getInstance("MD5" ); digest.reset(); digest.update(userName.getBytes()); byte [] bytes = digest.digest(); String hexstr = toHexString(bytes, "" ); StringBuilder sb = new StringBuilder (); for (int i = 0 ; i < hexstr.length(); i += 2 ) { sb.append(hexstr.charAt(i)); } String userSN = sb.toString(); if (!userSN.equalsIgnoreCase(sn)) return false ; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return false ; } return true ; }
(4) 格式处理
1 2 3 4 5 6 7 8 9 10 11 private static String toHexString (byte [] bytes, String separator) { StringBuilder hexString = new StringBuilder (); for (byte b : bytes) { String hex = Integer.toHexString(0xFF & b); if (hex.length() == 1 ){ hexString.append('0' ); } hexString.append(hex).append(separator); } return hexString.toString(); }
2.配置layout中activity_main.xml。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 <LinearLayout xmlns:android ="http://schemas.android.com/apk/res/android" //直线结构 android:layout_width ="match_parent" android:layout_height ="match_parent" android:orientation ="vertical" > //该直线垂直分布,即上下分布 <TextView android:id ="@+id/textView1" //组件id ,activity 中通过findbyid 获得该组件 android:layout_width ="match_parent" android:layout_height ="wrap_content" android:gravity ="center" android:text ="@string/info" android:textSize ="20dp" /> <LinearLayout android:layout_width ="match_parent" android:layout_height ="wrap_content" android:layout_marginLeft ="10dp" android:orientation ="horizontal" > <TextView android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:text ="@string/username" /> //引用string中的常量 <EditText android:id ="@+id/edit_username" android:hint ="@string/hint_username" android:layout_width ="0dp" android:layout_height ="wrap_content" android:layout_marginLeft ="10dp" android:layout_marginRight ="10dp" android:layout_weight ="1" android:ems ="10" > </EditText > </LinearLayout > <LinearLayout android:layout_width ="match_parent" android:layout_height ="wrap_content" android:layout_marginLeft ="10dp" android:orientation ="horizontal" > <TextView android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:text ="@string/sn" /> <EditText android:id ="@+id/edit_sn" android:hint ="@string/hint_sn" android:layout_width ="0dp" android:layout_height ="wrap_content" android:layout_marginLeft ="10dp" android:layout_marginRight ="10dp" android:layout_weight ="1" android:ems ="10" > </EditText > </LinearLayout > <Button android:id ="@+id/button_register" android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:layout_marginRight ="10dp" android:layout_gravity ="right" android:text ="@string/register" /> </LinearLayout >
3.启动项目 我们拿虚拟设备测试,启动虚拟机后运行项目会把项目发布到虚拟机上。
我们测试一下程序,随意输入用户名和注册码,点注册,发现弹出错误提示。
二、安卓逆向测验 测试完安卓程序后,我们尝试通过逆向破解该注册程序。
逆向的一般步骤:
拿到apk文件后,通过apktool反编译,拿到源代码,分析smail文件,修改smali文件内容。
对修改后的文件进行编译生成apk文件。
对新apk文件进行签名即可得到逆向破解后的源程序。
1. 这里我们首先对AS中的模块打包,生成apk文件。 通过AS上方的build中的Generate Signed选项打包,选择apk,需要一个签名jks,这里我们新建一个jks(后面逆向修改完再用这个签名),输入密码和别名后选择release版本等待打包,成功后会生成一个release包,其中的apk即为打包完的apk,可发送到手机上,手机端下载后可正常打开注册页面。
命令为apktool d <原apk目录> -o <输出目录>。
3. 反编译完成后,我们查看分析smali文件。 注: 老版安卓逆向书上是通过首先查找strings.xml中的unsuccess字符串,找到同目录下的public.xml,搜索unsuccess字符串找到该字符串对应的id,然后再smali文件中搜索id定位到那一块if逻辑代码,但我的smali文件中没有id,于是我直接搜索的unsuccess字符串找到的onclick方法中的if逻辑代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 invoke-static {p1, v0, v1}, Lcom/example/myapplication/MainActivity;->access$200 (Lcom/example/myapplication/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z move-result p1 const/4 v0, 0x0 if -nez p1, :cond_0 .line 34 iget-object p1, p0, Lcom/example/myapplication/MainActivity$1 ;->this $0 :Lcom/example/myapplication/MainActivity; sget v1, Lcom/example/myapplication/R$string;->unsuccessed:I invoke-static {p1, v1, v0}, Landroid/widget/Toast;->makeText(Landroid/content/Context;II)Landroid/widget/Toast; move-result-object p1 .line 35 invoke-virtual {p1}, Landroid/widget/Toast;->show()V :cond_0 iget-object p1, p0, Lcom/example/myapplication/MainActivity$1 ;->this $0 :Lcom/example/myapplication/MainActivity; sget v1, Lcom/example/myapplication/R$string;->successed:I
其中if-nez即为判断条件,上面MainActivity中的check的结果存到p1中,判断p1如果不为0(即注册码正确)则跳转到cond_0处,cond_0,否则向下执行,通过Toast;->show()弹出不成功的提示。
我们修改if-nez为if-eqz,即表示注册码不正确的时候跳转,那么我们随意输入注册码即可跳转到成功处。
修改完成后保存,通过apktool b 再打包成apk
4. 对apk签名通过apksigner 签名的时候报错了,因为sdk30以上的版本签名时要求四字节对齐,因此我们首先对apk进行字节对齐,之后进行签名。引自: [Android Targeting R+ requires the resources.arsc of installed APKs to be stored uncompressed and al_failure -124: failed parse during installpackagel-CSDN博客
签名命令:
1 2 3 4 5 6 apksigner sign --ks login.jks --ks-key-alias key --out signed.apk unsigned.apk 1 、--ks 你的.jks文件路劲2 、--ks-key-alias 你的签名文件的别名3 、--out 输出签名后的目标路径4 、unsigned.apk 未签名的原始apk文件路径
5.签名后下载 在AS 终端中,通过adb install <apk目录> 下载adb,如果报错
1 Failure [INSTALL_FAILED_UPDATE_INCOMPATIBLE: Package com.example.myapplication signatures do not match previously installed version; ignoring!]
是因为虚拟机或真机上已经有了原先那个apk(即未破解前的软件),我们可以通过uninstall卸载后重新下载
1 2 adb uninstall com.example.myapplication adb install xxx.apk
6.验证注册 逆向破解完,我们输入任意注册码发现提示注册成功!