0%

Android反射权限豁免原理

导出路径:aosp/out/soong/hiddenapi/hiddenapi-flags.csv
格式为:签名+annotationProperties

1
2
3
Landroid/Manifest$permission;-><init>()V,public-api,system-api,test-api,whitelist
Landroid/Manifest$permission;->ACCEPT_HANDOVER:Ljava/lang/String;,public-api,system-api,test-api,whitelist
Landroid/Manifest$permission;->ACCESS_AMBIENT_LIGHT_STATS:Ljava/lang/String;,system-api,whitelist

class转为greylist

aosp/art/tools/class2greylist
入口:Class2Greylist
输出:HiddenapiFlagsWriter

1
2
3
4
5
6
7

public void consume(String apiSignature, Map<String, String> annotationProperties,
Set<String> parsedFlags) {
if (parsedFlags.size() > 0) {
mOutput.println(apiSignature + "," + String.join(",", asSortedList(parsedFlags)));
}
}

image.png

权限豁免设置原理

1
adb shell settings put global hidden_api_policy  1

hidden_api_policy 是被写入到了Settings xml中
通过跟进代码发现这个值会在AMS的HiddenApiSettings中 mPolicy

1
2
3
4
5
6
7
static class HiddenApiSettings extends ContentObserver
implements DeviceConfig.OnPropertiesChangedListener {
@HiddenApiEnforcementPolicy private int mPolicy = HIDDEN_API_ENFORCEMENT_DEFAULT;
@HiddenApiEnforcementPolicy int getPolicy() {
return mPolicy;
}
}

然后我们在ProcessList中也追踪到了mPolicy的调用

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
boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
boolean disableHiddenApiChecks, boolean mountExtStorageFull,
String abiOverride) {
if (!disableHiddenApiChecks && !mService.mHiddenApiBlacklist.isDisabled()) {
app.info.maybeUpdateHiddenApiEnforcementPolicy(
// 这里获取policy
mService.mHiddenApiBlacklist.getPolicy());
// 这里取出 policy
@ApplicationInfo.HiddenApiEnforcementPolicy int policy =
app.info.getHiddenApiEnforcementPolicy();
// 这里右移运算,应该是为了下面的位运算
int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);
if ((policyBits & Zygote.API_ENFORCEMENT_POLICY_MASK) != policyBits) {
throw new IllegalStateException("Invalid API policy: " + policy);
}
// 做位运算
runtimeFlags |= policyBits;
}
}

private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
String seInfo, String requiredAbi, String instructionSet, String invokeWith,
long startTime) {
try {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
app.processName);
checkSlow(startTime, "startProcess: asking zygote to start proc");
final Process.ProcessStartResult startResult;
if (hostingRecord.usesWebviewZygote()) {
startResult = startWebView(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
} else if (hostingRecord.usesAppZygote()) {
final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
// 这里开始使用appzygote
startResult = appZygote.getProcess().start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
/*useUsapPool=*/ false,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
} else {
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, app.info.packageName,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
}
checkSlow(startTime, "startProcess: returned from zygote!");
return startResult;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}

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
public class ZygoteProcess {
private Process.ProcessStartResult startViaZygote(@NonNull final String processClass,
@Nullable final String niceName,
final int uid, final int gid,
@Nullable final int[] gids,
int runtimeFlags, int mountExternal,
int targetSdkVersion,
@Nullable String seInfo,
@NonNull String abi,
@Nullable String instructionSet,
@Nullable String appDataDir,
@Nullable String invokeWith,
boolean startChildZygote,
@Nullable String packageName,
boolean useUsapPool,
@Nullable String[] extraArgs)
throws ZygoteStartFailedEx {
argsForZygote.add("--runtime-flags=" + runtimeFlags);
synchronized(mLock) {
// The USAP pool can not be used if the application will not use the systems graphics
// driver. If that driver is requested use the Zygote application start path.
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
useUsapPool,
argsForZygote);
}
}

private Process.ProcessStartResult attemptUsapSendArgsAndGetResult(
ZygoteState zygoteState, String msgStr)
throws ZygoteStartFailedEx, IOException {
try (LocalSocket usapSessionSocket = zygoteState.getUsapSessionSocket()) {
final BufferedWriter usapWriter =
new BufferedWriter(
new OutputStreamWriter(usapSessionSocket.getOutputStream()),
Zygote.SOCKET_BUFFER_SIZE);
final DataInputStream usapReader =
new DataInputStream(usapSessionSocket.getInputStream());
// socket client
usapWriter.write(msgStr);
usapWriter.flush();

Process.ProcessStartResult result = new Process.ProcessStartResult();
result.pid = usapReader.readInt();
// USAPs can't be used to spawn processes that need wrappers.
result.usingWrapper = false;

if (result.pid >= 0) {
return result;
} else {
throw new ZygoteStartFailedEx("USAP specialization failed");
}
}
}
}

