android-re

Android逆向

apk文件结构

文件 解释
assets目录 存放apk的静态资源文件,如视频、音频、图片等。
lib/目录 arm64-v8a只用于64位的Android设备;armeabi-v7a基本通用所有Android设备;x86常见用于安卓模拟器,其目录下的.so文件是c/c++编译的动态链接库。
META-INF目录 保存应用的签名信息,相当于apk的身份证。用于验证apk的完整性,即:文件是否被修改。
res目录 存放资源文件,包括图片、字符串等等。apk的脸蛋由layout文件设计。
AndroidManifest.xml apk的应用清单信息,描述了应用的名字,版本,权限,引用的库等等信息。相当于元数据(metadata)。
classes.dex apk运行的主要逻辑。这是java源码编译后生成的java字节码文件。
resources.arsc 编译后的二进制资源文件。它是一个映射表,映射着资源和id,通过id找对应的资源。

AndroidManifest.xml

整个应用程序的信息描述文件,定义了应用程序中包含的Activity、Service、Content Provider和BroadcastReceiver组件信息。每个应用程序根目录下包含一个AndroidManifest.xml文件,且文件名不能修改

属性 描述
versionCode 版本号,主要用来更新,例如:12
versionName 版本名,给用户看的,例如:1.2
package 包名,例如:com.zj.52pj.demo
uses-permission android:name="" 应用权限,例如:android.permission.INTERNET代表网络权限
android:label="@string/app_name" 应用名称
android:icon="@mipmap/ic_launcher" 应用图标路径
android:debuggable="true" 应用是否开启debug权限

应用双开

即一个apk安装两遍,成为两个相同应用程序。

应用汉化

Dalvik、ART

  • Dalvik是Google专门为Android设计的一个虚拟机,有其专属的文件格式.dex(Dalvik executable)
  • Art(Android Runtime)相当于Dalvik的升级版,本质与Dalvik无异。

Smali

Smali是Dalvik虚拟机使用的一种汇编语言,通过对Android应用的反编译得来(主要是反编译.dex文件)。常用于安卓应用的逆向工程、分析和修改。

–>Smali语法<–

修改Smali代码逻辑可实现应用破解,例如跳过大会员检测。理解成C#中的IL代码,修改IL代码也能达到变更应用逻辑的效果,只不过修改方式不同:

  • IL:C#中需要用到Harmony类库,编写修改IL代码的patch(本质上是c#代码),然后实现运行时修改(动态)。
  • Smali:反汇编apk,编辑根目录下classes.dex核心逻辑文件,再保存重新签名、覆盖安装应用,是编译时修改(静态)。

安卓四大组件

组件 描述
Activity(活动) 每个Activity都表示一个界面。如MainActivity主界面。一个安卓应用必须通过Activity来运行和启动,Activity的生命周期交给系统管理。
Service(服务) 在后台执行长时间操作没有用户界面的组件。如:后台播放音乐、下载文件等。
Broadcast Receiver(广播接收器) 用于接收广播,并做出反应 的组件。常见的系统广播有:通知时区的改变、电量低、语言改变等。
Content Provider 接口。

Activity的生命周期

函数名称 描述
onCreate() 一个Activity启动后第一个被调用的函数,常用来在此方法中进行Activity的一些初始化操作。例如创建View,绑定数据,注册监听,加载参数等。
onStart() 当Activity显示在屏幕上时,此方法被调用但此时还无法进行与用户的交互操作。
onResume() 这个方法在onStart()之后调用,也就是在Activity准备好与用户进行交互的时候调用,此时的Activity一定位于Activity栈顶,处于运行状态。
onPause() 这个方法是在系统准备去启动或者恢复另外一个Activity的时候调用,通常在这个方法中执行一些释放资源的方法,以及保存一些关键数据。
onStop() 这个方法是在Activity完全不可见的时候调用的。
onDestroy() 这个方法在Activity销毁之前调用,之后Activity的状态为销毁状态。
onRestart() 当Activity从停止stop状态恢进入start状态时调用状态。

跳过广告/弹窗的方法

广告分为:

  • 启动广告(开屏广告)
  • 弹窗/更新广告
  • 横幅广告

开屏广告的流程:启动activity -> 广告activity -> 主页activity

Q:如何找到广告activity的类名?

A:mt管理器 - 左上角三横杠 - Activity记录,打开服务,再进入你的应用,会自动捕捉所有显示过的activity,包括广告的。

方法1 - 修改加载时间

即,通过修改广告activity本身的代码(如将延迟时间设为零)来跳过广告。下例:

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
66
//
// Decompiled by Jadx (from NP Manager)
//
package com.zj.wuaipojie.ui;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import androidx.appcompat.app.AppCompatActivity;
import com.zj.wuaipojie.ui.AdActivity$.ExternalSyntheticLambda0;
import kotlin.Metadata;

