android 12+从后台启动FGS限制
Posted AmyTan小小燕
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android 12+从后台启动FGS限制相关的知识,希望对你有一定的参考价值。
后台启动FGS限制
限制简介
以 android 12(API 级别 31)或更高版本为目标平台的应用在后台运行时无法启动前台服务,少数特殊情况除外。 如果应用程序在后台运行时尝试启动前台服务,而前台服务不满足其中一种异常情况,系统将抛出 ForegroundServiceStartNotAllowedException。
注意:如果一个应用调用 Context.startForegroundService() 来启动另一个应用拥有的前台服务,则这些限制仅适用于两个应用都以 Android 12 或更高版本为目标的情况。
错误日志如下
12-17 01:14:55.156 1383 12145 W ActivityManager: Background started FGS: Disallowed [callingPackage: com.debug.loggerui; callingUid: 10102; uidState: SVC ; intent: Intent cmp=com.debug.loggerui/.framework.DebugLoggerUIService ; code:DENIED; tempAllowListReason:; targetSdkVersion:31; callerTargetSdkVersion:31; startForegroundCount:0; bindFromPackage:null]
java.lang.RuntimeException: Unable to create service com.debug.loggerui.framework.DebugLoggerUIService: android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service com.debug.loggerui/.framework.DebugLoggerUIService
豁免日志如下:
12-21 01:01:17.121 2399 3193 I am_wtf : [0,2399,system_server,-1,ActivityManager,Background started FGS: Allowed [callingPackage: com.android.providers.contacts; callingUid: 10071; uidState: BFGS; intent: Intent act=android.intent.action.SIM_STATE_CHANGED cmp=com.android.providers.contacts/com.miui.providers.contacts.sim.SimStateChangedService (has extras) ; code:PROC_STATE_BFGS; tempAllowListReason:<,reasonCode:SYSTEM_ALLOW_LISTED,duration:9223372036854775807,callingUid:-1>; targetSdkVersion:33; callerTargetSdkVersion:33; startForegroundCount:0; bindFromPackage:null]]
限制原理
setFgsRestrictionLocked
FGS 有两个限制:
在 R 中,mAllowWhileInUsePermissionInFgs 是允许在前台服务中使用 while-in-use 权限。 从后台启动的 FGS 中的使用中权限可能会受到限制。 具体见 Bg start FGS的while in use权限
在S中,mAllowStartForeground是允许FGS是否startForeground。 从后台启动的服务可能不会成为 FGS。
启动或绑定或调用startForeground时会调用setFgsRestrictionLocked方法去校验上面的两个FGS限制。
private void setFgsRestrictionLocked(String callingPackage,
int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
boolean allowBackgroundActivityStarts)
.......
if (!r.mAllowWhileInUsePermissionInFgs
|| (r.mAllowStartForeground == REASON_DENIED))
// while in use权限校验
......
// 是否允许后台启动FGS校验
if (r.mAllowStartForeground == REASON_DENIED)
r.mAllowStartForeground = shouldAllowFgsStartForegroundWithBindingCheckLocked(
allowWhileInUse, callingPackage, callingPid, callingUid, intent, r,userId);
shouldAllowFgsStartForegroundWithBindingCheckLocked
mAllowStartForeground由shouldAllowFgsStartForegroundWithBindingCheckLocked方法返回值赋值;并计算赋值mInfoAllowStartForeground,以便后面打印相关信息。
是否应该允许 FGS 启动(又名 startForeground())
具体豁免情况见下面的 后台启动限制的豁免
private @ReasonCode int shouldAllowFgsStartForegroundWithBindingCheckLocked(
@ReasonCode int allowWhileInUse, String callingPackage, int callingPid,
int callingUid, Intent intent, ServiceRecord r, int userId)
ActivityManagerService.FgsTempAllowListItem tempAllowListReason =
// mDeviceIdleExceptIdleAllowlist 或 mFgsStartTempAllowList列表中
r.mInfoTempFgsAllowListReason = mAm.isAllowlistedForFgsStartLOSP(callingUid);
// 见“后台启动限制的豁免”
int ret = shouldAllowFgsStartForegroundNoBindingCheckLocked(allowWhileInUse, callingPid,callingUid, callingPackage, r);
String bindFromPackage = null;
// 查看client是否允许start FGS
if (ret == REASON_DENIED)
bindFromPackage = canBindingClientStartFgsLocked(callingUid);
if (bindFromPackage != null)
ret = REASON_FGS_BINDING;
final int uidState = mAm.getUidStateLocked(callingUid);
int callerTargetSdkVersion = -1;
try
callerTargetSdkVersion = mAm.mContext.getPackageManager()
.getTargetSdkVersion(callingPackage);
catch (PackageManager.NameNotFoundException ignored)
final String debugInfo =
// calling 相关信息
"[callingPackage: " + callingPackage
+ "; callingUid: " + callingUid
+ "; uidState: " + ProcessList.makeProcStateString(uidState)
// service信息
+ "; intent: " + intent
// 豁免reson code打印
+ "; code:" + reasonCodeToString(ret)
// 打印临时白名单的相关信息
+ "; tempAllowListReason:<"
+ (tempAllowListReason == null ? null :
(tempAllowListReason.mReason
+ ",reasonCode:"
+ reasonCodeToString(tempAllowListReason.mReasonCode)
+ ",duration:" + tempAllowListReason.mDuration
+ ",callingUid:" + tempAllowListReason.mCallingUid))
+ ">"
+ "; targetSdkVersion:" + r.appInfo.targetSdkVersion
+ "; callerTargetSdkVersion:" + callerTargetSdkVersion
+ "; startForegroundCount:" + r.mStartForegroundCount
+ "; bindFromPackage:" + bindFromPackage
+ "]";
// 赋值mInfoAllowStartForeground以便在logFgsBackgroundStart 打印这些信息
if (!debugInfo.equals(r.mInfoAllowStartForeground))
r.mLoggedInfoAllowStartForeground = false;
r.mInfoAllowStartForeground = debugInfo;
return ret;
logFgsBackgroundStart
启动Service时,如果是FGS则会去校验是否允许本次启动,后台启动FGS是否豁免等;如果不满足条件会抛出如上异常。
if (fgRequired)
// 打印Background started FGS相关log,无论是否允许启动都会打印
logFgsBackgroundStart(r);
if (r.mAllowStartForeground == REASON_DENIED && isBgFgsRestrictionEnabled(r))
String msg = "startForegroundService() not allowed due to "
+ "mAllowStartForeground false: service "
+ r.shortInstanceName;
// 打印出错信息
Slog.w(TAG, msg);
showFgsBgRestrictedNotificationLocked(r);
logFGSStateChangeLocked(r,
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED,
0, FGS_STOP_REASON_UNKNOWN);
// 抛出异常
if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, callingUid))
throw new ForegroundServiceStartNotAllowedException(msg);
return null;
如果是后台启动FGS,则无论是否豁免均会打印Background started FGS相关log
private void logFgsBackgroundStart(ServiceRecord r)
// Only log if FGS is started from background.
if (!isFgsBgStart(r.mAllowStartForeground))
return;
if (!r.mLoggedInfoAllowStartForeground)
// 主要豁免信息等保存在mInfoAllowStartForeground中
final String msg = "Background started FGS: "
+ ((r.mAllowStartForeground != REASON_DENIED) ? "Allowed " : "Disallowed ")
+ r.mInfoAllowStartForeground;
if (r.mAllowStartForeground != REASON_DENIED)
if (ActivityManagerUtils.shouldSamplePackageForAtom(r.packageName,
mAm.mConstants.mFgsStartAllowedLogSampleRate))
Slog.wtfQuiet(TAG, msg);
Slog.i(TAG, msg);
else
if (ActivityManagerUtils.shouldSamplePackageForAtom(r.packageName,
mAm.mConstants.mFgsStartDeniedLogSampleRate))
Slog.wtfQuiet(TAG, msg);
Slog.w(TAG, msg);
// 打印过后赋值为true
r.mLoggedInfoAllowStartForeground = true;
推荐解决方案
如果您发现您的应用在从后台运行时启动前台服务,请更新您的应用逻辑以使用 WorkManager。 要查看如何更新您的应用程序的示例,请查看 GitHub 上的 WorkManagerSample。
检查您的应用是否执行后台启动
为了更好地了解您的应用在后台运行时何时尝试启动前台服务,您可以启用每次出现此行为时显示的通知。 为此,请在连接到测试设备或模拟器的开发机器上执行以下 ADB 命令:
adb shell device_config put activity_manager default_fgs_starts_restriction_notification_enabled true
后台启动限制的豁免
/**
* The list of BG-FGS-Launch and temp-allow-list reason code.
* @hide
*/
@IntDef(flag = true, prefix = "REASON_" , value =
// BG-FGS-Launch reasons.
REASON_DENIED,
REASON_UNKNOWN,
REASON_OTHER,
// 前台procState
REASON_PROC_STATE_PERSISTENT,
REASON_PROC_STATE_PERSISTENT_UI,
REASON_PROC_STATE_TOP,
REASON_PROC_STATE_BTOP,
REASON_PROC_STATE_FGS,
REASON_PROC_STATE_BFGS,
// 有可见的window
REASON_UID_VISIBLE,
// 特殊uid
REASON_SYSTEM_UID,
REASON_ACTIVITY_STARTER,
// pendingIntent通知
REASON_START_ACTIVITY_FLAG,
// service的client能从后台启动FGS
REASON_FGS_BINDING,
REASON_DEVICE_OWNER,
// 资料所有者
REASON_PROFILE_OWNER,
// 应用使用配套设备管理器并声明
REASON_COMPANION_DEVICE_MANAGER,
// bg activity权限
REASON_BACKGROUND_ACTIVITY_PERMISSION,
// fgs权限
REASON_BACKGROUND_FGS_PERMISSION,
// bg activity权限的instr
REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION,
// fgs权限的instr
REASON_INSTR_BACKGROUND_FGS_PERMISSION,
// 悬浮窗权限
REASON_SYSTEM_ALERT_WINDOW_PERMISSION,
// 演示模式
REASON_DEVICE_DEMO_MODE,
// while-in-use
REASON_ALLOWLISTED_PACKAGE,
REASON_APPOP,
// 5s内可见
REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD,
// 应用获得允许ACTIVATE_VPN或ACTIVATE_PLATFORM_VPN权限
REASON_OP_ACTIVATE_VPN,
REASON_OP_ACTIVATE_PLATFORM_VPN,
// 应用是设备当前的输入法
REASON_CURRENT_INPUT_METHOD,
// while-in-use
REASON_TEMP_ALLOWED_WHILE_IN_USE,
)
@Retention(RetentionPolicy.SOURCE)
public @interface ReasonCode
在以下情况下,即使您的应用程序在后台运行,您的应用程序也可以启动前台服务:
@PowerExemptionManager.ReasonCode int mAllowStartForeground = REASON_DENIED;
应用程序在前台
- REASON_PROC_STATE_PERSISTENT
- REASON_PROC_STATE_PERSISTENT_UI
- REASON_PROC_STATE_TOP
private @ReasonCode int shouldAllowFgsStartForegroundNoBindingCheckLocked(
@ReasonCode int allowWhileInUse, int callingPid, int callingUid, String callingPackage,
@Nullable ServiceRecord targetService)
int ret = allowWhileInUse;
if (ret == REASON_DENIED)
final int uidState = mAm.getUidStateLocked(callingUid);
// Is the calling UID at PROCESS_STATE_TOP or above?
if (uidState <= PROCESS_STATE_TOP)
ret = getReasonCodeFromProcState(uidState);
- REASON_PROC_STATE_BTOP
- REASON_PROC_STATE_FGS
- REASON_PROC_STATE_BFGS
if (ret == REASON_DENIED)
final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, app ->
if (app.uid == callingUid)
final ProcessStateRecord state = app.mState;
if (state.isAllowedStartFgs())
return getReasonCodeFromProcState(state.getCurProcState());
.......
@GuardedBy("mService")
boolean isAllowedStartFgs()
return mCurProcState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
应用程序可见
- REASON_UID_VISIBLE
if (ret == REASON_DENIED)
// Does the calling UID have any visible activity?
final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid);
if (isCallingUidVisible)
ret = REASON_UID_VISIBLE;
该服务通过与通知交互来启动
该服务由不同的可见应用程序发送的 PendingIntent 启动
细节可参考 BackgroundLaunchProcessController 介绍
- REASON_START_ACTIVITY_FLAG
if (ret == REASON_DENIED)
// Is the allow activity background start flag on?
if (allowBackgroundActivityStarts)
ret = REASON_START_ACTIVITY_FLAG;
该服务由系统组件启动(root/system/nfc/shell)
- REASON_SYSTEM_UID
if (ret == REASON_DENIED)
boolean isCallerSystem = false;
final int callingAppId = UserHandle.getAppId(callingUid);
switch (callingAppId)
case ROOT_UID:
case SYSTEM_UID:
case NFC_UID:
case SHELL_UID:
isCallerSystem = true;
break;
default:
isCallerSystem = false;
break;
if (isCallerSystem)
ret = REASON_SYSTEM_UID;
该服务由具有 START_ACTIVITIES_FROM_BACKGROUND 特权权限的应用程序启动
- REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION
- REASON_BACKGROUND_ACTIVITY_PERMISSION
if (ret == REASON_DENIED)
if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
== PERMISSION_GRANTED)
ret = REASON_BACKGROUND_ACTIVITY_PERMISSION;
if (ret == REASON_DENIED)
if (targetService != null && targetService.app != null)
ActiveInstrumentation instr = targetService.app.getActiveInstrumentation();
if (instr != null && instr.mHasBackgroundActivityStartsPermission)
ret = REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
该服务由具有 START_FOREGROUND_SERVICES_FROM_BACKGROUND 特权权限的应用程序启动。
- REASON_BACKGROUND_FGS_PERMISSION
- REASON_INSTR_BACKGROUND_FGS_PERMISSION
if (ret == REASON_DENIED)
final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, app ->
if (app.uid == callingUid)
final ProcessStateRecord state = app.mState;
if (state.isAllowedStartFgs())
.......
else
final ActiveInstrumentation instr = app.getActiveInstrumentation();
if (instr != null
&& instr.mHasBackgroundForegroundServiceStartsPermission)
// 调用者是否拥有 START_FOREGROUND_SERVICES_FROM_BACKGROUND 权限
return REASON_INSTR_BACKGROUND_FGS_PERMISSION;
......
if (ret == REASON_DENIED)
if (mAm.checkPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND, callingPid,callingUid) == PERMISSION_GRANTED)
ret = REASON_BACKGROUND_FGS_PERMISSION;
应用在5s内可见
- REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD
if (ret == REASON_DENIED)
final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, app ->
if (app.uid == callingUid)
final ProcessStateRecord state = app.mState;
if (state.isAllowedStartFgs())
.......
else
.......
final long lastInvisibleTime = app.mState.getLastInvisibleTime();
if (lastInvisibleTime > 0 && lastInvisibleTime < Long.MAX_VALUE)
final long sinceLastInvisible = SystemClock.elapsedRealtime()
- lastInvisibleTime;
// 5s
if (sinceLastInvisible < mAm.mConstants.mFgToBgFgsGraceDuration)
return REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD;
......
应用申请了SYSTEM_ALERT_WINDOW权限并在权限管理页面获得用户同意
- REASON_SYSTEM_ALERT_WINDOW_PERMISSION
if (ret == REASON_DENIED)
if (mAm.mAtmInternal.hasSystemAlertWindowPermission(callingUid, callingPid,
callingPackage))
ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
应用使用配套设备管理器并声明REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND 权限或 REQUEST_COMPANION_RUN_IN_BACKGROUND 权限
尽可能使用 REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND。
- REASON_COMPANION_DEVICE_MANAGER
注意:当 CDM 应用程序具有 REQUEST_COMPANION_RUN_IN_BACKGROUND 时,该应用程序也会被放入用户白名单中。 但是,在这种情况下,我们要使用原因代码 REASON_COMPANION_DEVICE_MANAGER,因此此检查需要在 isAllowlistedForFgsStartLOSP 检查之前进行。
if (ret == REASON_DENIED)
final boolean isCompanionApp = mAm.mInternal.isAssociatedCompanionApp(
UserHandle.getUserId(callingUid), callingUid);
if (isCompanionApp)
if (isPermissionGranted(
REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND,
callingPid, callingUid)
|| isPeAndroid逆向-Android基础逆向(2-2)
[toc]
#0x00 前言
##不知所以然,请看
Android逆向-Android基础逆向(1)
Android逆向-Android基础逆向(2)
##以及java系列:
Android逆向-java代码基础(1)
Android逆向-java代码基础(2)
Android逆向-java代码基础(3)
Android逆向-java代码基础(4)
Android逆向-java代码基础(5)
Android逆向-java代码基础(6)
Android逆向-java代码基础(7)
Android逆向-java代码基础(8)
由于之前的Android逆向-Android基础逆向(2)的伪加密部分篇幅太长,导致其他内容没有完成,所以才有了这里的Android逆向-Android基础逆向(2-2)。希望可以完成计划中的内容。
##学习内容
(1)APK文件伪加密√
(2)资源文件防反编译
(3)apk打包流程
(4)apk反编译流程
(5)apk回编译流程、
#0x01 资源文件防反编译
之前说过可以通过更改第四个字段来进行防止一定程度的反编译。那么除了这种伪加密的方式,还有什么方式可以防止这种伪加密的出现呢。
来看看资源文件是如何防止反编译的。
自然我们需要研究一下xml文件的格式。四哥在2016年已经分析过了,不过那是人家分析的,只看别人分析的不能进行更深入的学习。纸上得来终觉浅,绝知此事要躬行。so,就有了这篇。
##1.第一个模块
这里对应使用一个实例分析,就用Android逆向-Android基础逆向(1)中的简单的apk来分析吧。
###1.1 Magic Number
这里魔数是00 08 00 03,这个是一个固定的值。
###1.2File Size
这个就是用来确认文件大小的。
这里是00 00 07 90 ,也就是1970个bytes。
###1.3用python实现分析
四哥用java写的,我就献丑写个python的,还在学习python的过程中,有什么错误或者做的不好的地方,还请见谅。
这个是实现这个模块的代码。但是感觉自己写的好繁琐,等一会儿适当修改一下。
2018年1月27日11:57:35,忙别的事情去了。
def fenxi(filename):
try:
f=open(filename,‘rb‘)
print ‘start--------‘
i=0
p1=""
p2=""
p3=""
p4=""
p=""
while True:
t=f.read(1)
t1=t.encode(‘hex‘)
if i==0:
p1=t1
if i==1:
p2=t1
if i==2:
p3=t1
if i==3:
p4=t1
i=i+1
if i<4:
p=p+" "
if i==4:
break
pass
p=p4+" "+p3+" "+p2+" "+p1
print "Magic Number:",p
i=0
p1=""
p2=""
p3=""
p4=""
p=""
while True:
t=f.read(1)
t1=t.encode(‘hex‘)
if i==0:
p1=t1
if i==1:
p2=t1
if i==2:
p3=t1
if i==3:
p4=t1
i=i+1
if i<4:
p=p+" "
if i==4:
break
pass
p=p4+" "+p3+" "+p2+" "+p1
print "FileSize:",p
except IOError:
print "This is bad for input ‘",name,"‘."
print "You can enter -h for help."
2.第二个模块
###1.Chunk Type
String Chunk 的标识符,默认是00 08 00 03
2.Chunk Size
String Chunk的大小。
3.String Count
字符串的个数。
4.Style Count
样式的个数
5.Unknow
6.String Pool Offset
首部偏移量,也就是String Chunk的位置。
7.Style Pool Offset
样式偏移,但是因为样式没有。所以这里全部为0
8.String Offsets
这个是字符串偏移,大小就是String count*4个bytes
9.常量池
这个就是最主要的地方了。不过中间有一个0的空字符串。需要注意,然后使用一个循环就可以简单的分析出来了。
这里帖出代码部分。
while True:
t1=f.read(1)
t2=f.read(1)
tf1=t1.encode(‘hex‘)
tf2=t2.encode(‘hex‘)
p1=tf2+tf1
ph=int(p1, 16)
p3=""
i=0
while True:
t=f.read(1)
t1=t.encode(‘hex‘)
p=int(t1, 16)
p3=p3+chr(p)
t=f.read(1)
i=i+1
if i==ph:
break
pass
print "first string:",p3
t=f.read(2)
if l==12:
t=f.read(4)
l=l+1
if l==x-1:
break
运行结果展示:
3.第三个模块 Resourceld Chunk
这个Chunk主要是存放的是AndroidManifest中用到的系统属性值对应的资源Id
3.1 Chunk Type
和其他Chunk一样,都有特征值,Resourceld Chunk的特征值是:0x00080108
3.2 Chunk Size
Size大小没有什么好解释的。
3.3Resourcelds
这里可以根据id在frameworks\base\core\res\res\values\public.xml中查找到相对应的string。
一下是简单的代码模块:
a=p/4-2
i1=0
while True:
i=0
p1=""
p2=""
p3=""
p4=""
p=""
while True:
t=f.read(1)
t1=t.encode(‘hex‘)
if i==0:
p1=t1
if i==1:
p2=t1
if i==2:
p3=t1
if i==3:
p4=t1
i=i+1
if i==4:
break
pass
p=p4+p3+p2+p1
p5=p4+" "+p3+" "+p2+" "+p1
p=int(p, 16)
print "123id:",p,"bytes","hex:",p5
i1=i1+1
if i1==a:
break
4.第四个模块
这个Chunk主要包含一个AndroidManifest文件中的命令空间的内容
4.1 Chunk Type
特征码,这里不强调了。 特征码是00 10 01 00。
4.2 Chunk Size
Chunk的大小。
4.3 Line Number
在AndroidManifest文件中的行号
4.4Unknown
未知区域,一般是ffff
4.5 Prefix
命名空间的前缀
4.6Uri
命名空间的Urk
5.第五个模块
这个模块主要是为了存放标签信息
这里要啰嗦了。第五个模块要写完的时候,突然鼠标的返回键被按到了。我都在想是不是应该在本地写了,而不是在云端写,太感人了这个。伤心。准备偷懒了。
5.1 Chunk Type
标志字段,固定字符。
00 10 01 02
5.2 Chunk Size
Chunk 大小
5.3 Line Number
行数,和上一个段一样
5.4 Unknown
位置区域
5.5Namespace Uri
标签用的uri,但是也有可能是返回 ff ff ff ff。
代码实现:
i=0
p1=""
p2=""
p3=""
p4=""
p=""
while True:
t=f.read(1)
t1=t.encode(‘hex‘)
if i==0:
p1=t1
if i==1:
p2=t1
if i==2:
p3=t1
if i==3:
p4=t1
i=i+1
if i==4:
break
pass
p=p4+p3+p2+p1
p=int(p, 16)
try :
print "Namespace Uri:",list[p]
except IndexError:
print "Namespace Uri is nothing"
5.6 name
标签名称字段
5.7 flags字段
标识是开始flags还是结束flags
5.8 Attribute Count
包含属性的个数
5.9 Class Attribute
标签包含的类属性
5.10Attributes Attribute
属性内容。包括NamespaceUri,Name,ValueString,type,Data,这五个字段。
6.第六个模块
这个和第五个块一样。
7.第七个模块
因为是和之前的模块一样这里就不做解释了
收获
##python
1.python右移的方式
2.python格式转换
3.对二进制模块分析
4.这个是最大的收货,得到了一个xml文件分析工具。
5.github地址:xml.py文件分析
结束语
感觉这里需要的内容很多,就得要分成很多小块来说。为什么这里要写关于xml的分析呢,因为加固的目的就是为了防止反编译。那么我们可以针对反编译软件进行针对化加固,在下一个小块将会详细讲解。
以上是关于android 12+从后台启动FGS限制的主要内容,如果未能解决你的问题,请参考以下文章
为啥从 firebase 控制台发送的通知能够绕过 android 后台任务限制? - 反应原生火力基地 -