[警告]
仅供学习参考,请勿使用非法用途!
如果你自己根据文章内容将功能实现,你对Android系统的理解将上一个层次。
自定义Android 15(API级别35)操作系统。
改完之后能用5~10年不落伍。
高版本操作系统权限管理更严格,APP的一些操作会被拒绝。
有了更严格的权限校验是优势,我的改造不会去禁用掉SELinux 。
我通过虚拟机构建,内存至少48GB,硬盘建议分配最大2TB的空间。
查看被跟踪Java方法的调用栈
跟踪Java方法调用:某个被跟踪方法执行后还直接或间接调用了哪些Java方法、第一个调用到的Native方法。举例:假设被跟踪 Java方法test调用了Java方法test1,test1调用了Native方法nativeTest3,ROM内能够识别出调用了test1和nativeTest3,将test、test1、nativeTest3这三个方法所属类、方法名,参数打印出来,打印test1和nativeTest3的时候会指出它们的调用来自于test1。
代理被跟踪Java方法
代理方法获得入参
代理方法获得返回值
代理方法改变被跟踪方法的返回值
会写C/C++,如果你对C/C++属于“会写”的层次,建议先看C/C++面试题
对Android系统有基本了解:起码会写一个简单的Android App;知道Art虚拟机、Dalvik虚拟机
不提供完整源码,需要自己学习实现
不提供如何将跟踪代码投放到其他APP的思路和代码
如果你自己根据文章内容将功能实现,你对Android系统的理解将上一个层次。
为了性能,只对被跟踪方法或者被跟踪的方法改为解释执行,不将整个APP改为解释执行。
位于runtime/art_method.cc。
这个地方判断是否通过解释器执行:
1
2
if
(UNLIKELY(!runtime->IsStarted() ||
(self->IsForceInterpreter() && !IsNative() && !IsProxyMethod() && IsInvokable()))) {
改动逻辑
把if里面的条件全部取出来,把结果赋值到一个方法局部变量内,假设赋值到isInterpreter
如果isInterpreter是true,跳转到第5步
如果isInterpreter是false,根据!IsNative() && !IsProxyMethod() && IsInvokable()
判断是否是有效的Java方法,如果它返回false,跳转到第5步,如果它返回true,进入第4步
判断这个方法是否需要被跟踪,如果不需要,跳转到第5步,如果需要,将isInterpreter改为true
将上面if内的表达式替换成isInterpreter
通过ArtMethod对象获得类、方法名、方法签名
1
2
3
4
5
art::ArtMethod *artMethod = xxx;
std::string className = artMethod->GetDeclaringClass()->PrettyDescriptor();
const
char
*methodName = artMethod->GetName();
std::string signature = artMethod->GetSignature().ToString();
className:它的风格不是Java签名风格,返回值举例:com.a.b.c.Test
methodName:方法名,举例:test
signature:方法签名,举例:(IILjava/lang/String;)Ljava/lang/String;
ArtMethod::Invoke函数调用栈
当调用到我的测试函数的时候,调用到Invoke函数的调用栈:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#02 0x7344c8a508 art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)
#03 0x7344e6316c bool art::interpreter::DoCall<false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)
#04 0x734506c58c
#05 0x7345157fdc
#06 0x7344cbbdf8
#07 0x7344cbb534
#08 0x7344e6316c bool art::interpreter::DoCall<false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)
#09 0x734506c58c
#10 0x7345157fdc
#11 0x7344cbbdf8
#12 0x7344cbb534
#13 0x7344e6316c bool art::interpreter::DoCall<false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)
#14 0x734506c58c
#15 0x7345157fdc
#16 0x7344cbbdf8
#17 0x7344cbb534
#18 0x7344e6316c bool art::interpreter::DoCall<false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)
#19 0x734506c58c
#20 0x7345157fdc
#21 0x7344cbbdf8
#22 0x7344cbbb14
#23 0x7344cbab90
#24 0x7345155a3c
栈深度0号和1号是进入了自定义函数,不需要贴出来。C/C++获取调用栈是通用逻辑,此处不贴出来,自行搜索。
跟踪方法的效果先看《跟踪Java方法调用》一节。
ShadowFrame传递跟踪信息
找到ShadowFrame类,它位于runtime/interpreter/shadow_frame.h。新增两个public字段:
1
2
3
4
5
6
跟踪描述数据结构 *traceObj = nullptr;
int
depth = 0;
跟踪描述数据结构:
1
2
3
4
5
6
7
8
class
跟踪描述数据结构 {
public
:
bool
enable =
false
;
......
};
初始化跟踪信息:
当被跟踪方法被检测到需要做跟踪的时候,在当前方法所属的ShadowFrame里面添加传递信息。在下面《代理被跟踪 Java方法》的《改动Execute函数》一节中,在检查方法是否要被跟踪的时候,能够拿到ShadowFrame对象,如果方法需要被跟踪,检查是否需要被跟踪,如果需要,在ShadowFrame对象里面添加“跟踪描述数据结构”,深度是0。
跟踪信息传递:
当调用一个Java方法的时候会创建一个新的ShadowFrame,在创建新的ShadowFrame时候调用函数的ShadowFrame也存在,在这个时候可以做跟踪信息传递,需要改动这几位置:
DoCallCommon函数,位于runtime/interpreter/interpreter_common.cc。在ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get();
执行之后,判断shadow_frame里面是否有跟踪信息,如果有,那么传递给新栈帧,然后深度+1 。
MethodHandleInvokeTransform和DoMethodHandleInvokeMethod函数,它们位于runtime/method_handles.cc。在它们调用PerformCall
函数前,传递跟踪信息。
上面这三个函数都调用了ShouldStayInSwitchInterpreter函数,还需要配合这个函数做改造,请见下一节。
ShouldStayInSwitchInterpreter新增参数
函数位于runtime/interpreter/interpreter_common.cc。这个函数用于判断被调的Java方法通过解释器执行还是通过Java编译完成的机器码执行,对于需要被跟踪的方法,我们要改成解释器执行。
这个函数的源码:
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
bool
ShouldStayInSwitchInterpreter(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) {
if
(!Runtime::Current()->IsStarted()) {
return
true
;
}
if
(UNLIKELY(method->IsNative() || method->IsProxyMethod())) {
return
false
;
}
if
(Thread::Current()->IsForceInterpreter()) {
return
true
;
}
if
(Thread::Current()->IsAsyncExceptionPending()) {
return
true
;
}
const
void
* code = method->GetEntryPointFromQuickCompiledCode();
return
Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(code);
}
我需要在const void* code = method->GetEntryPointFromQuickCompiledCode();
这条语句之前判断是否有跟踪信息,我们的跟踪信息保存在栈帧内,但是函数没有传入栈帧,所以需要将入参从ArtMethod* method
改为ShadowFrame& shadow_frame, ArtMethod* method
,新增ShadowFrame类型的入参。
更改后,上面三处调用这个函数的地方需要把栈帧传递进来,不同函数传入的栈帧对象不同:
DoCallCommon:不需要传入新的栈帧,传入调用来源方法的栈帧即可,因为新栈帧的创建在函数内是滞后的,如果需要跟踪,调用来源方法的栈帧中一定有跟踪信息,所以能够满足需求。
MethodHandleInvokeTransform和DoMethodHandleInvokeMethod:由于在调用ShouldStayInSwitchInterpreter函数前,新栈帧已经创建完成,并且按照上面的逻辑我们传递了跟踪信息,所以这两个函数传递新栈帧,能够满足需求。
位于runtime/art_method.cc。在上文对这个函数的改造中,对于需要被跟踪的Java方法改成了解释执行,改动完成后会调用到Execute函数(下文会做描述),我在Execute函数内做了跟踪代理,但是如果是从Java层调用到Native函数那么不会进入Execute函数,我希望从Java直接调用到Native的函数也能被检测、代理,本节会讲如何做这件事情。
代码说明:
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
NO_STACK_PROTECTOR
void
ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
const
char
* shorty) {
......
Runtime* runtime = Runtime::Current();
bool
isValidJavaMethod = !IsNative() && !IsProxyMethod() && IsInvokable();
auto
isInterpreter = UNLIKELY(!runtime->IsStarted() ||
(self->IsForceInterpreter() && isValidJavaMethod));
if
(这个APP是否对部分方法添加了跟踪) {
}
if
(isInterpreter) {
if
(IsStatic()) {
art::interpreter::EnterInterpreterFromInvoke(
self,
this
, nullptr, args, result,
true
);
}
else
{
mirror::Object* receiver =
reinterpret_cast
<StackReference<mirror::Object>*>(&args[0])->AsMirrorPtr();
art::interpreter::EnterInterpreterFromInvoke(
self,
this
, receiver, args + 1, result,
true
);
}
}
else
{
......
bool
isUseNewValue =
false
;
if
(IsNative()) {
if
(是否需要被跟踪,上面判断过,直接复用) {
JValue newValue;
bool
useNewValue =
false
;
处理跟踪(被跟踪方法相关信息, &newValue, useNewValue);
if
(useNewValue) {
*result = newValue;
isUseNewValue =
true
;
}
}
}
if
(!isUseNewValue) {
if
(!IsStatic()) {
(*art_quick_invoke_stub)(
this
, args, args_size, self, result, shorty);
}
else
{
(*art_quick_invoke_static_stub)(
this
, args, args_size, self, result, shorty);
}
......
}
}
else
{
LOG(INFO) <<
"Not invoking '"
<< PrettyMethod() <<
"' code=null"
;
if
(result != nullptr) {
result->SetJ(0);
}
}
}
self->PopManagedStackFragment(fragment);
}
解析入参转换成jvalue数组
上面调用代理类、方法,是Java方法,需要将ArtMethod::Invoke的入参uint32_t* args
转换成能够通过Jni接口CallObjectMethodV调用的入参,它通过jvalue*接收方法参数数组,jvalue是一个联合类型。
转换说明:
正确的取出参数:如果是解释执行,ArtMethod::Invoke函数会调用到EnterInterpreterFromInvoke函数(位于runtime/interpreter/interpreter.cc),这个函数内解析了args,将它里面的数据设置到了ShadowFrame里面,参数取值参考这段代码。
参数类型转换:
参数转换完成后,就可以通过JNI接口调用你自己的代理类、方法了。
拦截返回值,将jobject转换成JValue
我们的代理方法返回对象类型,类型结构如:
1
2
3
4
5
6
7
8
9
10
11
12
13
public
class
Result {
/**
* 是否拦截
*/
public
boolean
useNewValue;
/**
* 拦截值
*/
public
Object value;
}
CallObjectMethodV调用后返回值是jobject类型,返回值是Java的Result对象,如果useNewValue等于true,我要提取对象里面的value字段并返回,此时在Native层拿到的value是jobject对象。
通过阅读代码可知我需要返回JValue类型,JValue类型内部存储的对象需要调用SetL,入参是ObjPtr<mirror::Object>类型(参考art/runtime/jvalue.h文件)。我需要将jobject转换成ObjPtr<mirror::Object>,转换代码:
1
2
3
4
art::JValue value;
art::ScopedObjectAccess soa(env);
art::ObjPtr<art::mirror::Object> innerObj = soa.Decode<art::mirror::Object>(obj);
value.SetL(innerObj);
位于runtime/interpreter/interpreter.cc。执行到这个函数意味着目前是解释执行。
这个函数内在后面一点的位置会从栈帧内获得ArtMethod对象:ArtMethod *method = shadow_frame.GetMethod();
。把这个语句提到函数开头:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
NO_STACK_PROTECTOR
static
inline
JValue Execute(
Thread* self,
const
CodeItemDataAccessor& accessor,
ShadowFrame& shadow_frame,
JValue result_register,
bool
stay_in_interpreter =
false
,
bool
from_deoptimize =
false
) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!shadow_frame.GetMethod()->IsAbstract());
DCHECK(!shadow_frame.GetMethod()->IsNative());
ArtMethod *method = shadow_frame.GetMethod();
......
}
在获得ArtMethod *
之后做下面逻辑:
判断Java方法是否需要跟踪。
如果需要跟踪,判断是否要执行代理Java方法
需要执行Java代理方法,执行完成后,判断是否要做返回值拦截
这三点在《ArtMethod::Invoke函数内处理跟踪和代理》这一节以及讲过,区别是这个函数内以及为这个被调方法创建了栈帧,我需要在栈帧内取出被调方法参数。栈帧的创建在上文中提到的EnterInterpreterFromInvoke函数内,这个函数内调用的是SetXXX函数向栈帧设置方法参数,我只需要调用相应的GetXXX方法就可以取出。
如果要知道方法参数+返回值,可以在这个函数最后调用ExecuteSwitch
之后,做调用代理方法和返回值拦截相关逻辑。
为什么在ArtMethod::Invoke和Execute函数内都要检查一次是否需要跟踪?
ArtMethod::Invoke检查跟踪:
用于决定是否解释执行Java方法,如果是,那么调用链最终会调用到Execute函数
用于确定是否要对在Java层注册的Native函数做跟踪
Execute函数:
我发现有时候需要解释执行的Java方法调用,不仅来自于ArtMethod::Invoke函数,所以此处还需要判断一次(PS: 我在ArtMethod::Invoke这里判断被跟踪方法强制解释执行以及做了“跟踪Java方法调用解释执行”之后,被跟踪方法、需要跟踪的方法的执行经过ArtMethod::Invoke了,需要持续观察)
某个被跟踪方法执行后还直接或间接调用了哪些Java方法、第一个调用到的Native方法。举例:假设被跟踪 Java方法test调用了Java方法test1,test1调用了Native方法nativeTest3,ROM内能够识别出调用了test1和nativeTest3,将test、test1、nativeTest3这三个方法所属类、方法名,参数打印出来,打印test1和nativeTest3的时候会指出它们的调用来自于test1。
上面《将目标方法改为解释执行》的步骤一定要做。
请见《跟踪Java方法调用解释执行》一节,这一节不仅让跟踪方法解释执行,还传递了跟踪标识。
所谓跟踪数据是指要跟踪哪些方法。找到VMRuntime_setProcessPackageName
函数,位于runtime/native/dalvik_system_VMRuntime.cc,这个函数设置了包名,我在设置了包名的时候就加载这个包对应的跟踪数据。
找到源码中雷系下面的代码,你看一下系统是怎么遍历并打印Java调用堆栈的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
jobject internal =
soa.AddLocalReference<jobject>(Thread::Current()->CreateInternalStackTrace(soa));
if
(internal == nullptr) {
return
;
}
jobjectArray ste_array = Thread::InternalStackTraceToStackTraceElementArray(soa, internal);
if
(ste_array == nullptr) {
return
;
}
ObjPtr<mirror::ObjectArray<mirror::StackTraceElement>> trace_array =
soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>>(ste_array);
if
(trace_array == nullptr) {
return
;
}
类似下面的报错:
FAILED: out/soong/.intermediates/art/runtime/libart/android_arm_armv8-a_shared_apex31/unstripped/libart.so
prebuilts/clang/host/linux-x86/clang-r530567/bin/clang++ out/soong/.intermediates/bionic/libc/crtbegin_so/android_arm_armv8-a_apex31/crtbegin_so.o @out/soong/.intermediates/art/runtime/libart/android_arm_armv8-a_shared_apex31/unstripped/libart.so.rsp out/soong/.intermediates/bionic/libc/crtend_so/android_arm_arm
v8-a_apex31/crtend_so.o out/soong/.intermediates/bionic/libc/crt_pad_segment/android_arm_armv8-a_apex31/crt_pad_segment.o -o out/soong/.intermediates/art/runtime/libart/android_arm_armv8-a_shared_apex31/unstripped/libart.so -target armv7a-linux-androideabi31 -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--build
-id=md5 -Wl,--fatal-warnings -Wl,--no-undefined-version -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libgcc_stripped.a -Wl,--exclude-libs,libunwind_llvm.a -Wl,--exclude-libs,libunwind.a -fuse-ld=lld -Wl,--icf=safe -Wl,--no-demangle -Wl,--compress-debug-sections=zstd -Wl,--pack-dyn-relocs=android+relr -Wl,--no-
undefined -Wl,-m,armelf -Wl,-mllvm -Wl,-enable-shrink-wrap=false -nostdlib -Wl,--gc-sections -shared -Wl,-soname,libart.so -Wl,--exclude-libs=libziparchive.a -Wl,--keep-unique,__jit_debug_register_code -Wl,--keep-unique,__dex_debug_register_code -flto=thin -fsplit-lto-unit -Wl,-plugin-opt,-import-instr-limit=5
-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a
ld.lld: error: undefined symbol: _ZN3art9ArtMethod17GetDeclaringClassILNS_17ReadBarrierOptionE0EEENS_6ObjPtrINS_6mirror5ClassEEEv
>>> referenced by ArtMethodHelper.cpp:19 (android_custom_my/art/runtime/mycustomcodes/art/ArtMethodHelper.cpp:19)
>>> out/soong/.intermediates/art/runtime/libart-runtime/android_arm_armv8-a_static_afdo-libart_lto-thin_apex31/libart.so.lto.libart-runtime.a(ArtMethodHelper.o at 1631918).o:(_ZN11mycustomcodes15ArtMethodHelper11printMethodEPKcS2_PN3art6ThreadEPNS3_9ArtMethodE)
>>> referenced by ArtMethodHelper.cpp:56 (android_custom_my/art/runtime/mycustomcodes/art/ArtMethodHelper.cpp:56)
>>> out/soong/.intermediates/art/runtime/libart-runtime/android_arm_armv8-a_static_afdo-libart_lto-thin_apex31/libart.so.lto.libart-runtime.a(ArtMethodHelper.o at 1631918).o:(_ZN11mycustomcodes15ArtMethodHelper14matchClassNameEPN3art9ArtMethodEPKc)
ld.lld: error: undefined symbol: _ZN3art9ArtMethod7GetNameEv
>>> referenced by ArtMethodHelper.cpp:26 (android_custom_my/art/runtime/mycustomcodes/art/ArtMethodHelper.cpp:26)
>>> out/soong/.intermediates/art/runtime/libart-runtime/android_arm_armv8-a_static_afdo-libart_lto-thin_apex31/libart.so.lto.libart-runtime.a(ArtMethodHelper.o at 1631918).o:(_ZN11mycustomcodes15ArtMethodHelper11printMethodEPKcS2_PN3art6ThreadEPNS3_9ArtMethodE)
ld.lld: error: undefined symbol: _ZN3art9ArtMethod13IsProxyMethodEv
>>> referenced by ArtMethodHelper.cpp:37 (android_custom_my/art/runtime/mycustomcodes/art/ArtMethodHelper.cpp:37)
>>> out/soong/.intermediates/art/runtime/libart-runtime/android_arm_armv8-a_static_afdo-libart_lto-thin_apex31/libart.so.lto.libart-runtime.a(ArtMethodHelper.o at 1631918).o:(_ZN11mycustomcodes15ArtMethodHelper11printMethodEPKcS2_PN3art6ThreadEPNS3_9ArtMethodE)
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
10:39:52 ninja failed with: exit status 1
如果懒得看到底哪个符号找不到,把这段报错放到大模型里面让它给解析出来。这段报错是ArtMethod的一些方法找不到符号,依赖了#include "art_method.h"
头文件后如果还报此类错误,那么需要依赖#include "art_method-inl.h"
,-inl.h
结尾的头文件存放所有inline函数的实现,如果你调用到了inline函数,不能只依赖函数定义,必须要依赖实现。
背景
我自定义ROM里面自定义了AIDL,AIDL生成的代码也会被代码质量问题检测Lint工具报错,AIDL是不能添加诸如:@SuppressLint、@NonNull这样的注解的。遇到这样的问题有几种解法:
改AIDL生成工具,让它能够生成这些注解
改Lint工具不让它检测类似问题
让Lint忽略此类错误
方案一:改Lint工具
tools/metalava/metalava/src/main/java/com/android/tools/metalava/lint/ApiLint.kt
checkFlaggedApiOnNewApi函数把report语句注释掉。
方案二:让Lint忽略此类错误
需要重新生成lint-baseline.txt文件。
Lint报错后会在后面显示类似下面的提示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
74
more
error(s) omitted. Search the log
for
'error:'
to
find
all of them.
metalava wrote updated baseline to
/home/asher/android/lineage/out/soong/
.temp
/sbox/dcabbf69fbe8e32ae16e786d21b129ef1759ab95/
.
/out/api_lint_baseline
.txt
************************************************************
Your API changes are triggering API Lint warnings or errors.
To
make
these errors go away, fix the code according to the
error and
/or
warning messages above.
If it is not possible to
do
so, there are workarounds:
1. You can suppress the errors with @SuppressLint(
"<id>"
)
where the <
id
> is given
in
brackets
in
the error message above.
2. You can update the baseline by executing the following
command
:
(
cd
$ANDROID_BUILD_TOP &&
cp
\
"out/soong/.intermediates/frameworks/base/api/api-stubs-docs-non-updatable/android_common/everything/api_lint_baseline.txt"
\
"frameworks/base/core/api/lint-baseline.txt"
)
To submit the revised baseline.txt to the main Android
repository, you will need approval.
************************************************************
exit
status 255
23:47:10 ninja failed with:
exit
status 1
There was 1 action that completed after the action that failed. See verbose.log.gz
for
its output.
上面的第2点提示了如何重新生成基线文件(lint-baseline.xml)。运行完成后,重新编译系统。
lint-baseline.txt
的作用与局限性
功能 :
lint-baseline.txt
是 Lint 工具的基线配置文件,用于标记当前项目中已知但暂时不修复的问题。
它通过记录问题的类型、位置等信息,让后续的 Lint 检查忽略这些已记录的问题,避免重复报告。
局限性 :
不能动态抑制新问题 :基线文件仅对已存在的旧问题生效,若新生成的代码触发了相同类型的 Lint 错误(如 UnflaggedApi
),Lint 仍会报告。
依赖文件路径和行号 :基线文件中的条目通常包含具体的代码位置(如文件路径、行号)。如果生成的代码路径或内容发生变化(例如 AIDL 重新生成代码),原有基线条目会失效。
不修改 Lint 规则本身 :基线文件仅隐藏问题,而非解决根本原因。若需彻底禁用某类检查(如 UnflaggedApi
),需通过其他方式配置 Lint 规则。
普通APP获得自定义系统服务
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
package
android.mycustomcodes.services;
import
android.os.IBinder;
import
android.os.ServiceManager;
import
androidx.annotation.Nullable;
public
class
MyCustomerSystemServiceHelper {
private
static
final
String MODULE =
"MyCustomerSystemServiceHelper"
;
private
MyCustomerSystemServiceHelper() {
}
@Nullable
public
static
IMyCustomerSystemService getService() {
IMyCustomerSystemService service =
null
;
try
{
IBinder iBinder = ServiceManager.getService(MyCustomerSystemService.SERVICE_NAME);
if
(
null
== iBinder) {
Log.e(MODULE,
"binder get failed"
);
}
else
{
service = IMyCustomerSystemService.Stub.asInterface(iBinder);
}
}
catch
(Exception e) {
}
return
service;
}
}
修改SELinux规则能够正常注册
如果不修改SELinux规则,注册服务的时候将会在logcat打印出这样的错误:
Exception
java.lang.SecurityException: SELinux denied for service.
at android.os.Parcel.createExceptionOrNull(Parcel.java:3231)
at android.os.Parcel.createException(Parcel.java:3215)
at android.os.Parcel.readException(Parcel.java:3198)
at android.os.Parcel.readException(Parcel.java:3140)
at android.os.IServiceManager$Stub$Proxy.addService(IServiceManager.java:526)
at android.os.ServiceManagerProxy.addService(ServiceManagerNative.java:78)
at android.os.ServiceManager.addService(ServiceManager.java:253)
at android.os.ServiceManager.addService(ServiceManager.java:218)
at com.android.server.SystemServer.startMyCustomerSystemService(SystemServer.java:3463)
at com.android.server.SystemServer.run(SystemServer.java:955)
at com.android.server.SystemServer.main(SystemServer.java:674)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:591)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:849) ,我添加的是自定义服务MyCustomerSystemService
进入system/sepolicy/private/system_server.te
文件,添加allow system_server my_customer_system_service:service_manager add;
,如果你添加到最后,注意最后一行必须是空行。
修改SELinux规则能够获得服务
如果不修改SELinux规则,获得服务的时候将会在logcat打印出这样的错误:
2025-03-25 15:31:05.571 0-0 SELinux kernel E avc: denied { find } for pid=5438 uid=10208 name=my_customer_system_service scontext=u:r:untrusted_app:s0:c208,c256,c512,c768 tcontext=u:object_r:default_android_service:s0 tclass=service_manager permissive=0
打开system/sepolicy/private/service_contexts
文件,添加:
my_customer_system_service u:object_r:my_customer_system_service:s0
这行语句不要添加到文件最后面,我添加到文件最后面的时候出现了编译失败。
在service_contexts文件的相同目录下新增文件my_customer_system_service.te
,内容:
## 声明服务类型
type my_customer_system_service, service_manager_type;
## 允许所有应用查找和绑定服务
allow { untrusted_app platform_app } my_customer_system_service:service_manager { find };
allow { untrusted_app platform_app } my_customer_system_service:binder { call transfer }; # 允许调用方法
注意,最后一行必须是空行。
添加模糊测试
在SELinux规则里面新增服务编译报错:
FAILED: out/soong/.intermediates/system/sepolicy/contexts/fuzzer_bindings_test/android_common/timestamp
out/host/linux-x86/bin/fuzzer_bindings_check -s out/soong/.intermediates/system/sepolicy/contexts/plat_service_contexts/android_common/plat_service_contexts -b out/soong/.intermediates/bindings.json && touch out/soong/.intermediates/system/sepolicy/contexts/fuzzer_bindings_test/android_common/timestamp # hash of
input list: 691a90120ba890c7387a1082f843bac17e8e7769875e131b7d72fabe549de9ef
error: Service 'my_customer_system_service' is being added, but we have no fuzzer on file for it. Fuzzers are listed at $ANDROID_BUILD_TOP/system/sepolicy/build/soong/service_fuzzer_bindings.go
NOTE: automatic service fuzzers are currently not supported in Java (b/287102710.)In this case, please ignore this for now and add an entry for yournew service in service_fuzzer_bindings.go
If you are writing a new service, it may be subject to attack from other potentially malicious processes. A fuzzer can be written automatically by adding these things:
- a cc_fuzz Android.bp entry
- a main file that constructs your service and calls 'fuzzService'
An examples can be found here:
- $ANDROID_BUILD_TOP/hardware/interfaces/vibrator/aidl/default/fuzzer.cpp
- https://source.android.com/docs/core/architecture/aidl/aidl-fuzzing
This is only ~30 lines of configuration. It requires dependency injection for your service which is a good practice, and (in AOSP) you will get bugs automatically filed on you. You will find out about issues without needing to backport changes years later, and the system will automatically find ways to reproduce
difficult to solve issues for you.
This error can be bypassed by adding entry for new service in $ANDROID_BUILD_TOP/system/sepolicy/build/soong/service_fuzzer_bindings.go
- Android Fuzzing and Security teams
19:20:03 ninja failed with: exit status 1
There were 3 actions that completed after the action that failed. See verbose.log.gz for their output.
打开service_fuzzer_bindings.go
文件,ServiceFuzzerBindings
列表里面新增:
"my_customer_system_service": EXCEPTION_NO_FUZZER,
我没有放到最后,我是在列表里面插入了一条。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2025-4-8 09:40
被不歪编辑
,原因: 修改文章问题