@Metadata(d1 = {"\u0000(\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u000b\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\u0002\n\u0002\b\u0003\n\u0002\u0018\u0002\n\u0002\b\u0005\u0018\u0000 \u00102\u00020\u0001:\u0001\u0010B\u0005¢\u0006\u0002\u0010\u0002J\b\u0010\u0007\u001a\u00020\bH\u0002J\b\u0010\t\u001a\u00020\bH\u0002J\u0012\u0010\n\u001a\u00020\b2\b\u0010\u000b\u001a\u0004\u0018\u00010\fH\u0014J\b\u0010\r\u001a\u00020\bH\u0014J\b\u0010\u000e\u001a\u00020\bH\u0014J\b\u0010\u000f\u001a\u00020\bH\u0014R\u000e\u0010\u0003\u001a\u00020\u0004X‚\u000e¢\u0006\u0002\n\u0000R\u000e\u0010\u0005\u001a\u00020\u0006X‚\u0004¢\u0006\u0002\n\u0000¨\u0006\u0011"}, d2 = {"Lcom/zj/wuaipojie/ui/AdActivity;", "Landroidx/appcompat/app/AppCompatActivity;", "()V", "hasPaused", "", "timeoutHandler", "Landroid/os/Handler;", "jump", "", "loadAd", "onCreate", "savedInstanceState", "Landroid/os/Bundle;", "onDestroy", "onRestart", "onStop", "Companion", "app_release"}, k = 1, mv = {1, 7, 1}, xi = 48)
/* compiled from: AdActivity.kt */
public final class AdActivity extends AppCompatActivity {
private static final int AD_TIMEOUT = 3000;
public static final Companion Companion = new Companion(null);
private static final int MSG_AD_TIMEOUT = 1001;
private boolean hasPaused;
private final Handler timeoutHandler = new Handler(new ExternalSyntheticLambda0(this));

private final void jump() {
if (!this.hasPaused) {
this.hasPaused = true;
startActivity(new Intent((Context) this, ChallengeThird.class));
finish();
}
}

protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(2131427362);
loadAd();
}

protected void onRestart() {
super.onRestart();
this.hasPaused = false;
jump();
}

protected void onStop() {
this.timeoutHandler.removeMessages(MSG_AD_TIMEOUT);
this.hasPaused = true;
super.onStop();
}

private final void loadAd() {
this.timeoutHandler.removeMessages(MSG_AD_TIMEOUT);
this.timeoutHandler.sendEmptyMessageDelayed(MSG_AD_TIMEOUT, 3000);
}

/* renamed from: timeoutHandler$lambda-0 */
private static final boolean m0timeoutHandler$lambda-0(AdActivity adActivity, Message message) {
if (adActivity.hasWindowFocus()) {
adActivity.jump();
}
return false;
}

protected void onDestroy() {
super.onDestroy();
}
}
  1. 广告超时跳转 (jump() 方法)
  • jump() 方法被用来跳过广告,并且只会在广告页面暂停后跳转。
  • this.hasPaused 用来检查广告是否已经暂停。如果还没有暂停(即广告未跳过),它会跳转到 ChallengeThird 活动并关闭当前的 AdActivity
  1. loadAd() 方法
  • 该方法用来启动广告的加载逻辑,并且设置了一个超时定时器(3 秒)。
  • this.timeoutHandler.sendEmptyMessageDelayed(MSG_AD_TIMEOUT, 3000) 会在 3 秒后发送一个消息 (MSG_AD_TIMEOUT) 来触发广告跳过。
  1. Handler 处理超时 (timeoutHandler)
  • timeoutHandler 是一个 Handler,它接收到 MSG_AD_TIMEOUT 消息时,会调用 jump() 方法来跳过广告。
  • m0timeoutHandler$lambda-0() 这个方法会在消息触发时检查当前窗口是否有焦点,如果有焦点,就调用 jump() 跳过广告。
  1. 生命周期控制
  • onCreate():页面创建时会调用 loadAd() 来加载广告并设置 3 秒超时。
  • onRestart():当页面重启时,hasPaused 被重置为 false,并立刻调用 jump() 跳过广告。
  • onStop():当页面停止时,会移除超时消息并标记 hasPausedtrue
  • onDestroy():页面销毁时没有额外逻辑,调用父类的 onDestroy()

具体方法:

  1. 修改超时消息的延时:可以减少 MSG_AD_TIMEOUT 的延时,快速跳过广告。

修改这一行:

1
this.timeoutHandler.sendEmptyMessageDelayed(MSG_AD_TIMEOUT, 3000);

将3000改成50,但不可改成零。因为若没有延时,消息立即被处理,那么hasPaused可能还是true的状态,无法进行jump()的逻辑。

(通过Smali代码修改)

  1. 直接调用跳转:可以通过反射或修改代码,直接在 onCreate()onRestart() 中调用 jump() 来跳过广告。

    没试过。。

方法2 - Activity切换定位,修改Intent的Activity类名

Q:如何找到相关类在Smali代码中的引用?

A:1. mt管理器(或np)打开classes.dex

  1. 搜索原始类名,如com.zj.wuaipojie.ui.AdActivity,搜索类型为类名
  2. 找到正确的类后,长摁 - 复制 - 选择最后一项,如Lcom/zj/wuaipojie/ui/AdActivity; 这是类在Smali中引用时的名字。
  3. 将复制的新名字填入搜索框,搜索类型为代码

下例Intent:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
switch (position) {  
case 0:
Intent intent = new Intent();
intent.setClass(it.getContext(), ChallengeFirst.class);
it.getContext().startActivity(intent);
return;
case 1:
Intent intent2 = new Intent();
intent2.setClass(it.getContext(), ChallengeSecond.class);
it.getContext().startActivity(intent2);
return;
case 2:
Intent intent3 = new Intent(); //new一个Intent,
intent3.setClass(it.getContext(), AdActivity.class); //传入要切换的Acitivity的类名
it.getContext().startActivity(intent3); //启动对应的Activity
return;
case 3:
Intent intent4 = new Intent();
intent4.setClass(it.getContext(), ChallengeFourth.class);
it.getContext().startActivity(intent4);
return;
default:
return;
}

1
intent3.setClass(it.getContext(), AdActivity.class);

中的AdActivity改成页面Activity即可实现跳过广告。

跳过弹窗法一 - 修改xml中的versionCode

考虑到弹窗有一部分是更新提示,可以改版本号跳过更新检测。即,修改AndroidManifest.xmlversionCodeversionName

(适用于已知最新版本号的前提)

跳过弹窗法二 - Hook弹窗(算法助手

(针对那些劫持返回键的弹窗)

算法助手 - 开启对应应用的应用总开关UI下的弹窗定位(返回键可取消),再点击右上角的小三角启动应用。被劫持的返回键又能用了!

(屏蔽关键词弹窗)

算法助手 - 开启对应应用的应用总开关UI下的屏蔽关键词弹窗,输入弹窗中模糊/精确的关键词,如”广告”,即可一键屏蔽所有使用该关键词的广告!

跳过弹窗法三 - 修改dex代码

step1:定位弹窗位于哪个方法中。可利用 算法助手 - 日志 - 某个Dialog弹窗 - 调用堆栈 查看。

step2:一般弹窗需要调用 xxxDialog.show()方法才能显示。因此把Smali中调用.show()方法的语句注释掉即可。

跳过横幅广告的方法

step1: 开发助手 - 布局查看(需启用无障碍权限)- 切到横幅应用 - 点击悬浮窗上的放大镜图标 - 定位横幅广告 - 复制View Id(Hex)

step2: mt管理器 - 进入目标应用apk - XML搜索 - 搜索刚刚复制的View Id(十六进制,搜索类型 资源ID

下例:

1
2
3
4
5
6
<ImageView
android:id="@7F0801CA" <!-- 十六进制ViewId -->
android:background="@7F0D0017"
android:layout_width="-2" <!-- 弹窗宽度,-2代表 wrap_content 自适应-->
android:layout_height="150dp" <!-- 弹窗高度 -->
android:layout_marginTop="100dp" />

要跳过横幅,只需把宽高全调成0dp即可。

Jeb Decompiler

一款安卓的反编译工具,可进行动态调试

利用 Jeb 动态调试

简记一个步骤。

  1. 打开安卓usb调试功能。设置 - 点击版本号7次进入开发者选项 - 启用USB调试

  2. 设置应用可调试权限(debuggable)。打开AndroidManifest.xml - 添加android:debuggable="true" - 保存覆盖安装

  3. pc命令行输入adb指令。

    1
    2
    3
    4
    adb shell am start -D -n 包名/类名

    例如:
    adb shell am start -D -n com.zj.wuaipojie/.ui.MainActivity
  4. 打开jeb,拖入应用apk反编译。点击开始图标开始调试。

    确保进程名和你的应用一致(图中为com.zj.wuaipojie),点击附上

  5. 正式开始调试。光标停在某一行smali字节码上,ctrl+B下断点。

Log插桩

Log插桩指的是反编译APK文件时,在对应的smali文件里,添加相应的smali代码,将程序中的关键信息,以log日志的形式进行输出。


android-re
https://becks723.github.io/2025/02/11/android-re/
作者
Becks723
发布于
2025年2月11日
许可协议