我本以为会和谐掉,看来是我想多了。
准备工具
1、反编译
2、安卓开发
我本来不想发这个帖子的,因为之前的帖子已经补充了一个简单的删除代码。优点是简单,缺点是需要启动2次才能更新,因为第一次删除的时候会导致连接信息清空。
但这几天上YouTube一直看见这款软件的广告,遂心血来潮。那么有没有办法让APP自动还原呢?因为之前产生随机ID的时候记录下了时间戳,所以我的思路是利用保存的时间戳来判断是否需要还原APP。
一、不能自动更新的原因
1.png (92.92 KB, 下载次数: 1)
下载附件
2021-1-18 21:47 上传
先看一下安装包数据分区,重要的就2个,数据库和sp轻量级储存。因为databases文件夹下有个db文件,里面已经保存了试用的信息,这个APP每次启动的时候都会判断这个数据库是否有id在里面(上个帖子已说),如果不存在才调用用户id,这就导致虽然已经更新了随机id,但是APP并没有写入数据库当中,所以你就得每次都要手动清一下数据才能继续试用。但是绿叶就不一样了,他没有保存到数据库,所以就没那么多事。
如果在调用新id的同时删除数据库,虽然能够达到目标,但是需要启动2次,因为第一次删除的时候不仅是用户数据,连接信息也不见了,所以就要第二次启动来更新被删除的内容。这样许多追求完美的白嫖怪们叹息不已
二、全自动思路
既然调用新id的同时删除数据库慢了点,那我就要更快,在调用id之前就把数据库给删了。那么首先考虑Patch的地方是APP入口,因为只有这样才能做到最快,不要等APP加载的差不多了再去调用,这就晚了。
现在需要再写一个新类,添加到入口调用。代码整体逻辑就是先提取保存的时间戳,如果大于规定的时间(必须和更新id的时间相同)那么则删除databases下的所有文件,同时要注意第一次启动的情况,因为第一次启动是没有文件的。
我给出的代码如下:
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import java.io.File;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
public class del {
private static final String a = "Catch_Me_If_You_Can";
private static final String c = "time"; //保存的时间戳
@SuppressLint("SdCardPath")
private static final String f = "/data/data/com.和谐.androidproxy/databases"; //数据库路径
public static final String TAG = "123456789";
private static boolean now(long saveTime) {
AtomicLong t1 = new AtomicLong(0L);
AtomicLong t2 = new AtomicLong(saveTime);
if (t1.get() == t2.get()) {
return false;
}
return (System.currentTimeMillis() - saveTime)
反编后得到smali代码如下:
.class public Ldel;
.super Ljava/lang/Object;
# static fields
.field public static final TAG:Ljava/lang/String; = "123456789"
.field private static final a:Ljava/lang/String; = "Catch_Me_If_You_Can"
.field private static final c:Ljava/lang/String; = "time"
.field private static final f:Ljava/lang/String; = "/data/data/com.和谐.androidproxy/databases"
.annotation build Landroid/annotation/SuppressLint;
value = {
"SdCardPath"
}
.end annotation
.end field
# direct methods
.method public constructor ()V
.registers 1
.line 11
invoke-direct {p0}, Ljava/lang/Object;->()V
return-void
.end method
.method public static deleteAll(Ljava/io/File;)V
.registers 6
.line 45
invoke-virtual {p0}, Ljava/io/File;->exists()Z
move-result v4
if-nez v4, :cond_7
.line 63
:goto_6
return-void
.line 48
:cond_7
invoke-virtual {p0}, Ljava/io/File;->listFiles()[Ljava/io/File;
move-result-object v4
invoke-static {v4}, Ljava/util/Objects;->requireNonNull(Ljava/lang/Object;)Ljava/lang/Object;
move-result-object v2
check-cast v2, [Ljava/io/File;
.line 49
array-length v3, v2
.line 50
const/4 v1, 0x0
.line 51
if-ge v1, v3, :cond_24
.line 53
:cond_15
aget-object v0, v2, v1
.line 54
invoke-virtual {v0}, Ljava/io/File;->isFile()Z
move-result v4
if-eqz v4, :cond_28
.line 55
invoke-virtual {v0}, Ljava/io/File;->delete()Z
.line 59
:cond_20
:goto_20
add-int/lit8 v1, v1, 0x1
.line 60
if-lt v1, v3, :cond_15
.line 62
:cond_24
invoke-virtual {p0}, Ljava/io/File;->delete()Z
goto :goto_6
.line 56
:cond_28
invoke-virtual {v0}, Ljava/io/File;->isDirectory()Z
move-result v4
if-eqz v4, :cond_20
.line 57
invoke-static {v0}, Ldel;->deleteAll(Ljava/io/File;)V
goto :goto_20
.end method
.method public static kill(Landroid/content/Context;)V
.registers 7
.line 29
const-string v3, "Catch_Me_If_You_Can"
const/4 v4, 0x0
invoke-virtual {p0, v3, v4}, Landroid/content/Context;->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences;
move-result-object v0
.line 30
new-instance v1, Ljava/util/concurrent/atomic/AtomicLong;
const-string v3, "time"
const-wide/16 v4, 0x0
invoke-interface {v0, v3, v4, v5}, Landroid/content/SharedPreferences;->getLong(Ljava/lang/String;J)J
move-result-wide v4
invoke-direct {v1, v4, v5}, Ljava/util/concurrent/atomic/AtomicLong;->(J)V
.line 31
new-instance v2, Ljava/util/concurrent/atomic/AtomicBoolean;
invoke-virtual {v1}, Ljava/util/concurrent/atomic/AtomicLong;->get()J
move-result-wide v4
invoke-static {v4, v5}, Ldel;->now(J)Z
move-result v3
invoke-direct {v2, v3}, Ljava/util/concurrent/atomic/AtomicBoolean;->(Z)V
.line 32
invoke-virtual {v2}, Ljava/util/concurrent/atomic/AtomicBoolean;->get()Z
move-result v3
if-eqz v3, :cond_28
.line 37
:goto_27
return-void
.line 35
:cond_28
invoke-static {}, Ldel;->startDel()V
.line 36
const-string v3, "123456789"
const-string v4, "一草一木要爱惜,地球资源要珍惜。"
invoke-static {v3, v4}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
goto :goto_27
.end method
.method private static now(J)Z
.registers 10
const/4 v2, 0x0
.line 20
new-instance v0, Ljava/util/concurrent/atomic/AtomicLong;
const-wide/16 v4, 0x0
invoke-direct {v0, v4, v5}, Ljava/util/concurrent/atomic/AtomicLong;->(J)V
.line 21
new-instance v1, Ljava/util/concurrent/atomic/AtomicLong;
invoke-direct {v1, p0, p1}, Ljava/util/concurrent/atomic/AtomicLong;->(J)V
.line 22
invoke-virtual {v0}, Ljava/util/concurrent/atomic/AtomicLong;->get()J
move-result-wide v4
invoke-virtual {v1}, Ljava/util/concurrent/atomic/AtomicLong;->get()J
move-result-wide v6
cmp-long v3, v4, v6
if-nez v3, :cond_1a
.line 25
:cond_19
:goto_19
return v2
:cond_1a
invoke-static {}, Ljava/lang/System;->currentTimeMillis()J
move-result-wide v4
sub-long/2addr v4, p0
const-wide/32 v6, 0x66ff300
cmp-long v3, v4, v6
if-gez v3, :cond_19
const/4 v2, 0x1
goto :goto_19
.end method
.method public static startDel()V
.registers 2
.line 40
new-instance v0, Ljava/io/File;
const-string v1, "/data/data/com.和谐.androidproxy/databases"
invoke-direct {v0, v1}, Ljava/io/File;->(Ljava/lang/String;)V
.line 41
invoke-static {v0}, Ldel;->deleteAll(Ljava/io/File;)V
.line 42
return-void
.end method
有了smali代码以后,怎么加到dex里面去我就不再示范了。
三、添加到入口
反编译后打开AndroidManifest.xml配置文件,里面记录了很多属性信息,常见的修改就是改版本号和启动界面,入口很少改,改入口一般用于破解。
通过application信息,可以知道入口类叫做APP,顺着路径找一下smali,他在dex2。
1.png (106.45 KB, 下载次数: 1)
下载附件
2021-1-18 22:29 上传
1.png (66.63 KB, 下载次数: 1)
下载附件
2021-1-18 22:31 上传
那么多个方法里,一般要Patch的地方就是onCreate,当然也有例外。onCreate在安卓里面会进行初始化操作,像创建窗口、创建Activity等,某溜零大厂很喜欢玩弄onCreate估计就是因为这个。
1.png (69.43 KB, 下载次数: 2)
下载附件
2021-1-18 22:38 上传
invoke-static {p0}, Ldel;->kill(Landroid/content/Context;)V
最后在onCreate的开头添加kill调用,当你启动APP的时候优先进行时间戳判断,如果超时立即删除数据库,然后运行到获取用户id那个地方,当APP读取数据库的内容时,因为已经清空了,就误以为是第一次安装,这样就达到了自动还原所有数据的目的。