这里使用了socket通信,上面是socket client端,而socket server端是Zygote

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
public final class Zygote {
private static Runnable usapMain(LocalServerSocket usapPoolSocket,
FileDescriptor writePipe) {
while (true) {
try {
sessionSocket = usapPoolSocket.accept();
if (argStrings != null) {
// 这里解析我们传递的数据
args = new ZygoteArguments(argStrings);

// TODO (chriswailes): Should this only be run for debug builds?
validateUsapCommand(args);
break;
} else {
Log.e("USAP", "Truncated command received.");
IoUtils.closeQuietly(sessionSocket);

// Re-enable SIGTERM so the USAP can be flushed from the pool if necessary.
unblockSigTerm();
}
} catch(Exception e){
}
}
// 这里正式开始使用 mRuntimeFlags,这个很重要
specializeAppProcess(args.mUid, args.mGid, args.mGids,
args.mRuntimeFlags, rlimits, args.mMountExternal,
args.mSeInfo, args.mNiceName, args.mStartChildZygote,
args.mInstructionSet, args.mAppDataDir);
}
private static native void nativeSpecializeAppProcess(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir);
}

最终调用到了nativeSpecializeAppProcess,这是一个native的方法
aosp/frameworks/base/core/jni/com_android_internal_os_Zygote.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static void com_android_internal_os_Zygote_nativeSpecializeAppProcess(
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring nice_name,
jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);

SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
capabilities, capabilities,
mount_external, se_info, nice_name, false,
is_child_zygote == JNI_TRUE, instruction_set, app_data_dir);
}

static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
jlong permitted_capabilities, jlong effective_capabilities,
jint mount_external, jstring managed_se_info,
jstring managed_nice_name, bool is_system_server,
bool is_child_zygote, jstring managed_instruction_set,

jstring managed_app_data_dir) {
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
is_system_server, is_child_zygote, managed_instruction_set);
}

这里回调了Zygote中的callPostForkChildHooks方法

1
2
3
4
private static void callPostForkChildHooks(int runtimeFlags, boolean isSystemServer,
boolean isZygote, String instructionSet) {
ZygoteHooks.postForkChild(runtimeFlags, isSystemServer, isZygote, instructionSet);
}

这里调用了ZygoteHooks的postForkChild方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final class ZygoteHooks {
@libcore.api.CorePlatformApi
public static void postForkChild(int runtimeFlags, boolean isSystemServer, boolean isZygote,
String instructionSet) {
//
nativePostForkChild(token, runtimeFlags, isSystemServer, isZygote, instructionSet);

Math.setRandomSeedInternal(System.currentTimeMillis());
}
// Hook for all child processes post forking.
private static native void nativePostForkChild(long token, int runtimeFlags,
boolean isSystemServer, boolean isZygote,
String instructionSet);
}

aosp/art/runtime/native/dalvik_system_ZygoteHooks.cc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static void ZygoteHooks_nativePostForkChild(JNIEnv* env,
jclass,
jlong token,
jint runtime_flags,
jboolean is_system_server,
jboolean is_zygote,
jstring instruction_set) {
runtime_flags = EnableDebugFeatures(runtime_flags);
hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kDisabled;

Runtime* runtime = Runtime::Current();

api_enforcement_policy = hiddenapi::EnforcementPolicyFromInt(
(runtime_flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT);
// 将标记设置到了runtime中
runtime->SetHiddenApiEnforcementPolicy(api_enforcement_policy);
}

aosp/art/runtime/runtime.h

1
2
3
4
5
6
7
void SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy policy) {
hidden_api_policy_ = policy;
}

hiddenapi::EnforcementPolicy GetHiddenApiEnforcementPolicy() const {
return hidden_api_policy_;
}

反射权限限制原理

1
public native Field[] getDeclaredFields();

