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
54e3f199
Commit
54e3f199
authored
Dec 13, 2021
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support RootService on stub APKs
parent
edcf9f1b
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
130 additions
and
68 deletions
+130
-68
DynAPK.java
app/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java
+23
-22
App.kt
app/src/main/java/com/topjohnwu/magisk/core/App.kt
+12
-7
RootRegistry.kt
...main/java/com/topjohnwu/magisk/core/utils/RootRegistry.kt
+9
-1
proguard-rules.pro
stub/proguard-rules.pro
+1
-0
DelegateApplication.java
...c/main/java/com/topjohnwu/magisk/DelegateApplication.java
+8
-1
DelegateComponentFactory.java
...n/java/com/topjohnwu/magisk/DelegateComponentFactory.java
+1
-1
DelegateRootService.java
...c/main/java/com/topjohnwu/magisk/DelegateRootService.java
+36
-0
DownloadActivity.java
.../src/main/java/com/topjohnwu/magisk/DownloadActivity.java
+1
-1
DynLoad.java
stub/src/main/java/com/topjohnwu/magisk/DynLoad.java
+39
-35
No files found.
app/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java
View file @
54e3f199
...
@@ -13,11 +13,6 @@ import io.michaelrocks.paranoid.Obfuscate;
...
@@ -13,11 +13,6 @@ import io.michaelrocks.paranoid.Obfuscate;
@Obfuscate
@Obfuscate
public
class
DynAPK
{
public
class
DynAPK
{
// Indices of the object array
private
static
final
int
STUB_VERSION_ENTRY
=
0
;
private
static
final
int
CLASS_COMPONENT_MAP
=
1
;
private
static
File
dynDir
;
private
static
File
dynDir
;
private
static
Method
addAssetPath
;
private
static
Method
addAssetPath
;
...
@@ -41,21 +36,6 @@ public class DynAPK {
...
@@ -41,21 +36,6 @@ public class DynAPK {
return
new
File
(
getDynDir
(
c
),
"update.apk"
);
return
new
File
(
getDynDir
(
c
),
"update.apk"
);
}
}
public
static
Data
load
(
Object
o
)
{
Object
[]
arr
=
(
Object
[])
o
;
Data
data
=
new
Data
();
data
.
version
=
(
int
)
arr
[
STUB_VERSION_ENTRY
];
data
.
classToComponent
=
(
Map
<
String
,
String
>)
arr
[
CLASS_COMPONENT_MAP
];
return
data
;
}
public
static
Object
pack
(
Data
data
)
{
Object
[]
arr
=
new
Object
[
2
];
arr
[
STUB_VERSION_ENTRY
]
=
data
.
version
;
arr
[
CLASS_COMPONENT_MAP
]
=
data
.
classToComponent
;
return
arr
;
}
public
static
void
addAssetPath
(
AssetManager
asset
,
String
path
)
{
public
static
void
addAssetPath
(
AssetManager
asset
,
String
path
)
{
try
{
try
{
if
(
addAssetPath
==
null
)
if
(
addAssetPath
==
null
)
...
@@ -65,7 +45,28 @@ public class DynAPK {
...
@@ -65,7 +45,28 @@ public class DynAPK {
}
}
public
static
class
Data
{
public
static
class
Data
{
public
int
version
;
// Indices of the object array
public
Map
<
String
,
String
>
classToComponent
;
private
static
final
int
STUB_VERSION
=
0
;
private
static
final
int
CLASS_COMPONENT_MAP
=
1
;
private
static
final
int
ROOT_SERVICE
=
2
;
private
static
final
int
ARR_SIZE
=
3
;
private
final
Object
[]
arr
;
public
Data
()
{
arr
=
new
Object
[
ARR_SIZE
];
}
public
Data
(
Object
o
)
{
arr
=
(
Object
[])
o
;
}
public
Object
getObject
()
{
return
arr
;
}
public
int
getVersion
()
{
return
(
int
)
arr
[
STUB_VERSION
];
}
public
void
setVersion
(
int
version
)
{
arr
[
STUB_VERSION
]
=
version
;
}
public
Map
<
String
,
String
>
getClassToComponent
()
{
// noinspection unchecked
return
(
Map
<
String
,
String
>)
arr
[
CLASS_COMPONENT_MAP
];
}
public
void
setClassToComponent
(
Map
<
String
,
String
>
map
)
{
arr
[
CLASS_COMPONENT_MAP
]
=
map
;
}
public
Class
<?>
getRootService
()
{
return
(
Class
<?>)
arr
[
ROOT_SERVICE
];
}
public
void
setRootService
(
Class
<?>
service
)
{
arr
[
ROOT_SERVICE
]
=
service
;
}
}
}
}
}
app/src/main/java/com/topjohnwu/magisk/core/App.kt
View file @
54e3f199
...
@@ -22,16 +22,15 @@ import kotlin.system.exitProcess
...
@@ -22,16 +22,15 @@ import kotlin.system.exitProcess
open
class
App
()
:
Application
()
{
open
class
App
()
:
Application
()
{
constructor
(
o
:
Any
)
:
this
()
{
constructor
(
o
:
Any
)
:
this
()
{
Info
.
stub
=
DynAPK
.
load
(
o
)
val
data
=
DynAPK
.
Data
(
o
)
// Add the root service name mapping
data
.
classToComponent
[
RootRegistry
::
class
.
java
.
name
]
=
data
.
rootService
.
name
// Send back the actual root service class
data
.
rootService
=
RootRegistry
::
class
.
java
Info
.
stub
=
data
}
}
init
{
init
{
Shell
.
setDefaultBuilder
(
Shell
.
Builder
.
create
()
.
setFlags
(
Shell
.
FLAG_MOUNT_MASTER
)
.
setInitializers
(
ShellInit
::
class
.
java
)
.
setTimeout
(
2
))
Shell
.
EXECUTOR
=
IODispatcherExecutor
()
// Always log full stack trace with Timber
// Always log full stack trace with Timber
Timber
.
plant
(
Timber
.
DebugTree
())
Timber
.
plant
(
Timber
.
DebugTree
())
Thread
.
setDefaultUncaughtExceptionHandler
{
_
,
e
->
Thread
.
setDefaultUncaughtExceptionHandler
{
_
,
e
->
...
@@ -41,6 +40,12 @@ open class App() : Application() {
...
@@ -41,6 +40,12 @@ open class App() : Application() {
}
}
override
fun
attachBaseContext
(
base
:
Context
)
{
override
fun
attachBaseContext
(
base
:
Context
)
{
Shell
.
setDefaultBuilder
(
Shell
.
Builder
.
create
()
.
setFlags
(
Shell
.
FLAG_MOUNT_MASTER
)
.
setInitializers
(
ShellInit
::
class
.
java
)
.
setTimeout
(
2
))
Shell
.
EXECUTOR
=
IODispatcherExecutor
()
// Some context magic
// Some context magic
val
app
:
Application
val
app
:
Application
val
impl
:
Context
val
impl
:
Context
...
...
app/src/main/java/com/topjohnwu/magisk/core/utils/RootRegistry.kt
View file @
54e3f199
...
@@ -10,7 +10,11 @@ import timber.log.Timber
...
@@ -10,7 +10,11 @@ import timber.log.Timber
import
java.util.concurrent.CountDownLatch
import
java.util.concurrent.CountDownLatch
import
kotlin.system.exitProcess
import
kotlin.system.exitProcess
class
RootRegistry
:
RootService
()
{
class
RootRegistry
(
stub
:
Any
?)
:
RootService
()
{
constructor
()
:
this
(
null
)
private
val
className
:
String
?
=
stub
?.
javaClass
?.
name
init
{
init
{
// Always log full stack trace with Timber
// Always log full stack trace with Timber
...
@@ -26,6 +30,10 @@ class RootRegistry : RootService() {
...
@@ -26,6 +30,10 @@ class RootRegistry : RootService() {
return
Binder
()
return
Binder
()
}
}
override
fun
getComponentName
():
ComponentName
{
return
ComponentName
(
packageName
,
className
?:
javaClass
.
name
)
}
// TODO: PLACEHOLDER
// TODO: PLACEHOLDER
object
Connection
:
CountDownLatch
(
1
),
ServiceConnection
{
object
Connection
:
CountDownLatch
(
1
),
ServiceConnection
{
override
fun
onServiceConnected
(
name
:
ComponentName
,
service
:
IBinder
)
{
override
fun
onServiceConnected
(
name
:
ComponentName
,
service
:
IBinder
)
{
...
...
stub/proguard-rules.pro
View file @
54e3f199
...
@@ -30,3 +30,4 @@
...
@@ -30,3 +30,4 @@
-
keepclassmembers
class
com
.
topjohnwu
.
magisk
.
dummy
.
*
{
<
init
>
();
}
-
keepclassmembers
class
com
.
topjohnwu
.
magisk
.
dummy
.
*
{
<
init
>
();
}
-
keepclassmembers
class
com
.
topjohnwu
.
magisk
.
DownloadActivity
{
<
init
>
();
}
-
keepclassmembers
class
com
.
topjohnwu
.
magisk
.
DownloadActivity
{
<
init
>
();
}
-
keepclassmembers
class
com
.
topjohnwu
.
magisk
.
FileProvider
{
<
init
>
();
}
-
keepclassmembers
class
com
.
topjohnwu
.
magisk
.
FileProvider
{
<
init
>
();
}
-
keepclassmembers
class
com
.
topjohnwu
.
magisk
.
DelegateRootService
{
<
init
>
();
}
stub/src/main/java/com/topjohnwu/magisk/DelegateApplication.java
View file @
54e3f199
...
@@ -18,7 +18,7 @@ public class DelegateApplication extends Application {
...
@@ -18,7 +18,7 @@ public class DelegateApplication extends Application {
protected
void
attachBaseContext
(
Context
base
)
{
protected
void
attachBaseContext
(
Context
base
)
{
super
.
attachBaseContext
(
base
);
super
.
attachBaseContext
(
base
);
receiver
=
InjectAPK
.
setup
(
this
);
receiver
=
DynLoad
.
setup
(
this
);
if
(
receiver
!=
null
)
try
{
if
(
receiver
!=
null
)
try
{
// Call attachBaseContext without ContextImpl to show it is being wrapped
// Call attachBaseContext without ContextImpl to show it is being wrapped
Method
m
=
ContextWrapper
.
class
.
getDeclaredMethod
(
"attachBaseContext"
,
Context
.
class
);
Method
m
=
ContextWrapper
.
class
.
getDeclaredMethod
(
"attachBaseContext"
,
Context
.
class
);
...
@@ -27,6 +27,13 @@ public class DelegateApplication extends Application {
...
@@ -27,6 +27,13 @@ public class DelegateApplication extends Application {
}
catch
(
Exception
ignored
)
{
/* Impossible */
}
}
catch
(
Exception
ignored
)
{
/* Impossible */
}
}
}
@Override
public
void
onCreate
()
{
super
.
onCreate
();
if
(
receiver
!=
null
)
receiver
.
onCreate
();
}
@Override
@Override
public
void
onConfigurationChanged
(
Configuration
newConfig
)
{
public
void
onConfigurationChanged
(
Configuration
newConfig
)
{
super
.
onConfigurationChanged
(
newConfig
);
super
.
onConfigurationChanged
(
newConfig
);
...
...
stub/src/main/java/com/topjohnwu/magisk/DelegateComponentFactory.java
View file @
54e3f199
...
@@ -16,7 +16,7 @@ public class DelegateComponentFactory extends AppComponentFactory {
...
@@ -16,7 +16,7 @@ public class DelegateComponentFactory extends AppComponentFactory {
AppComponentFactory
receiver
;
AppComponentFactory
receiver
;
public
DelegateComponentFactory
()
{
public
DelegateComponentFactory
()
{
InjectAPK
.
componentFactory
=
this
;
DynLoad
.
componentFactory
=
this
;
}
}
@Override
@Override
...
...
stub/src/main/java/com/topjohnwu/magisk/DelegateRootService.java
0 → 100644
View file @
54e3f199
package
com
.
topjohnwu
.
magisk
;
import
android.content.Context
;
import
android.content.ContextWrapper
;
import
android.util.Log
;
import
java.lang.reflect.Constructor
;
import
java.lang.reflect.Method
;
import
io.michaelrocks.paranoid.Obfuscate
;
@Obfuscate
public
class
DelegateRootService
extends
ContextWrapper
{
public
DelegateRootService
()
{
super
(
null
);
}
@Override
protected
void
attachBaseContext
(
Context
base
)
{
if
(
DynLoad
.
inject
(
base
)
==
null
)
return
;
// Create the actual RootService and call its attachBaseContext
try
{
Constructor
<?>
ctor
=
DynLoad
.
apkData
.
getRootService
().
getConstructor
(
Object
.
class
);
ctor
.
setAccessible
(
true
);
Object
service
=
ctor
.
newInstance
(
this
);
Method
m
=
ContextWrapper
.
class
.
getDeclaredMethod
(
"attachBaseContext"
,
Context
.
class
);
m
.
setAccessible
(
true
);
m
.
invoke
(
service
,
base
);
}
catch
(
Exception
e
)
{
Log
.
e
(
DelegateRootService
.
class
.
getSimpleName
(),
""
,
e
);
}
}
}
stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java
View file @
54e3f199
...
@@ -115,7 +115,7 @@ public class DownloadActivity extends Activity {
...
@@ -115,7 +115,7 @@ public class DownloadActivity extends Activity {
File
apk
=
dynLoad
?
DynAPK
.
current
(
this
)
:
new
File
(
getCacheDir
(),
"manager.apk"
);
File
apk
=
dynLoad
?
DynAPK
.
current
(
this
)
:
new
File
(
getCacheDir
(),
"manager.apk"
);
request
(
apkLink
).
setExecutor
(
AsyncTask
.
THREAD_POOL_EXECUTOR
).
getAsFile
(
apk
,
file
->
{
request
(
apkLink
).
setExecutor
(
AsyncTask
.
THREAD_POOL_EXECUTOR
).
getAsFile
(
apk
,
file
->
{
if
(
dynLoad
)
{
if
(
dynLoad
)
{
InjectAPK
.
setup
(
this
);
DynLoad
.
setup
(
this
);
runOnUiThread
(()
->
{
runOnUiThread
(()
->
{
dialog
.
dismiss
();
dialog
.
dismiss
();
Toast
.
makeText
(
themed
,
relaunch_app
,
Toast
.
LENGTH_LONG
).
show
();
Toast
.
makeText
(
themed
,
relaunch_app
,
Toast
.
LENGTH_LONG
).
show
();
...
...
stub/src/main/java/com/topjohnwu/magisk/
InjectAPK
.java
→
stub/src/main/java/com/topjohnwu/magisk/
DynLoad
.java
View file @
54e3f199
...
@@ -23,13 +23,10 @@ import java.lang.reflect.Field;
...
@@ -23,13 +23,10 @@ import java.lang.reflect.Field;
import
io.michaelrocks.paranoid.Obfuscate
;
import
io.michaelrocks.paranoid.Obfuscate
;
@Obfuscate
@Obfuscate
public
class
InjectAPK
{
public
class
DynLoad
{
static
Object
componentFactory
;
static
Object
componentFactory
;
static
final
DynAPK
.
Data
apkData
=
createApkData
();
private
static
DelegateComponentFactory
getComponentFactory
()
{
return
(
DelegateComponentFactory
)
componentFactory
;
}
private
static
void
copy
(
InputStream
src
,
OutputStream
dest
)
throws
IOException
{
private
static
void
copy
(
InputStream
src
,
OutputStream
dest
)
throws
IOException
{
try
(
InputStream
s
=
src
)
{
try
(
InputStream
s
=
src
)
{
...
@@ -42,13 +39,9 @@ public class InjectAPK {
...
@@ -42,13 +39,9 @@ public class InjectAPK {
}
}
}
}
@SuppressWarnings
(
"ResultOfMethodCallIgnored"
)
// Dynamically load APK, inject ClassLoader into ContextImpl, then
static
Application
setup
(
Context
context
)
{
// create the actual Application instance from the loaded APK
// Get ContextImpl
static
Application
inject
(
Context
context
)
{
while
(
context
instanceof
ContextWrapper
)
{
context
=
((
ContextWrapper
)
context
).
getBaseContext
();
}
File
apk
=
DynAPK
.
current
(
context
);
File
apk
=
DynAPK
.
current
(
context
);
File
update
=
DynAPK
.
update
(
context
);
File
update
=
DynAPK
.
update
(
context
);
...
@@ -64,7 +57,7 @@ public class InjectAPK {
...
@@ -64,7 +57,7 @@ public class InjectAPK {
try
{
try
{
copy
(
new
FileInputStream
(
external
),
new
FileOutputStream
(
apk
));
copy
(
new
FileInputStream
(
external
),
new
FileOutputStream
(
apk
));
}
catch
(
IOException
e
)
{
}
catch
(
IOException
e
)
{
Log
.
e
(
InjectAPK
.
class
.
getSimpleName
(),
""
,
e
);
Log
.
e
(
DynLoad
.
class
.
getSimpleName
(),
""
,
e
);
apk
.
delete
();
apk
.
delete
();
}
finally
{
}
finally
{
external
.
delete
();
external
.
delete
();
...
@@ -84,7 +77,7 @@ public class InjectAPK {
...
@@ -84,7 +77,7 @@ public class InjectAPK {
copy
(
src
,
new
FileOutputStream
(
apk
));
copy
(
src
,
new
FileOutputStream
(
apk
));
}
}
}
catch
(
IOException
e
)
{
}
catch
(
IOException
e
)
{
Log
.
e
(
InjectAPK
.
class
.
getSimpleName
(),
""
,
e
);
Log
.
e
(
DynLoad
.
class
.
getSimpleName
(),
""
,
e
);
apk
.
delete
();
apk
.
delete
();
}
}
}
}
...
@@ -95,21 +88,30 @@ public class InjectAPK {
...
@@ -95,21 +88,30 @@ public class InjectAPK {
PackageInfo
pkgInfo
=
pm
.
getPackageArchiveInfo
(
apk
.
getPath
(),
0
);
PackageInfo
pkgInfo
=
pm
.
getPackageArchiveInfo
(
apk
.
getPath
(),
0
);
try
{
try
{
return
createApp
(
context
,
cl
,
pkgInfo
.
applicationInfo
);
return
createApp
(
context
,
cl
,
pkgInfo
.
applicationInfo
);
}
catch
(
Exception
e
)
{
}
catch
(
ReflectiveOperation
Exception
e
)
{
Log
.
e
(
InjectAPK
.
class
.
getSimpleName
(),
""
,
e
);
Log
.
e
(
DynLoad
.
class
.
getSimpleName
(),
""
,
e
);
apk
.
delete
();
apk
.
delete
();
}
}
// fallthrough
}
return
null
;
}
// Inject and create Application, or setup redirections for the current app
static
Application
setup
(
Context
context
)
{
Application
app
=
inject
(
context
);
if
(
app
!=
null
)
{
return
app
;
}
}
ClassLoader
cl
=
new
RedirectClassLoader
();
ClassLoader
cl
=
new
RedirectClassLoader
();
try
{
try
{
setClassLoader
(
context
,
cl
);
setClassLoader
(
context
,
cl
);
if
(
Build
.
VERSION
.
SDK_INT
>=
28
)
{
if
(
Build
.
VERSION
.
SDK_INT
>=
28
)
{
getComponentFactory
(
).
loader
=
cl
;
((
DelegateComponentFactory
)
componentFactory
).
loader
=
cl
;
}
}
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
Log
.
e
(
InjectAPK
.
class
.
getSimpleName
(),
""
,
e
);
Log
.
e
(
DynLoad
.
class
.
getSimpleName
(),
""
,
e
);
}
}
return
null
;
return
null
;
...
@@ -120,40 +122,42 @@ public class InjectAPK {
...
@@ -120,40 +122,42 @@ public class InjectAPK {
// Create the receiver Application
// Create the receiver Application
Object
app
=
cl
.
loadClass
(
info
.
className
)
Object
app
=
cl
.
loadClass
(
info
.
className
)
.
getConstructor
(
Object
.
class
)
.
getConstructor
(
Object
.
class
)
.
newInstance
(
DynAPK
.
pack
(
dynData
()
));
.
newInstance
(
apkData
.
getObject
(
));
// Create the receiver component factory
// Create the receiver component factory
Object
factory
=
null
;
if
(
Build
.
VERSION
.
SDK_INT
>=
28
&&
componentFactory
!=
null
)
{
if
(
Build
.
VERSION
.
SDK_INT
>=
28
)
{
Object
factory
=
cl
.
loadClass
(
info
.
appComponentFactory
).
newInstance
();
factory
=
cl
.
loadClass
(
info
.
appComponentFactory
).
newInstance
();
DelegateComponentFactory
delegate
=
(
DelegateComponentFactory
)
componentFactory
;
delegate
.
loader
=
cl
;
delegate
.
receiver
=
(
AppComponentFactory
)
factory
;
}
}
setClassLoader
(
context
,
cl
);
setClassLoader
(
context
,
cl
);
// Finally, set variables
if
(
Build
.
VERSION
.
SDK_INT
>=
28
)
{
getComponentFactory
().
loader
=
cl
;
getComponentFactory
().
receiver
=
(
AppComponentFactory
)
factory
;
}
return
(
Application
)
app
;
return
(
Application
)
app
;
}
}
// Replace LoadedApk mClassLoader
// Replace LoadedApk mClassLoader
private
static
void
setClassLoader
(
Context
impl
,
ClassLoader
cl
)
private
static
void
setClassLoader
(
Context
context
,
ClassLoader
cl
)
throws
NoSuchFieldException
,
IllegalAccessException
{
throws
NoSuchFieldException
,
IllegalAccessException
{
Field
mInfo
=
impl
.
getClass
().
getDeclaredField
(
"mPackageInfo"
);
// Get ContextImpl
while
(
context
instanceof
ContextWrapper
)
{
context
=
((
ContextWrapper
)
context
).
getBaseContext
();
}
Field
mInfo
=
context
.
getClass
().
getDeclaredField
(
"mPackageInfo"
);
mInfo
.
setAccessible
(
true
);
mInfo
.
setAccessible
(
true
);
Object
loadedApk
=
mInfo
.
get
(
impl
);
Object
loadedApk
=
mInfo
.
get
(
context
);
Field
mcl
=
loadedApk
.
getClass
().
getDeclaredField
(
"mClassLoader"
);
Field
mcl
=
loadedApk
.
getClass
().
getDeclaredField
(
"mClassLoader"
);
mcl
.
setAccessible
(
true
);
mcl
.
setAccessible
(
true
);
mcl
.
set
(
loadedApk
,
cl
);
mcl
.
set
(
loadedApk
,
cl
);
}
}
private
static
DynAPK
.
Data
dyn
Data
()
{
private
static
DynAPK
.
Data
createApk
Data
()
{
DynAPK
.
Data
data
=
new
DynAPK
.
Data
();
DynAPK
.
Data
data
=
new
DynAPK
.
Data
();
data
.
version
=
BuildConfig
.
STUB_VERSION
;
data
.
setVersion
(
BuildConfig
.
STUB_VERSION
);
data
.
classToComponent
=
Mapping
.
inverseMap
;
data
.
setClassToComponent
(
Mapping
.
inverseMap
);
data
.
setRootService
(
DelegateRootService
.
class
);
return
data
;
return
data
;
}
}
}
}
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