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;
@Obfuscate
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
Method
addAssetPath
;
...
...
@@ -41,21 +36,6 @@ public class DynAPK {
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
)
{
try
{
if
(
addAssetPath
==
null
)
...
...
@@ -65,7 +45,28 @@ public class DynAPK {
}
public
static
class
Data
{
public
int
version
;
public
Map
<
String
,
String
>
classToComponent
;
// Indices of the object array
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
open
class
App
()
:
Application
()
{
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
{
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
Timber
.
plant
(
Timber
.
DebugTree
())
Thread
.
setDefaultUncaughtExceptionHandler
{
_
,
e
->
...
...
@@ -41,6 +40,12 @@ open class App() : Application() {
}
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
val
app
:
Application
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
import
java.util.concurrent.CountDownLatch
import
kotlin.system.exitProcess
class
RootRegistry
:
RootService
()
{
class
RootRegistry
(
stub
:
Any
?)
:
RootService
()
{
constructor
()
:
this
(
null
)
private
val
className
:
String
?
=
stub
?.
javaClass
?.
name
init
{
// Always log full stack trace with Timber
...
...
@@ -26,6 +30,10 @@ class RootRegistry : RootService() {
return
Binder
()
}
override
fun
getComponentName
():
ComponentName
{
return
ComponentName
(
packageName
,
className
?:
javaClass
.
name
)
}
// TODO: PLACEHOLDER
object
Connection
:
CountDownLatch
(
1
),
ServiceConnection
{
override
fun
onServiceConnected
(
name
:
ComponentName
,
service
:
IBinder
)
{
...
...
stub/proguard-rules.pro
View file @
54e3f199
...
...
@@ -30,3 +30,4 @@
-
keepclassmembers
class
com
.
topjohnwu
.
magisk
.
dummy
.
*
{
<
init
>
();
}
-
keepclassmembers
class
com
.
topjohnwu
.
magisk
.
DownloadActivity
{
<
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 {
protected
void
attachBaseContext
(
Context
base
)
{
super
.
attachBaseContext
(
base
);
receiver
=
InjectAPK
.
setup
(
this
);
receiver
=
DynLoad
.
setup
(
this
);
if
(
receiver
!=
null
)
try
{
// Call attachBaseContext without ContextImpl to show it is being wrapped
Method
m
=
ContextWrapper
.
class
.
getDeclaredMethod
(
"attachBaseContext"
,
Context
.
class
);
...
...
@@ -27,6 +27,13 @@ public class DelegateApplication extends Application {
}
catch
(
Exception
ignored
)
{
/* Impossible */
}
}
@Override
public
void
onCreate
()
{
super
.
onCreate
();
if
(
receiver
!=
null
)
receiver
.
onCreate
();
}
@Override
public
void
onConfigurationChanged
(
Configuration
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 {
AppComponentFactory
receiver
;
public
DelegateComponentFactory
()
{
InjectAPK
.
componentFactory
=
this
;
DynLoad
.
componentFactory
=
this
;
}
@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 {
File
apk
=
dynLoad
?
DynAPK
.
current
(
this
)
:
new
File
(
getCacheDir
(),
"manager.apk"
);
request
(
apkLink
).
setExecutor
(
AsyncTask
.
THREAD_POOL_EXECUTOR
).
getAsFile
(
apk
,
file
->
{
if
(
dynLoad
)
{
InjectAPK
.
setup
(
this
);
DynLoad
.
setup
(
this
);
runOnUiThread
(()
->
{
dialog
.
dismiss
();
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;
import
io.michaelrocks.paranoid.Obfuscate
;
@Obfuscate
public
class
InjectAPK
{
public
class
DynLoad
{
static
Object
componentFactory
;
private
static
DelegateComponentFactory
getComponentFactory
()
{
return
(
DelegateComponentFactory
)
componentFactory
;
}
static
final
DynAPK
.
Data
apkData
=
createApkData
();
private
static
void
copy
(
InputStream
src
,
OutputStream
dest
)
throws
IOException
{
try
(
InputStream
s
=
src
)
{
...
...
@@ -42,13 +39,9 @@ public class InjectAPK {
}
}
@SuppressWarnings
(
"ResultOfMethodCallIgnored"
)
static
Application
setup
(
Context
context
)
{
// Get ContextImpl
while
(
context
instanceof
ContextWrapper
)
{
context
=
((
ContextWrapper
)
context
).
getBaseContext
();
}
// Dynamically load APK, inject ClassLoader into ContextImpl, then
// create the actual Application instance from the loaded APK
static
Application
inject
(
Context
context
)
{
File
apk
=
DynAPK
.
current
(
context
);
File
update
=
DynAPK
.
update
(
context
);
...
...
@@ -64,7 +57,7 @@ public class InjectAPK {
try
{
copy
(
new
FileInputStream
(
external
),
new
FileOutputStream
(
apk
));
}
catch
(
IOException
e
)
{
Log
.
e
(
InjectAPK
.
class
.
getSimpleName
(),
""
,
e
);
Log
.
e
(
DynLoad
.
class
.
getSimpleName
(),
""
,
e
);
apk
.
delete
();
}
finally
{
external
.
delete
();
...
...
@@ -84,7 +77,7 @@ public class InjectAPK {
copy
(
src
,
new
FileOutputStream
(
apk
));
}
}
catch
(
IOException
e
)
{
Log
.
e
(
InjectAPK
.
class
.
getSimpleName
(),
""
,
e
);
Log
.
e
(
DynLoad
.
class
.
getSimpleName
(),
""
,
e
);
apk
.
delete
();
}
}
...
...
@@ -95,21 +88,30 @@ public class InjectAPK {
PackageInfo
pkgInfo
=
pm
.
getPackageArchiveInfo
(
apk
.
getPath
(),
0
);
try
{
return
createApp
(
context
,
cl
,
pkgInfo
.
applicationInfo
);
}
catch
(
Exception
e
)
{
Log
.
e
(
InjectAPK
.
class
.
getSimpleName
(),
""
,
e
);
}
catch
(
ReflectiveOperation
Exception
e
)
{
Log
.
e
(
DynLoad
.
class
.
getSimpleName
(),
""
,
e
);
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
();
try
{
setClassLoader
(
context
,
cl
);
if
(
Build
.
VERSION
.
SDK_INT
>=
28
)
{
getComponentFactory
(
).
loader
=
cl
;
((
DelegateComponentFactory
)
componentFactory
).
loader
=
cl
;
}
}
catch
(
Exception
e
)
{
Log
.
e
(
InjectAPK
.
class
.
getSimpleName
(),
""
,
e
);
Log
.
e
(
DynLoad
.
class
.
getSimpleName
(),
""
,
e
);
}
return
null
;
...
...
@@ -120,40 +122,42 @@ public class InjectAPK {
// Create the receiver Application
Object
app
=
cl
.
loadClass
(
info
.
className
)
.
getConstructor
(
Object
.
class
)
.
newInstance
(
DynAPK
.
pack
(
dynData
()
));
.
newInstance
(
apkData
.
getObject
(
));
// Create the receiver component factory
Object
factory
=
null
;
if
(
Build
.
VERSION
.
SDK_INT
>=
28
)
{
factory
=
cl
.
loadClass
(
info
.
appComponentFactory
).
newInstance
();
if
(
Build
.
VERSION
.
SDK_INT
>=
28
&&
componentFactory
!=
null
)
{
Object
factory
=
cl
.
loadClass
(
info
.
appComponentFactory
).
newInstance
();
DelegateComponentFactory
delegate
=
(
DelegateComponentFactory
)
componentFactory
;
delegate
.
loader
=
cl
;
delegate
.
receiver
=
(
AppComponentFactory
)
factory
;
}
setClassLoader
(
context
,
cl
);
// Finally, set variables
if
(
Build
.
VERSION
.
SDK_INT
>=
28
)
{
getComponentFactory
().
loader
=
cl
;
getComponentFactory
().
receiver
=
(
AppComponentFactory
)
factory
;
}
return
(
Application
)
app
;
}
// Replace LoadedApk mClassLoader
private
static
void
setClassLoader
(
Context
impl
,
ClassLoader
cl
)
private
static
void
setClassLoader
(
Context
context
,
ClassLoader
cl
)
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
);
Object
loadedApk
=
mInfo
.
get
(
impl
);
Object
loadedApk
=
mInfo
.
get
(
context
);
Field
mcl
=
loadedApk
.
getClass
().
getDeclaredField
(
"mClassLoader"
);
mcl
.
setAccessible
(
true
);
mcl
.
set
(
loadedApk
,
cl
);
}
private
static
DynAPK
.
Data
dyn
Data
()
{
private
static
DynAPK
.
Data
createApk
Data
()
{
DynAPK
.
Data
data
=
new
DynAPK
.
Data
();
data
.
version
=
BuildConfig
.
STUB_VERSION
;
data
.
classToComponent
=
Mapping
.
inverseMap
;
data
.
setVersion
(
BuildConfig
.
STUB_VERSION
);
data
.
setClassToComponent
(
Mapping
.
inverseMap
);
data
.
setRootService
(
DelegateRootService
.
class
);
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