aosp/art/runtime/native/java_lang_Class.cc

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
67
68
69
70
71
static ObjPtr<mirror::ObjectArray<mirror::Field>> GetDeclaredFields(
Thread* self,
ObjPtr<mirror::Class> klass,
bool public_only,
bool force_resolve) REQUIRES_SHARED(Locks::mutator_lock_) {
StackHandleScope<1> hs(self);
IterationRange<StrideIterator<ArtField>> ifields = klass->GetIFields();
IterationRange<StrideIterator<ArtField>> sfields = klass->GetSFields();
size_t array_size = klass->NumInstanceFields() + klass->NumStaticFields();
hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(self);
// Lets go subtract all the non discoverable fields.
for (ArtField& field : ifields) {
if (!IsDiscoverable(public_only, hiddenapi_context, &field)) {
--array_size;
}
}
for (ArtField& field : sfields) {
if (!IsDiscoverable(public_only, hiddenapi_context, &field)) {
--array_size;
}
}
size_t array_idx = 0;
auto object_array = hs.NewHandle(mirror::ObjectArray<mirror::Field>::Alloc(
self, GetClassRoot<mirror::ObjectArray<mirror::Field>>(), array_size));
if (object_array == nullptr) {
return nullptr;
}
for (ArtField& field : ifields) {
if (IsDiscoverable(public_only, hiddenapi_context, &field)) {
ObjPtr<mirror::Field> reflect_field =
mirror::Field::CreateFromArtField<kRuntimePointerSize>(self, &field, force_resolve);
if (reflect_field == nullptr) {
if (kIsDebugBuild) {
self->AssertPendingException();
}
// Maybe null due to OOME or type resolving exception.
return nullptr;
}
object_array->SetWithoutChecks<false>(array_idx++, reflect_field);
}
}
for (ArtField& field : sfields) {
if (IsDiscoverable(public_only, hiddenapi_context, &field)) {
ObjPtr<mirror::Field> reflect_field =
mirror::Field::CreateFromArtField<kRuntimePointerSize>(self, &field, force_resolve);
if (reflect_field == nullptr) {
if (kIsDebugBuild) {
self->AssertPendingException();
}
return nullptr;
}
object_array->SetWithoutChecks<false>(array_idx++, reflect_field);
}
}
DCHECK_EQ(array_idx, array_size);
return object_array.Get();
}


template<typename T>
ALWAYS_INLINE static bool IsDiscoverable(bool public_only,
const hiddenapi::AccessContext& access_context,
T* member)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (public_only && ((member->GetAccessFlags() & kAccPublic) == 0)) {
return false;
}
// 校验权限
return !hiddenapi::ShouldDenyAccessToMember(
member, access_context, hiddenapi::AccessMethod::kNone);
}

aosp/art/runtime/hidden_api.h

1
2
3
4
5
6
enum class EnforcementPolicy {
kDisabled = 0,
kJustWarn = 1, // keep checks enabled, but allow everything (enables logging)
kEnabled = 2, // ban dark grey & blacklist
kMax = kEnabled,
};

aosp/art/runtime/hidden_api.cc

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
template<typename T>
bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method) {
DCHECK(member != nullptr);
Runtime* runtime = Runtime::Current();
// 从runtime中获取policy,
EnforcementPolicy policy = runtime->GetHiddenApiEnforcementPolicy();
DCHECK(policy != EnforcementPolicy::kDisabled)
<< "Should never enter this function when access checks are completely disabled";

const bool deny_access =
(policy == EnforcementPolicy::kEnabled) &&
IsSdkVersionSetAndMoreThan(runtime->GetTargetSdkVersion(),
api_list.GetMaxAllowedSdkVersion());

MemberSignature member_signature(member);

// Check for an exemption first. Exempted APIs are treated as white list.
if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
// Avoid re-examining the exemption list next time.
// Note this results in no warning for the member, which seems like what one would expect.
// Exemptions effectively adds new members to the whitelist.
MaybeUpdateAccessFlags(runtime, member, kAccPublicApi);
return false;
}

if (access_method != AccessMethod::kNone) {
// Print a log message with information about this class member access.
// We do this if we're about to deny access, or the app is debuggable.
if (kLogAllAccesses || deny_access || runtime->IsJavaDebuggable()) {
member_signature.WarnAboutAccess(access_method, api_list, deny_access);
}

// If there is a StrictMode listener, notify it about this violation.
member_signature.NotifyHiddenApiListener(access_method);

// If event log sampling is enabled, report this violation.
if (kIsTargetBuild && !kIsTargetLinux) {
uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
// Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
if (eventLogSampleRate != 0) {
const uint32_t sampled_value = static_cast<uint32_t>(std::rand()) & 0xffff;
if (sampled_value < eventLogSampleRate) {
member_signature.LogAccessToEventLog(sampled_value, access_method, deny_access);
}
}
}

// If this access was not denied, move the member into whitelist and skip
// the warning the next time the member is accessed.
if (!deny_access) {
MaybeUpdateAccessFlags(runtime, member, kAccPublicApi);
}
}

