Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
M
Magisk
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Magisk
Commits
a260e990
Commit
a260e990
authored
Aug 11, 2021
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support code injection on Android 12
parent
25efdd3d
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
180 additions
and
53 deletions
+180
-53
entry.cpp
native/jni/inject/entry.cpp
+5
-3
hook.cpp
native/jni/inject/hook.cpp
+142
-16
inject.hpp
native/jni/inject/inject.hpp
+4
-4
memory.cpp
native/jni/inject/memory.cpp
+2
-1
ptrace.cpp
native/jni/inject/ptrace.cpp
+4
-5
utils.cpp
native/jni/inject/utils.cpp
+23
-24
No files found.
native/jni/inject/entry.cpp
View file @
a260e990
...
...
@@ -87,9 +87,8 @@ static void inject_cleanup_wait() {
__attribute__
((
constructor
))
static
void
inject_init
()
{
inject_logging
();
if
(
getenv
(
INJECT_ENV_2
))
{
inject_logging
();
LOGD
(
"zygote: inject 2nd stage
\n
"
);
active_threads
=
1
;
unsetenv
(
INJECT_ENV_2
);
...
...
@@ -97,6 +96,7 @@ static void inject_init() {
// Get our own handle
self_handle
=
dlopen
(
INJECT_LIB_2
,
RTLD_LAZY
);
dlclose
(
self_handle
);
unlink
(
INJECT_LIB_2
);
hook_functions
();
...
...
@@ -105,6 +105,7 @@ static void inject_init() {
active_threads
++
;
new_daemon_thread
(
&
unload_first_stage
);
}
else
if
(
char
*
env
=
getenv
(
INJECT_ENV_1
))
{
inject_logging
();
LOGD
(
"zygote: inject 1st stage
\n
"
);
if
(
env
[
0
]
==
'1'
)
...
...
@@ -117,6 +118,7 @@ static void inject_init() {
cp_afc
(
INJECT_LIB_1
,
INJECT_LIB_2
);
dlopen
(
INJECT_LIB_2
,
RTLD_LAZY
);
unlink
(
INJECT_LIB_1
);
unsetenv
(
INJECT_ENV_1
);
}
}
...
...
@@ -128,7 +130,7 @@ int app_process_main(int argc, char *argv[]) {
return
1
;
int
in
=
xopen
(
buf
,
O_RDONLY
);
int
out
=
xopen
(
INJECT_LIB_1
,
O_
CREAT
|
O_WRONLY
|
O_TRUNC
,
0777
);
int
out
=
xopen
(
INJECT_LIB_1
,
O_
WRONLY
|
O_CREAT
,
0644
);
sendfile
(
out
,
in
,
nullptr
,
INT_MAX
);
close
(
in
);
close
(
out
);
...
...
native/jni/inject/hook.cpp
View file @
a260e990
#include <dlfcn.h>
#include <xhook.h>
#include <utils.hpp>
#include <flags.hpp>
...
...
@@ -64,13 +65,17 @@ struct HookContext {
void
*
raw_args
;
};
};
struct
vtable_t
;
static
vector
<
tuple
<
const
char
*
,
const
char
*
,
void
**>>
*
xhook_list
;
static
vector
<
JNINativeMethod
>
*
jni_hook_list
;
static
hash_map
<
xstring
,
tree_map
<
xstring
,
tree_map
<
xstring
,
void
*>>>
*
jni_method_map
;
static
JavaVM
*
g_jvm
;
static
HookContext
*
current_ctx
;
static
JavaVM
*
g_jvm
;
static
vtable_t
*
gAppRuntimeVTable
;
static
const
JNINativeInterface
*
old_functions
;
static
JNINativeInterface
*
new_functions
;
#define DCL_HOOK_FUNC(ret, func, ...) \
static ret (*old_##func)(__VA_ARGS__); \
...
...
@@ -103,10 +108,8 @@ if (methods[i].name == #method##sv) { \
continue; \
}
DCL_HOOK_FUNC
(
int
,
jniRegisterNativeMethods
,
static
unique_ptr
<
JNINativeMethod
[]
>
hookAndSaveJNIMethods
(
JNIEnv
*
env
,
const
char
*
className
,
const
JNINativeMethod
*
methods
,
int
numMethods
)
{
LOGD
(
"hook: jniRegisterNativeMethods %s"
,
className
);
if
(
g_jvm
==
nullptr
)
{
// Save for later unhooking
env
->
GetJavaVM
(
&
g_jvm
);
...
...
@@ -129,7 +132,40 @@ DCL_HOOK_FUNC(int, jniRegisterNativeMethods,
HOOK_JNI
(
nativeForkSystemServer
);
}
}
return
newMethods
;
}
static
jclass
gClassRef
;
static
jmethodID
class_getName
;
static
string
get_class_name
(
JNIEnv
*
env
,
jclass
clazz
)
{
if
(
!
gClassRef
)
{
jclass
cls
=
env
->
FindClass
(
"java/lang/Class"
);
gClassRef
=
(
jclass
)
env
->
NewGlobalRef
(
cls
);
env
->
DeleteLocalRef
(
cls
);
class_getName
=
env
->
GetMethodID
(
gClassRef
,
"getName"
,
"()Ljava/lang/String;"
);
}
auto
nameRef
=
(
jstring
)
env
->
CallObjectMethod
(
clazz
,
class_getName
);
const
char
*
name
=
env
->
GetStringUTFChars
(
nameRef
,
nullptr
);
string
className
(
name
);
env
->
ReleaseStringUTFChars
(
nameRef
,
name
);
std
::
replace
(
className
.
begin
(),
className
.
end
(),
'.'
,
'/'
);
return
className
;
}
// -----------------------------------------------------------------
static
jint
new_env_RegisterNatives
(
JNIEnv
*
env
,
jclass
clazz
,
const
JNINativeMethod
*
methods
,
jint
numMethods
)
{
auto
className
=
get_class_name
(
env
,
clazz
);
LOGD
(
"hook: JNIEnv->RegisterNatives %s
\n
"
,
className
.
data
());
auto
newMethods
=
hookAndSaveJNIMethods
(
env
,
className
.
data
(),
methods
,
numMethods
);
return
old_functions
->
RegisterNatives
(
env
,
clazz
,
newMethods
.
get
()
?:
methods
,
numMethods
);
}
DCL_HOOK_FUNC
(
int
,
jniRegisterNativeMethods
,
JNIEnv
*
env
,
const
char
*
className
,
const
JNINativeMethod
*
methods
,
int
numMethods
)
{
LOGD
(
"hook: jniRegisterNativeMethods %s
\n
"
,
className
);
auto
newMethods
=
hookAndSaveJNIMethods
(
env
,
className
,
methods
,
numMethods
);
return
old_jniRegisterNativeMethods
(
env
,
className
,
newMethods
.
get
()
?:
methods
,
numMethods
);
}
...
...
@@ -148,11 +184,56 @@ DCL_HOOK_FUNC(int, selinux_android_setcontext,
return
old_selinux_android_setcontext
(
uid
,
isSystemServer
,
seinfo
,
pkgname
);
}
static
int
sigmask
(
int
how
,
int
signum
)
{
sigset_t
set
;
sigemptyset
(
&
set
);
sigaddset
(
&
set
,
signum
);
return
sigprocmask
(
how
,
&
set
,
nullptr
);
// -----------------------------------------------------------------
// android::AndroidRuntime vtable layout
struct
vtable_t
{
void
*
rtti
;
void
*
dtor
;
void
(
*
onVmCreated
)(
void
*
self
,
JNIEnv
*
env
);
void
(
*
onStarted
)(
void
*
self
);
void
(
*
onZygoteInit
)(
void
*
self
);
void
(
*
onExit
)(
void
*
self
,
int
code
);
};
// This method is a trampoline for hooking JNIEnv->RegisterNatives
DCL_HOOK_FUNC
(
void
,
onVmCreated
,
void
*
self
,
JNIEnv
*
env
)
{
LOGD
(
"hook: AppRuntime::onVmCreated
\n
"
);
// Restore the virtual table, we no longer need it
auto
*
new_table
=
*
reinterpret_cast
<
vtable_t
**>
(
self
);
*
reinterpret_cast
<
vtable_t
**>
(
self
)
=
gAppRuntimeVTable
;
delete
new_table
;
// Replace the function table in JNIEnv to hook RegisterNatives
old_functions
=
env
->
functions
;
new_functions
=
new
JNINativeInterface
();
memcpy
(
new_functions
,
env
->
functions
,
sizeof
(
*
new_functions
));
new_functions
->
RegisterNatives
=
new_env_RegisterNatives
;
env
->
functions
=
new_functions
;
old_onVmCreated
(
self
,
env
);
}
// This method is a trampoline for swizzling android::AppRuntime vtable
static
bool
swizzled
=
false
;
DCL_HOOK_FUNC
(
void
,
setArgv0
,
void
*
self
,
const
char
*
argv0
,
bool
setProcName
)
{
if
(
swizzled
)
{
old_setArgv0
(
self
,
argv0
,
setProcName
);
return
;
}
LOGD
(
"hook: AndroidRuntime::setArgv0
\n
"
);
// Swizzle C++ vtable to hook virtual function
gAppRuntimeVTable
=
*
reinterpret_cast
<
vtable_t
**>
(
self
);
old_onVmCreated
=
gAppRuntimeVTable
->
onVmCreated
;
auto
*
new_table
=
new
vtable_t
();
memcpy
(
new_table
,
gAppRuntimeVTable
,
sizeof
(
vtable_t
));
new_table
->
onVmCreated
=
&
new_onVmCreated
;
*
reinterpret_cast
<
vtable_t
**>
(
self
)
=
new_table
;
swizzled
=
true
;
old_setArgv0
(
self
,
argv0
,
setProcName
);
}
// -----------------------------------------------------------------
...
...
@@ -181,6 +262,13 @@ static void nativeSpecializeAppProcess_post(HookContext *ctx, JNIEnv *env, jclas
// -----------------------------------------------------------------
static
int
sigmask
(
int
how
,
int
signum
)
{
sigset_t
set
;
sigemptyset
(
&
set
);
sigaddset
(
&
set
,
signum
);
return
sigprocmask
(
how
,
&
set
,
nullptr
);
}
// Do our own fork before loading any 3rd party code
// First block SIGCHLD, unblock after original fork is done
#define PRE_FORK() \
...
...
@@ -210,6 +298,16 @@ static void nativeForkAndSpecialize_post(HookContext *ctx, JNIEnv *env, jclass c
// -----------------------------------------------------------------
static
void
nativeForkSystemServer_pre
(
HookContext
*
ctx
,
JNIEnv
*
env
,
jclass
clazz
)
{
if
(
env
->
functions
==
new_functions
)
{
// Restore JNIEnv
env
->
functions
=
old_functions
;
if
(
gClassRef
)
{
env
->
DeleteGlobalRef
(
gClassRef
);
gClassRef
=
nullptr
;
class_getName
=
nullptr
;
}
}
PRE_FORK
();
LOGD
(
"hook: %s
\n
"
,
__FUNCTION__
);
}
...
...
@@ -245,22 +343,50 @@ static int hook_register(const char *path, const char *symbol, void *new_func, v
return
0
;
}
template
<
class
T
>
static
inline
void
default_new
(
T
*&
p
)
{
p
=
new
T
();
}
#define XHOOK_REGISTER_SYM(PATH_REGEX, SYM, NAME) \
hook_register(PATH_REGEX, SYM, (void*) new_##NAME, (void **) &old_##NAME)
#define XHOOK_REGISTER(PATH_REGEX, NAME) \
hook_register(PATH_REGEX, #NAME, (void*) new_##NAME, (void **) &old_##NAME)
XHOOK_REGISTER_SYM(PATH_REGEX, #NAME, NAME)
#define ANDROID_RUNTIME ".*/libandroid_runtime.so$"
#define APP_PROCESS "^/system/bin/app_process.*"
void
hook_functions
()
{
#ifdef MAGISK_DEBUG
xhook_enable_debug
(
1
);
xhook_enable_sigsegv_protection
(
0
);
#endif
xhook_list
=
new
remove_pointer_t
<
decltype
(
xhook_list
)
>
(
);
jni_hook_list
=
new
remove_pointer_t
<
decltype
(
jni_hook_list
)
>
(
);
jni_method_map
=
new
remove_pointer_t
<
decltype
(
jni_method_map
)
>
(
);
default_new
(
xhook_list
);
default_new
(
jni_hook_list
);
default_new
(
jni_method_map
);
XHOOK_REGISTER
(
".*
\\
libandroid_runtime.so$"
,
jniRegisterNativeMethods
);
XHOOK_REGISTER
(
".*
\\
libandroid_runtime.so$"
,
fork
);
XHOOK_REGISTER
(
".*
\\
libandroid_runtime.so$"
,
selinux_android_setcontext
);
XHOOK_REGISTER
(
ANDROID_RUNTIME
,
fork
);
XHOOK_REGISTER
(
ANDROID_RUNTIME
,
selinux_android_setcontext
);
XHOOK_REGISTER
(
ANDROID_RUNTIME
,
jniRegisterNativeMethods
);
hook_refresh
();
// Remove unhooked methods
xhook_list
->
erase
(
std
::
remove_if
(
xhook_list
->
begin
(),
xhook_list
->
end
(),
[](
auto
&
t
)
{
return
*
std
::
get
<
2
>
(
t
)
==
nullptr
;}),
xhook_list
->
end
());
if
(
old_jniRegisterNativeMethods
==
nullptr
)
{
LOGD
(
"hook: jniRegisterNativeMethods not used
\n
"
);
// android::AndroidRuntime::setArgv0(const char *, bool)
XHOOK_REGISTER_SYM
(
APP_PROCESS
,
"_ZN7android14AndroidRuntime8setArgv0EPKcb"
,
setArgv0
);
hook_refresh
();
// We still need old_jniRegisterNativeMethods as other code uses it
// android::AndroidRuntime::registerNativeMethods(_JNIEnv*, const char *, const JNINativeMethod *, int)
constexpr
char
sig
[]
=
"_ZN7android14AndroidRuntime21registerNativeMethodsEP7_JNIEnvPKcPK15JNINativeMethodi"
;
*
(
void
**
)
&
old_jniRegisterNativeMethods
=
dlsym
(
RTLD_DEFAULT
,
sig
);
}
}
bool
unhook_functions
()
{
...
...
native/jni/inject/inject.hpp
View file @
a260e990
...
...
@@ -11,11 +11,11 @@
// Unmap all pages matching the name
void
unmap_all
(
const
char
*
name
);
// Get library name
and base address that contains the function
uintptr_t
get_function_
lib
(
uintptr_t
addr
,
char
*
lib
);
// Get library name
+ offset (from start of ELF), given function address
uintptr_t
get_function_
off
(
int
pid
,
uintptr_t
addr
,
char
*
lib
);
// Get
library base address with name
uintptr_t
get_
remote_lib
(
int
pid
,
const
char
*
lib
);
// Get
function address, given library name + offset
uintptr_t
get_
function_addr
(
int
pid
,
const
char
*
lib
,
uintptr_t
off
);
void
self_unload
();
void
hook_functions
();
...
...
native/jni/inject/memory.cpp
View file @
a260e990
...
...
@@ -5,7 +5,8 @@ namespace jni_hook {
// We know our minimum alignment is WORD size (size of pointer)
static
constexpr
size_t
ALIGN
=
sizeof
(
long
);
static
constexpr
size_t
CAPACITY
=
(
1
<<
24
);
// 4MB is more than enough
static
constexpr
size_t
CAPACITY
=
(
1
<<
22
);
// No need to be thread safe as the initial mmap always happens on the main thread
static
uint8_t
*
_area
=
nullptr
;
...
...
native/jni/inject/ptrace.cpp
View file @
a260e990
...
...
@@ -226,16 +226,15 @@ uintptr_t remote_call_abi(int pid, uintptr_t func_addr, int nargs, va_list va) {
uintptr_t
remote_call_vararg
(
int
pid
,
uintptr_t
addr
,
int
nargs
,
...)
{
char
lib_name
[
4096
];
auto
local
=
get_function_lib
(
addr
,
lib_name
);
if
(
local
==
0
)
auto
off
=
get_function_off
(
getpid
(),
addr
,
lib_name
);
if
(
off
==
0
)
return
0
;
auto
remote
=
get_
remote_lib
(
pid
,
lib_name
);
auto
remote
=
get_
function_addr
(
pid
,
lib_name
,
off
);
if
(
remote
==
0
)
return
0
;
addr
=
addr
-
local
+
remote
;
va_list
va
;
va_start
(
va
,
nargs
);
auto
result
=
remote_call_abi
(
pid
,
addr
,
nargs
,
va
);
auto
result
=
remote_call_abi
(
pid
,
remote
,
nargs
,
va
);
va_end
(
va
);
return
result
;
}
native/jni/inject/utils.cpp
View file @
a260e990
#include <cinttypes>
#include <utils.hpp>
#include "inject.hpp"
...
...
@@ -9,10 +10,11 @@ namespace {
struct
map_info
{
uintptr_t
start
;
uintptr_t
end
;
uintptr_t
off
;
int
perms
;
char
*
path
;
map_info
()
:
start
(
0
),
end
(
0
),
perms
(
0
),
path
(
nullptr
)
{}
map_info
()
:
start
(
0
),
end
(
0
),
off
(
0
),
perms
(
0
),
path
(
nullptr
)
{}
enum
{
EXEC
=
(
1
<<
0
),
...
...
@@ -30,27 +32,24 @@ static void parse_maps(int pid, Func fn) {
// format: start-end perms offset dev inode path
sprintf
(
file
,
"/proc/%d/maps"
,
pid
);
file_readline
(
true
,
file
,
[
=
](
string_view
l
)
->
bool
{
char
*
pos
=
(
char
*
)
l
.
data
();
char
*
line
=
(
char
*
)
l
.
data
();
map_info
info
;
char
perm
[
5
];
int
path_off
;
// Parse address hex strings
info
.
start
=
strtoul
(
pos
,
&
pos
,
16
);
info
.
end
=
strtoul
(
++
pos
,
&
pos
,
16
)
;
if
(
sscanf
(
line
,
"%"
PRIxPTR
"-%"
PRIxPTR
" %4s %"
PRIxPTR
" %*x:%*x %*d %n%*s"
,
&
info
.
start
,
&
info
.
end
,
perm
,
&
info
.
off
,
&
path_off
)
!=
4
)
return
true
;
// Parse permissions
if
(
*
(
++
pos
)
!=
'-'
)
if
(
perm
[
0
]
!=
'-'
)
info
.
perms
|=
map_info
::
READ
;
if
(
*
(
++
pos
)
!=
'-'
)
if
(
perm
[
1
]
!=
'-'
)
info
.
perms
|=
map_info
::
WRITE
;
if
(
*
(
++
pos
)
!=
'-'
)
if
(
perm
[
2
]
!=
'-'
)
info
.
perms
|=
map_info
::
EXEC
;
pos
+=
3
;
// Skip everything except path
int
path_off
;
sscanf
(
pos
,
"%*s %*s %*s %n%*s"
,
&
path_off
);
pos
+=
path_off
;
info
.
path
=
pos
;
info
.
path
=
line
+
path_off
;
return
fn
(
info
);
});
...
...
@@ -74,28 +73,28 @@ void unmap_all(const char *name) {
}
}
uintptr_t
get_function_
lib
(
uintptr_t
addr
,
char
*
lib
)
{
uintptr_t
base
=
0
;
parse_maps
(
getpid
(),
[
=
,
&
base
](
map_info
&
info
)
->
bool
{
uintptr_t
get_function_
off
(
int
pid
,
uintptr_t
addr
,
char
*
lib
)
{
uintptr_t
off
=
0
;
parse_maps
(
pid
,
[
=
,
&
off
](
map_info
&
info
)
->
bool
{
if
(
addr
>=
info
.
start
&&
addr
<
info
.
end
)
{
if
(
lib
)
strcpy
(
lib
,
info
.
path
);
base
=
info
.
start
;
off
=
addr
-
info
.
start
+
info
.
off
;
return
false
;
}
return
true
;
});
return
base
;
return
off
;
}
uintptr_t
get_
remote_lib
(
int
pid
,
const
char
*
lib
)
{
uintptr_t
base
=
0
;
parse_maps
(
pid
,
[
=
,
&
base
](
map_info
&
info
)
->
bool
{
uintptr_t
get_
function_addr
(
int
pid
,
const
char
*
lib
,
uintptr_t
off
)
{
uintptr_t
addr
=
0
;
parse_maps
(
pid
,
[
=
,
&
addr
](
map_info
&
info
)
->
bool
{
if
(
strcmp
(
info
.
path
,
lib
)
==
0
&&
(
info
.
perms
&
map_info
::
EXEC
))
{
base
=
info
.
start
;
addr
=
info
.
start
-
info
.
off
+
off
;
return
false
;
}
return
true
;
});
return
base
;
return
addr
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment