记首次对安卓APP的研究
记首次对Android APP的研究
起始原因🤔
最初,我在今年购买了一块Garmin Instinct2X Solar Tactical 这个腕表有一个软件:
这个软件有非常有趣的功能:应用弹道学(Applied Ballistics),可以辅助计算弹道等,同时有一个配套的应用AB Synapse-Garmin app 该应用可以在Google play的外区下载到(也自有其他办法下载到)
当然在一顿折腾后 我成功的安装上了这个APP,并且成功的连接了我的腕表:
当点击按钮后 APP将会跳转到下面的链接(请原谅我在此处隐藏了我的device-id)
在此处你可以选择需要购买的软件许可证版本Ultralight 轻量版/Elite 精英版 售价分别是100$/300$
这实在是太可怕了,那么是不是存在某种可能允许我对购买验证进行绕过呢?比如利用Frida等?
1.初步尝试前的准备:)
1.1 Frida
首先让我们来安装一下Frida吧 我的电脑上并没有Firda😕
pip install frida
安装成功后 使用frida --version
来检查你的frida版本 这很重要
frida --version
现在 到Frida的Github仓库中下载与你安装的版本相同的Frida-server 并将它上传到Android设备 (此处因为我成功取得了手机的ROOT权限,所以我在我的手机上进行了相关操作)
https://github.com/frida/frida/releases
请注意,你需要的是Frida-server,检查好名字 不要下载错,而且最好是检查你的Android架构
adb shell
uname -m
如果是ARMv8a/aarch64 那么就找frida-server-xx.x.x-android-arm64版本下载,如果是其他的,那么请自行下载对应的架构
在下载完成后,需要将Frida-server上传到Android设备中
adb push frida-server /data/local/tmp/
现在 Frida的准备环节 基本大功告成了,让我们来做下一部分吧
1.2 apktool,jadx-gui与jadx
接下来 为了逆向的正常推进 我们需要下载这些工具
apktool:
https://apktool.org/docs/install/
麻烦自行参考安装指南进行安装,因为我实在无法总结出我究竟是怎么做的了😊
总之只需要点点指南中的链接就可以获取两个文件了:apktool.bat
,apktool.jar
然后将它们移动到你的C:/Windows
jadx-gui&jadx
https://github.com/skylot/jadx
下载releases中的最新版jadx-gui与jadx即可
真是令人激动 已经期待修改完成功的界面了😋
2 初步尝试
做了这么久的准备工作,让我们开始吧
2.1 用Frida来尝试绕过
现在 尝试一下Frida吧🤤 我还是头一次使用这个强大的工具
*adb shell中
chmod +x ./data/local/tmp/frida-server-16.5.7-android-arm64
./data/local/tmp/frida-server-16.5.7-android-arm64&
我想我们运行了frida-server,让我们用firda连接一下
出于种种原因,我想我的Frida无法正常工作 即便我切换了很多尝试😕
2.2 apktool与jadx反编译,提取源码
没关系 既然我们动态hook程序的小心思无法实现 ,也可以静态patch进去再打包编译的
使用jadx-GUI就可以在相对友好的界面反编译并查看源码了
apktool
使用apktool当然也可以提取出已经处理好的smali文件,后面也许用得到
apktool d AB Synapse - Garmin_46_apkcombo.com.apk -o output_folder
3 分析逻辑
既然我们拿到了反编译的源码,那么就来对源码进行分析吧!😤
3.1 java逻辑分析
通过jadx-GUI的搜索功能 我首先尝试了一些关键字,比如:Unlock
,但是很明显结果不是那么如意
但是不要沮丧 既然我们之前的截图出现了一些字符串 那么它们一定是硬编码进去的
这个时候我想到了那个URL(就是被隐藏了device-id的那个),URL中最有特点的参数?device-id=xx
,我猜,这个device-id
的值一定是现从连接到的设备获取的,不如让我们尝试搜搜这个
很明显 我们很幸运 这个关键字只出现了一次
emm🤔 根据上面的Elite
,Ultralight
和这里的device-id
,看来我们应该是找到了处理验证的地方,那么就复制下来进行分析吧
//Global.java
package com.p003ab.synapse.app;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.Uri;
/* loaded from: classes.dex */
public class Global {
// 常量定义
static final int BLE_PACKET_SIZE = 19; // 蓝牙数据包大小
static final String EMPTY_STRING = ""; // 空字符串常量
static final boolean IS_DEMO_MODE = false; // 是否为演示模式
static int MAX_PROFILES = 50; // 最大配置文件数
static final int MAX_PROFILES_ELITE = 10; // Elite版本最大配置文件数
static final int MAX_PROFILES_UPGRADED_ELITE = 30; // 升级后的Elite版本最大配置文件数
static final int MAX_PROFILES_UPGRADED_ULTRALIGHT = 5; // 升级后的Ultralight版本最大配置文件数
static int MAX_SEND_PROFILES = 10; // 最大发送配置文件数
static int MILS = 1; // 毫度单位
public static final String PRODUCT_ELITE = "garmin-instinct-elite-upgrade"; // Elite版本产品ID
public static final String PRODUCT_ELITE_ONLY = "garmin-elite-purchase"; // 仅购买Elite产品ID
public static final String PRODUCT_ULTRALITE = "garmin-instinct-ultralight-upgrade"; // Ultralight版本产品ID
static final int REQUEST_ENABLE_BT = 1; // 请求启用蓝牙的标志
static int STARTING_PROFILES = 1; // 启动时的配置文件数
public static final String STORE_URL = "https://applied-balllistics-inc-garmin.myshopify.com/products/"; // 产品购买链接
static int ammoId = 0; // 弹药ID
static int ammoSelected = 0; // 选中的弹药ID
static String androidId = ""; // Android设备ID
static boolean app_paused = false; // 应用是否暂停
static String btAddress = null; // 蓝牙设备地址
static boolean btConnected = false; // 蓝牙连接状态
static boolean btIsConnecting = false; // 蓝牙是否正在连接
public static String btName = null; // 蓝牙设备名称
static GunProfile currentProfile = null; // 当前枪械配置文件
static volatile int device_solver_level = -1; // 设备许可证级别
static boolean gun_is_saved = false; // 是否保存了枪械配置
static boolean hasCheckedSolverLevel = false; // 是否已检查许可证级别
static boolean isRegisterCommandSuccessful = false; // 注册命令是否成功
static boolean isRegisteringCommand = false; // 是否正在注册命令
static boolean isShowConnectDevice = true; // 是否显示连接设备界面
static boolean isValidDevice = true; // 设备是否有效
static final String kProfile = "synapse-profile"; // 配置文件标识
static BluetoothLeService mBluetoothLeService = null; // 蓝牙服务对象
static int online_solver_level = -1; // 在线许可证级别
static SharedPreferences preferences = null; // 存储首选项
static int runningActivities = 0; // 当前活动数
private static final int saveProfileNumber = -1; // 配置文件保存号
static boolean send_heartbeat = true; // 是否发送心跳
static boolean send_profiles = false; // 是否发送配置文件
static boolean sync_failed = false; // 同步失败标志
// 更新设备许可证级别,并保存设置
public static void updateSolverLevel(int i) {
device_solver_level = i; // 更新许可证级别
Preferences.saveSettings(); // 保存设置
}
// 获取当前许可证级别
public static int getSolverlevel() {
Preferences.loadSettings(); // 加载设置
return device_solver_level; // 返回许可证级别
}
// 删除指定配置文件
static void deleteProfile(GunProfile gunProfile) {
int i = gunProfile.f97id;
SharedPreferences.Editor edit = preferences.edit();
// 遍历配置文件并删除指定配置
while (true) {
int i2 = i + 1;
if (i2 < Preferences.profileCount) {
GunProfile buildProfile = GunProfile.buildProfile(i2);
buildProfile.f97id = i;
edit.putString(kProfile + buildProfile.f97id, buildProfile.ProfileToJson());
i = i2;
} else {
currentProfile = GunProfile.buildProfile(0); // 设置当前配置文件为默认配置
edit.remove(kProfile + Preferences.profileCount); // 删除最后一个配置文件
edit.apply();
Preferences.profileCount--; // 更新配置文件数
Preferences.saveSettings(); // 保存设置
return;
}
}
}
// 保存配置文件到首选项
static void saveProfile(GunProfile gunProfile) {
SharedPreferences.Editor edit = preferences.edit();
edit.putString(kProfile + gunProfile.f97id, gunProfile.ProfileToJson()); // 将配置文件转为JSON并保存
edit.apply();
}
// 将配置文件保存到数据库
static boolean saveProfileToDB() {
GunProfile gunProfile = new GunProfile();
SharedPreferences.Editor edit = preferences.edit();
// 遍历所有配置文件,检查并更新有效的配置文件
for (int i = 0; i < getProfileCount(); i++) {
gunProfile.JSONToProfile(preferences.getString(kProfile + i, null)); // 从JSON加载配置
if (gunProfile.valid == 0) {
currentProfile.valid = 1; // 设置当前配置为有效
currentProfile.checked = 0; // 未检查标记
edit.putString(kProfile + i, currentProfile.ProfileToJson()); // 更新配置文件
edit.apply();
return true;
}
}
return false;
}
// 根据许可证级别返回对应的产品ID
private static String GetStoreProduct() {
return getSolverlevel() >= 2 ? PRODUCT_ELITE : RxData.licenseLevel1Int == 4 ? PRODUCT_ELITE_ONLY : PRODUCT_ULTRALITE;
}
// 获取许可证名称(Elite或Ultralight)
public static String GetLicenseName() {
int solverlevel = getSolverlevel();
return solverlevel != -1 ? solverlevel != 2 ? "Elite" : "Ultralight" : "";
}
// 打开购买页面,跳转到商店URL
public static void ShowStore(Context context) {
// 检查设备ID是否有效,如果有效则打开商店页面
if (Preferences.deviceID != null && Preferences.deviceID != "null") {
context.startActivity(new Intent("android.intent.action.VIEW", Uri.parse(STORE_URL + GetStoreProduct() + "?device-id=" + Preferences.deviceID + "&variant=44594687672626")));
updateSolverLevel(-1); // 重置许可证级别
} else {
// 如果设备ID无效,则显示错误信息
Utilities.showExternalToast(context, "Error obtaining your device ID. Wait a few seconds and try again.");
}
}
// 获取当前配置文件数量
static int getProfileCount() {
return Preferences.profileCount;
}
// 检查蓝牙是否已连接
static boolean isConnected() {
return btConnected && isRegisterCommandSuccessful;
}
// 检查是否有已连接的设备
static boolean hasConnectedDevice() {
String str = btAddress;
return (str == null || str.isEmpty()) ? false : true;
}
// 检查枪械参数是否使用英制单位
static boolean isEnglishGunParameterUnits() {
return Preferences.gunParameterUnits == 1;
}
// 检查是否使用码为单位
static boolean isYardsUnits() {
return Preferences.rangeUnits == 1;
}
// 检查是否使用华氏温度
static boolean isFahrenheitUnits() {
return Preferences.temperatureUnits == 1;
}
// 暂停线程一段时间
static void sleep(long j) {
try {
Thread.sleep(j); // 睡眠指定时间
} catch (Exception unused) {
}
}
// 检查设备是否有网络连接
static boolean hasInternet(Context context) {
Network activeNetwork;
NetworkCapabilities networkCapabilities;
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService("connectivity");
if (connectivityManager == null || (activeNetwork = connectivityManager.getActiveNetwork()) == null || (networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork)) == null) {
return false; // 如果没有网络连接,则返回false
}
// 检查网络是否具有可用的传输方式
return networkCapabilities.hasTransport(1) || networkCapabilities.hasTransport(0) || networkCapabilities.hasTransport(3);
}
}
可以看到,涉及到核心的关键函数有updateSolverLevel()
,getSolverlevel()
,GetStoreProduct()
,GetLicenseName()
,ShowStore(Context context)
这几个,如果我们将这些函数进行修改,是否会达到预期效果呢🤔
3.2 java逻辑修改
我们已经分析出了最核心的几个关键函数了, 现在就让我们来修改一下这些函数的判断逻辑就好
package com.p003ab.synapse.app;
import java.util.prefs.Preferences;
import javax.swing.text.Utilities;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.Uri;
/* loaded from: C:\Users\gtfat\Desktop\各个apk\AB Synapse - Garmin_46_apkcombo.com\classes.dex */
public class Global {
static final int BLE_PACKET_SIZE = 19;
static final String EMPTY_STRING = "";
static final boolean IS_DEMO_MODE = false;
static int MAX_PROFILES = 50;
static final int MAX_PROFILES_ELITE = 10;
static final int MAX_PROFILES_UPGRADED_ELITE = 30;
static final int MAX_PROFILES_UPGRADED_ULTRALIGHT = 5;
static int MAX_SEND_PROFILES = 10;
static int MILS = 1;
public static final String PRODUCT_ELITE = "garmin-instinct-elite-upgrade";
public static final String PRODUCT_ELITE_ONLY = "garmin-elite-purchase";
public static final String PRODUCT_ULTRALITE = "garmin-instinct-ultralight-upgrade";
static final int REQUEST_ENABLE_BT = 1;
static int STARTING_PROFILES = 1;
public static final String STORE_URL = "https://applied-balllistics-inc-garmin.myshopify.com/products/";
static int ammoId = 0;
static int ammoSelected = 0;
static String androidId = "";
static boolean app_paused = false;
static String btAddress = null;
static boolean btConnected = false;
static boolean btIsConnecting = false;
public static String btName = null;
static GunProfile currentProfile = null;
static volatile int device_solver_level = 4;//先将初始默认值修改为4
static boolean gun_is_saved = false;
static boolean hasCheckedSolverLevel = true;//将此处改为ture 跳过检查
static boolean isRegisterCommandSuccessful = true;//将此处改为ture 注册成功
static boolean isRegisteringCommand = false;
static boolean isShowConnectDevice = true;
static boolean isValidDevice = true;
static final String kProfile = "synapse-profile";
static BluetoothLeService mBluetoothLeService = null;
static int online_solver_level = 4; //此处将在线值改为4
static SharedPreferences preferences = null;
static int runningActivities = 0;
private static final int saveProfileNumber = -1;
static boolean send_heartbeat = true;
static boolean send_profiles = false;
static boolean sync_failed = false;
public static void updateSolverLevel(int i) {
// device_solver_level = i;在源码的基础上直接注释修改吧 这样也许更保险
device_solver_level = 2;//此处修改为2为UltraLight 4为ELITE
Preferences.saveSettings();
}
public static int getSolverlevel() {
Preferences.loadSettings();
return device_solver_level;//可以直接使用下面的返回2/4 也可以不修改此处
// return 2;
}
static void deleteProfile(GunProfile gunProfile) {
int i = gunProfile.f97id;
SharedPreferences.Editor edit = preferences.edit();
while (true) {
int i2 = i + 1;
if (i2 < Preferences.profileCount) {
GunProfile buildProfile = GunProfile.buildProfile(i2);
buildProfile.f97id = i;
edit.putString(kProfile + buildProfile.f97id, buildProfile.ProfileToJson());
i = i2;
} else {
currentProfile = GunProfile.buildProfile(0);
edit.remove(kProfile + Preferences.profileCount);
edit.apply();
Preferences.profileCount--;
Preferences.saveSettings();
return;
}
}
}
static void saveProfile(GunProfile gunProfile) {
SharedPreferences.Editor edit = preferences.edit();
edit.putString(kProfile + gunProfile.f97id, gunProfile.ProfileToJson());
edit.apply();
}
static boolean saveProfileToDB() {
GunProfile gunProfile = new GunProfile();
SharedPreferences.Editor edit = preferences.edit();
for (int i = 0; i < getProfileCount(); i++) {
gunProfile.JSONToProfile(preferences.getString(kProfile + i, null));
if (gunProfile.valid == 0) {
currentProfile.valid = 1;
currentProfile.checked = 0;
edit.putString(kProfile + i, currentProfile.ProfileToJson());
edit.apply();
return true;
}
}
return false;
}
private static String GetStoreProduct() {
return getSolverlevel() >= 2 ? PRODUCT_ELITE : RxData.licenseLevel1Int == 4 ? PRODUCT_ELITE_ONLY : PRODUCT_ULTRALITE;//此处同上个函数 可以直接返回值 也可以不修改
// return PRODUCT_ELITE;
}
public static String GetLicenseName() {
int solverlevel = getSolverlevel();
return solverlevel != -1 ? solverlevel != 2 ? "Elite" : "Ultralight" : "";
}
public static void ShowStore(Context context) {
if (Preferences.deviceID != null && Preferences.deviceID != "null") {
context.startActivity(new Intent("android.intent.action.VIEW", Uri.parse(STORE_URL + GetStoreProduct() + "?device-id=" + Preferences.deviceID + "&variant=44594687672626")));
//updateSolverLevel(-1);
updateSolverLevel(2);
} else {
Utilities.showExternalToast(context, "Error obtaining your device ID. Wait a few seconds and try again.");
}
}
static int getProfileCount() {
return Preferences.profileCount;
}
static boolean isConnected() {
return btConnected && isRegisterCommandSuccessful;
}
static boolean hasConnectedDevice() {
String str = btAddress;
return (str == null || str.isEmpty()) ? false : true;
}
static boolean isEnglishGunParameterUnits() {
return Preferences.gunParameterUnits == 1;
}
static boolean isYardsUnits() {
return Preferences.rangeUnits == 1;
}
static boolean isFahrenheitUnits() {
return Preferences.temperatureUnits == 1;
}
static void sleep(long j) {
try {
Thread.sleep(j);
} catch (Exception unused) {
}
}
static boolean hasInternet(Context context) {
Network activeNetwork;
NetworkCapabilities networkCapabilities;
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService("connectivity");
if (connectivityManager == null || (activeNetwork = connectivityManager.getActiveNetwork()) == null || (networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork)) == null) {
return false;
}
return networkCapabilities.hasTransport(1) || networkCapabilities.hasTransport(0) || networkCapabilities.hasTransport(3);
}
}
4 patch进apk
既然我们修改好了文件 那么 我们patch进去吧😉
4.1 apktool
先让我们插入一段关于.smali文件的小简介吧
.smali 文件是 Android 应用程序反编译后的中间表示文件,类似于汇编语言的格式,用来表示 Android 应用程序的字节码。 .smali 文件由 Smali 反编译工具生成,它将 APK 中的 DEX 文件(Dalvik Executable)转换为 Smali 格式的代码。每个 .smali 文件都对应一个 Java 类文件,它们是 Java 源代码编译后的中间表示形式
简单来说,.smali 文件包含了 Android 应用的代码逻辑,但与原始 Java 代码相比,它是更低级别的表示,包含的是字节码的指令集。
我们需要首先将Global.java通过java编译成Global.class,再转为新的Global.smali文件 这样也许也可以打包回去,
javac -d ./ \sources\com\ab\synapse\app\Global.java
看起来并没有像我们计划的那样顺利 emmm...难搞
但是没关系 让我们换个逻辑 至少我们知道了.java文件与.smali文件的关联性 不如...让我们直接来修改对应逻辑的smali文件吧
4.2 .smali文件分析与修改
由于我们的研究突然出现了smali文件与语法,导致我现在对此毫无准备,我们只好借助AI来快速达到我们的目的了
updateSolverLevel()
尽管使用了AI,但我还是尽可能分析了AI替换了什么位置的值以达到修改目的,并以此为参考自己手动重新进行了修改
java代码:
public static void updateSolverLevel(int i) {
device_solver_level = 4;
Preferences.saveSettings();
}
.method public static updateSolverLevel(I)V
.locals 1
.param p0, "i" # I
# 将device_solver_level设置为4
const/4 v0, 0x4
sput v0, Lcom/p003ab/synapse/app/Global;->device_solver_level:I
# 调用Preferences.saveSettings()
invoke-static {}, Lcom/p003ab/synapse/app/Preferences;->saveSettings()V
return-void
.end method
让我们节省一些时间 直接附上修改完的smali文件吧!
.class public Lcom/ab/synapse/app/Global;
.super Ljava/lang/Object;
.source "Global.java"
# static fields
.field static final BLE_PACKET_SIZE:I = 0x13
.field static final EMPTY_STRING:Ljava/lang/String; = ""
.field static final IS_DEMO_MODE:Z = false
.field static MAX_PROFILES:I = 0x32
.field static final MAX_PROFILES_ELITE:I = 0xa
.field static final MAX_PROFILES_UPGRADED_ELITE:I = 0x1e
.field static final MAX_PROFILES_UPGRADED_ULTRALIGHT:I = 0x5
.field static MAX_SEND_PROFILES:I = 0xa
.field static MILS:I = 0x1
.field public static final PRODUCT_ELITE:Ljava/lang/String; = "garmin-instinct-elite-upgrade"
.field public static final PRODUCT_ELITE_ONLY:Ljava/lang/String; = "garmin-elite-purchase"
.field public static final PRODUCT_ULTRALITE:Ljava/lang/String; = "garmin-instinct-ultralight-upgrade"
.field static final REQUEST_ENABLE_BT:I = 0x1
.field static STARTING_PROFILES:I = 0x1
.field public static final STORE_URL:Ljava/lang/String; = "https://applied-balllistics-inc-garmin.myshopify.com/products/"
.field static ammoId:I = 0x0
.field static ammoSelected:I = 0x0
.field static androidId:Ljava/lang/String; = ""
.field static app_paused:Z = false
.field static btAddress:Ljava/lang/String; = null
.field static btConnected:Z = false
.field static btIsConnecting:Z = false
.field public static btName:Ljava/lang/String; = null
.field static currentProfile:Lcom/ab/synapse/app/GunProfile; = null
.field static volatile device_solver_level:I = 0x4
.field static gun_is_saved:Z = false
.field static hasCheckedSolverLevel:Z = true
.field static isRegisterCommandSuccessful:Z = true
.field static isRegisteringCommand:Z = true
.field static isShowConnectDevice:Z = true
.field static isValidDevice:Z = true
.field static final kProfile:Ljava/lang/String; = "synapse-profile"
.field static mBluetoothLeService:Lcom/ab/synapse/app/BluetoothLeService; = null
.field static online_solver_level:I = 0x4
.field static preferences:Landroid/content/SharedPreferences; = null
.field static runningActivities:I = 0x0
.field private static final saveProfileNumber:I = -0x1
.field static send_heartbeat:Z = true
.field static send_profiles:Z
.field static sync_failed:Z
# direct methods
.method static constructor <clinit>()V
.registers 0
return-void
.end method
.method public constructor <init>()V
.registers 1
.line 35
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
.method public static GetLicenseName()Ljava/lang/String;
.registers 2
.line 221
invoke-static {}, Lcom/ab/synapse/app/Global;->getSolverlevel()I
move-result v0
const/4 v1, -0x1
if-eq v0, v1, :cond_10
const/4 v1, 0x2
if-eq v0, v1, :cond_d
const-string v0, "Elite"
return-object v0
:cond_d
const-string v0, "Ultralight"
return-object v0
:cond_10
const-string v0, ""
return-object v0
.end method
.method private static GetStoreProduct()Ljava/lang/String;
.registers 2
.line 196
invoke-static {}, Lcom/ab/synapse/app/Global;->getSolverlevel()I
move-result v0
const/4 v1, 0x2
if-lt v0, v1, :cond_a
const-string v0, "garmin-instinct-elite-upgrade"
return-object v0
.line 211
:cond_a
sget v0, Lcom/ab/synapse/app/RxData;->licenseLevel1Int:I
const/4 v1, 0x4
if-ne v0, v1, :cond_12
const-string v0, "garmin-elite-purchase"
return-object v0
:cond_12
const-string v0, "garmin-instinct-ultralight-upgrade"
return-object v0
.end method
.method public static ShowStore(Landroid/content/Context;)V
.registers 4
.line 234
sget-object v0, Lcom/ab/synapse/app/Preferences;->deviceID:Ljava/lang/String;
if-eqz v0, :cond_42
sget-object v0, Lcom/ab/synapse/app/Preferences;->deviceID:Ljava/lang/String;
const-string v1, "null"
if-eq v0, v1, :cond_42
.line 235
new-instance v0, Landroid/content/Intent;
new-instance v1, Ljava/lang/StringBuilder;
const-string v2, "https://applied-balllistics-inc-garmin.myshopify.com/products/"
invoke-direct {v1, v2}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
invoke-static {}, Lcom/ab/synapse/app/Global;->GetStoreProduct()Ljava/lang/String;
move-result-object v2
invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v1
const-string v2, "?device-id="
invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v1
sget-object v2, Lcom/ab/synapse/app/Preferences;->deviceID:Ljava/lang/String;
invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v1
const-string v2, "&variant=44594687672626"
invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v1
invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v1
invoke-static {v1}, Landroid/net/Uri;->parse(Ljava/lang/String;)Landroid/net/Uri;
move-result-object v1
const-string v2, "android.intent.action.VIEW"
invoke-direct {v0, v2, v1}, Landroid/content/Intent;-><init>(Ljava/lang/String;Landroid/net/Uri;)V
.line 236
invoke-virtual {p0, v0}, Landroid/content/Context;->startActivity(Landroid/content/Intent;)V
const/4 p0, 0x4
.line 238
invoke-static {p0}, Lcom/ab/synapse/app/Global;->updateSolverLevel(I)V
goto :goto_47
:cond_42
const-string v0, "Error obtaining your device ID. Wait a few seconds and try again."
.line 241
invoke-static {p0, v0}, Lcom/ab/synapse/app/Utilities;->showExternalToast(Landroid/content/Context;Ljava/lang/String;)V
:goto_47
return-void
.end method
.method static deleteProfile(Lcom/ab/synapse/app/GunProfile;)V
.registers 5
.line 119
iget p0, p0, Lcom/ab/synapse/app/GunProfile;->id:I
.line 121
sget-object v0, Lcom/ab/synapse/app/Global;->preferences:Landroid/content/SharedPreferences;
invoke-interface {v0}, Landroid/content/SharedPreferences;->edit()Landroid/content/SharedPreferences$Editor;
move-result-object v0
:goto_8
add-int/lit8 v1, p0, 0x1
.line 124
sget v2, Lcom/ab/synapse/app/Preferences;->profileCount:I
const-string v3, "synapse-profile"
if-ge v1, v2, :cond_2e
.line 127
invoke-static {v1}, Lcom/ab/synapse/app/GunProfile;->buildProfile(I)Lcom/ab/synapse/app/GunProfile;
move-result-object v2
.line 128
iput p0, v2, Lcom/ab/synapse/app/GunProfile;->id:I
.line 129
new-instance p0, Ljava/lang/StringBuilder;
invoke-direct {p0, v3}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
iget v3, v2, Lcom/ab/synapse/app/GunProfile;->id:I
invoke-virtual {p0, v3}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
move-result-object p0
invoke-virtual {p0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object p0
invoke-virtual {v2}, Lcom/ab/synapse/app/GunProfile;->ProfileToJson()Ljava/lang/String;
move-result-object v2
invoke-interface {v0, p0, v2}, Landroid/content/SharedPreferences$Editor;->putString(Ljava/lang/String;Ljava/lang/String;)Landroid/content/SharedPreferences$Editor;
move p0, v1
goto :goto_8
:cond_2e
const/4 p0, 0x0
.line 133
invoke-static {p0}, Lcom/ab/synapse/app/GunProfile;->buildProfile(I)Lcom/ab/synapse/app/GunProfile;
move-result-object p0
sput-object p0, Lcom/ab/synapse/app/Global;->currentProfile:Lcom/ab/synapse/app/GunProfile;
.line 135
new-instance p0, Ljava/lang/StringBuilder;
invoke-direct {p0, v3}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
sget v1, Lcom/ab/synapse/app/Preferences;->profileCount:I
invoke-virtual {p0, v1}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
move-result-object p0
invoke-virtual {p0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object p0
invoke-interface {v0, p0}, Landroid/content/SharedPreferences$Editor;->remove(Ljava/lang/String;)Landroid/content/SharedPreferences$Editor;
.line 136
invoke-interface {v0}, Landroid/content/SharedPreferences$Editor;->apply()V
.line 138
sget p0, Lcom/ab/synapse/app/Preferences;->profileCount:I
add-int/lit8 p0, p0, -0x1
sput p0, Lcom/ab/synapse/app/Preferences;->profileCount:I
.line 139
invoke-static {}, Lcom/ab/synapse/app/Preferences;->saveSettings()V
return-void
.end method
.method static getProfileCount()I
.registers 1
.line 247
sget v0, Lcom/ab/synapse/app/Preferences;->profileCount:I
return v0
.end method
.method public static getSolverlevel()I
.registers 1
.line 114
invoke-static {}, Lcom/ab/synapse/app/Preferences;->loadSettings()V
.line 115
sget v0, Lcom/ab/synapse/app/Global;->device_solver_level:I
return v0
.end method
.method static hasConnectedDevice()Z
.registers 1
.line 257
sget-object v0, Lcom/ab/synapse/app/Global;->btAddress:Ljava/lang/String;
if-eqz v0, :cond_d
invoke-virtual {v0}, Ljava/lang/String;->isEmpty()Z
move-result v0
if-eqz v0, :cond_b
goto :goto_d
:cond_b
const/4 v0, 0x1
goto :goto_e
:cond_d
:goto_d
const/4 v0, 0x0
:goto_e
return v0
.end method
.method static hasInternet(Landroid/content/Context;)Z
.registers 4
const-string v0, "connectivity"
.line 290
invoke-virtual {p0, v0}, Landroid/content/Context;->getSystemService(Ljava/lang/String;)Ljava/lang/Object;
move-result-object p0
check-cast p0, Landroid/net/ConnectivityManager;
const/4 v0, 0x0
if-nez p0, :cond_c
return v0
.line 296
:cond_c
invoke-virtual {p0}, Landroid/net/ConnectivityManager;->getActiveNetwork()Landroid/net/Network;
move-result-object v1
if-nez v1, :cond_13
return v0
.line 302
:cond_13
invoke-virtual {p0, v1}, Landroid/net/ConnectivityManager;->getNetworkCapabilities(Landroid/net/Network;)Landroid/net/NetworkCapabilities;
move-result-object p0
if-eqz p0, :cond_2e
const/4 v1, 0x1
.line 303
invoke-virtual {p0, v1}, Landroid/net/NetworkCapabilities;->hasTransport(I)Z
move-result v2
if-nez v2, :cond_2d
invoke-virtual {p0, v0}, Landroid/net/NetworkCapabilities;->hasTransport(I)Z
move-result v2
if-nez v2, :cond_2d
const/4 v2, 0x3
invoke-virtual {p0, v2}, Landroid/net/NetworkCapabilities;->hasTransport(I)Z
move-result p0
if-eqz p0, :cond_2e
:cond_2d
move v0, v1
:cond_2e
return v0
.end method
.method static isConnected()Z
.registers 1
.line 252
sget-boolean v0, Lcom/ab/synapse/app/Global;->btConnected:Z
if-eqz v0, :cond_b
sget-boolean v0, Lcom/ab/synapse/app/Global;->isRegisterCommandSuccessful:Z
if-nez v0, :cond_9
goto :goto_b
:cond_9
const/4 v0, 0x1
goto :goto_c
:cond_b
:goto_b
const/4 v0, 0x0
:goto_c
return v0
.end method
.method static isEnglishGunParameterUnits()Z
.registers 2
.line 262
sget v0, Lcom/ab/synapse/app/Preferences;->gunParameterUnits:I
const/4 v1, 0x1
if-ne v0, v1, :cond_6
goto :goto_7
:cond_6
const/4 v1, 0x0
:goto_7
return v1
.end method
.method static isFahrenheitUnits()Z
.registers 2
.line 272
sget v0, Lcom/ab/synapse/app/Preferences;->temperatureUnits:I
const/4 v1, 0x1
if-ne v0, v1, :cond_6
goto :goto_7
:cond_6
const/4 v1, 0x0
:goto_7
return v1
.end method
.method static isYardsUnits()Z
.registers 2
.line 267
sget v0, Lcom/ab/synapse/app/Preferences;->rangeUnits:I
const/4 v1, 0x1
if-ne v0, v1, :cond_6
goto :goto_7
:cond_6
const/4 v1, 0x0
:goto_7
return v1
.end method
.method static saveProfile(Lcom/ab/synapse/app/GunProfile;)V
.registers 5
.line 145
sget-object v0, Lcom/ab/synapse/app/Global;->preferences:Landroid/content/SharedPreferences;
invoke-interface {v0}, Landroid/content/SharedPreferences;->edit()Landroid/content/SharedPreferences$Editor;
move-result-object v0
.line 146
invoke-virtual {p0}, Lcom/ab/synapse/app/GunProfile;->ProfileToJson()Ljava/lang/String;
move-result-object v1
.line 147
new-instance v2, Ljava/lang/StringBuilder;
const-string v3, "synapse-profile"
invoke-direct {v2, v3}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
iget p0, p0, Lcom/ab/synapse/app/GunProfile;->id:I
invoke-virtual {v2, p0}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
move-result-object p0
invoke-virtual {p0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object p0
invoke-interface {v0, p0, v1}, Landroid/content/SharedPreferences$Editor;->putString(Ljava/lang/String;Ljava/lang/String;)Landroid/content/SharedPreferences$Editor;
.line 148
invoke-interface {v0}, Landroid/content/SharedPreferences$Editor;->apply()V
return-void
.end method
.method static saveProfileToDB()Z
.registers 8
.line 157
new-instance v0, Lcom/ab/synapse/app/GunProfile;
invoke-direct {v0}, Lcom/ab/synapse/app/GunProfile;-><init>()V
.line 160
sget-object v1, Lcom/ab/synapse/app/Global;->preferences:Landroid/content/SharedPreferences;
invoke-interface {v1}, Landroid/content/SharedPreferences;->edit()Landroid/content/SharedPreferences$Editor;
move-result-object v1
const/4 v2, 0x0
move v3, v2
.line 165
:goto_d
invoke-static {}, Lcom/ab/synapse/app/Global;->getProfileCount()I
move-result v4
if-ge v3, v4, :cond_57
.line 167
sget-object v4, Lcom/ab/synapse/app/Global;->preferences:Landroid/content/SharedPreferences;
new-instance v5, Ljava/lang/StringBuilder;
const-string v6, "synapse-profile"
invoke-direct {v5, v6}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
invoke-virtual {v5, v3}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
move-result-object v5
invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v5
const/4 v7, 0x0
invoke-interface {v4, v5, v7}, Landroid/content/SharedPreferences;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
move-result-object v4
.line 168
invoke-virtual {v0, v4}, Lcom/ab/synapse/app/GunProfile;->JSONToProfile(Ljava/lang/String;)V
.line 170
iget v4, v0, Lcom/ab/synapse/app/GunProfile;->valid:I
if-nez v4, :cond_54
.line 172
sget-object v0, Lcom/ab/synapse/app/Global;->currentProfile:Lcom/ab/synapse/app/GunProfile;
const/4 v4, 0x1
iput v4, v0, Lcom/ab/synapse/app/GunProfile;->valid:I
.line 173
sget-object v0, Lcom/ab/synapse/app/Global;->currentProfile:Lcom/ab/synapse/app/GunProfile;
iput v2, v0, Lcom/ab/synapse/app/GunProfile;->checked:I
.line 174
sget-object v0, Lcom/ab/synapse/app/Global;->currentProfile:Lcom/ab/synapse/app/GunProfile;
invoke-virtual {v0}, Lcom/ab/synapse/app/GunProfile;->ProfileToJson()Ljava/lang/String;
move-result-object v0
.line 175
new-instance v2, Ljava/lang/StringBuilder;
invoke-direct {v2, v6}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
move-result-object v2
invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v2
invoke-interface {v1, v2, v0}, Landroid/content/SharedPreferences$Editor;->putString(Ljava/lang/String;Ljava/lang/String;)Landroid/content/SharedPreferences$Editor;
.line 176
invoke-interface {v1}, Landroid/content/SharedPreferences$Editor;->apply()V
move v2, v4
goto :goto_57
:cond_54
add-int/lit8 v3, v3, 0x1
goto :goto_d
:cond_57
:goto_57
return v2
.end method
.method static sleep(J)V
.registers 2
.line 279
:try_start_0
invoke-static {p0, p1}, Ljava/lang/Thread;->sleep(J)V
:try_end_3
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_3} :catch_3
:catch_3
return-void
.end method
.method public static updateSolverLevel(I)V
.registers 1
.line 109
const/4 p0, 0x4
sput p0, Lcom/ab/synapse/app/Global;->device_solver_level:I
.line 110
invoke-static {}, Lcom/ab/synapse/app/Preferences;->saveSettings()V
return-void
.end method
4.3 MT管理器
既然上面的打包方法都试了 那么只有最后的杀手锏了
MT管理器直接打开apk,访问class.dex 这时会弹出相应的界面
让我们使用DEX++编辑器来修改对应的数值吧,我很担心这是否真的会起作用😟
修改完成后便会弹出
emm...修改完成的app提示并没有检测到签名,但是没关系 我们依然可以借助MT管理器安装成功
5 阶段性胜利🥳🥳🥳
很明显 在上个阶段 我们成功安装并运行了我们修改的APP(即便它并没有签名,请原谅我在此次的偷懒),现在让我们看看成果吧
很明显 我们成功的绕过了APP的检测🥳🎉🎉这令人激动不是吗?
但是腕表端依然是没有解锁 有机会的话 我们就再来研究一下腕表端的程序! 我想有机会我会遵守我的承诺的😊 See you Next Time