return deny_access;
}

可以看出判断权限的时候是获取runtime中的policy,目前9.0以上还可以使用灰名单的一些技术方案就是修改runtime中的policy值,来达到权限豁免的目的

FreeReflection原理

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

int unseal(JNIEnv *env, jint targetSdkVersion) {

char api_level_str[5];
char preview_api_str[5];
__system_property_get("ro.build.version.sdk", api_level_str);
__system_property_get("ro.build.version.preview_sdk", preview_api_str);

int api_level = atoi(api_level_str);
bool is_preview = atoi(preview_api_str) > 0;

bool isAndroidR = api_level >= 30 || (api_level == 29 && is_preview);

JavaVM *javaVM;
env->GetJavaVM(&javaVM); //获取javavm

JavaVMExt *javaVMExt = (JavaVMExt *) javaVM; //强转为结构体JavaVMExt

void *runtime = javaVMExt->runtime; // 获取其中的runtime指针

LOGV("runtime ptr: %p, vmExtPtr: %p", runtime, javaVMExt);

const int MAX = 2000;
// findOffset 动态寻址,找到runtime中的javaVM
int offsetOfVmExt = findOffset(runtime, 0, MAX, (size_t) javaVMExt);
LOGV("offsetOfVmExt: %d", offsetOfVmExt);

if (offsetOfVmExt < 0) {
return -1;
}

int startOffset = offsetOfVmExt;
if (isAndroidR) {
startOffset += 200;
}
// 继续找到 runtime中的 target_sdk_version
int targetSdkVersionOffset = findOffset(runtime, startOffset, MAX, targetSdkVersion);
LOGV("target: %d", targetSdkVersionOffset);

if (targetSdkVersionOffset < 0) {
return -2;
}

if (isAndroidR) {
// 这里将 target_sdk_version 强转为 自定义的PartialRuntimeR结构体
auto *partialRuntime = reinterpret_cast<PartialRuntimeR *>((char *) runtime +
targetSdkVersionOffset);
unseal0<PartialRuntimeR>(partialRuntime);
} else {
auto *partialRuntime = (PartialRuntime *) ((char *) runtime +
targetSdkVersionOffset);
unseal0<PartialRuntime>(partialRuntime);
}

return 0;
}

template<typename T>
int findOffset(void *start, int regionStart, int regionEnd, T value) {

if (NULL == start || regionEnd <= 0 || regionStart < 0) {
return -1;
}
char *c_start = (char *) start;

for (int i = regionStart; i < regionEnd; i += 4) {
T *current_value = (T *) (c_start + i);
if (value == *current_value) {
LOGV("found offset: %d", i);
return i;
}
}
return -2;
}

enum class EnforcementPolicy {
kNoChecks = 0,
kJustWarn = 1, // keep checks enabled, but allow everything (enables logging)
kDarkGreyAndBlackList = 2, // ban dark grey & blacklist
kBlacklistOnly = 3, // ban blacklist violations only
kMax = 3,
};


template<typename Runtime>
int unseal0(Runtime *partialRuntime) {
bool is_java_debuggable = partialRuntime->is_java_debuggable_;
bool is_native_debuggable = partialRuntime->is_native_debuggable_;
bool safe_mode = partialRuntime->safe_mode_;

// TODO validate

LOGV("is_java_debuggable: %d, is_native_debuggable: %d, safe_mode: %d", is_java_debuggable,
is_native_debuggable, safe_mode);
LOGV("hidden api policy before : %d", partialRuntime->hidden_api_policy_);
LOGV("fingerprint: %s", partialRuntime->fingerprint_.c_str());
// 这里给runtime中的policy重新赋值,kNoCheck=0,也就是不进行权限校验
partialRuntime->hidden_api_policy_ = EnforcementPolicy::kNoChecks;
LOGV("hidden api policy after: %d", partialRuntime->hidden_api_policy_);
return 0;
}

https://github.com/tiann/FreeReflection