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
c09b4dab
Commit
c09b4dab
authored
Feb 13, 2022
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Generate class mapping at runtime
parent
a4aa4a91
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
200 additions
and
126 deletions
+200
-126
Codegen.kt
buildSrc/src/main/java/Codegen.kt
+34
-83
ClassLoaders.java
stub/src/main/java/com/topjohnwu/magisk/ClassLoaders.java
+9
-3
DelegateComponentFactory.java
...n/java/com/topjohnwu/magisk/DelegateComponentFactory.java
+16
-7
DownloadActivity.java
.../src/main/java/com/topjohnwu/magisk/DownloadActivity.java
+8
-0
DynLoad.java
stub/src/main/java/com/topjohnwu/magisk/DynLoad.java
+121
-7
DummyService.java
...rc/main/java/com/topjohnwu/magisk/dummy/DummyService.java
+12
-0
Mapping.java
stub/template/Mapping.java
+0
-26
No files found.
buildSrc/src/main/java/Codegen.kt
View file @
c09b4dab
...
...
@@ -74,13 +74,6 @@ fun genKeyData(keysDir: File, outSrc: File) {
}
fun
genStubManifest
(
srcDir
:
File
,
outDir
:
File
):
String
{
class
Component
(
val
real
:
String
,
val
stub
:
String
,
val
xml
:
String
,
val
genClass
:
Boolean
=
false
)
outDir
.
deleteRecursively
()
val
mainPkgDir
=
File
(
outDir
,
"com/topjohnwu/magisk"
)
...
...
@@ -88,25 +81,9 @@ fun genStubManifest(srcDir: File, outDir: File): String {
fun
String
.
ind
(
level
:
Int
)
=
replaceIndentByMargin
(
" "
.
repeat
(
level
))
val
cmpList
=
mutableListOf
<
Component
>()
cmpList
.
add
(
Component
(
"androidx.core.app.CoreComponentFactory"
,
"DelegateComponentFactory"
,
""
,
true
))
cmpList
.
add
(
Component
(
"com.topjohnwu.magisk.core.App"
,
"DelegateApplication"
,
""
,
true
))
cmpList
.
add
(
Component
(
"com.topjohnwu.magisk.core.Provider"
,
"dummy.DummyProvider"
,
val
cmpList
=
mutableListOf
<
String
>()
cmpList
.
add
(
"""
|<provider
| android:name="%s"
...
...
@@ -114,11 +91,9 @@ fun genStubManifest(srcDir: File, outDir: File): String {
| android:directBootAware="true"
| android:exported="false"
| android:grantUriPermissions="true" />"""
.
ind
(
2
)
)
)
)
cmpList
.
add
(
Component
(
"com.topjohnwu.magisk.core.Receiver"
,
"dummy.DummyReceiver"
,
cmpList
.
add
(
"""
|<receiver
| android:name="%s"
...
...
@@ -135,11 +110,9 @@ fun genStubManifest(srcDir: File, outDir: File): String {
| <data android:scheme="package" />
| </intent-filter>
|</receiver>"""
.
ind
(
2
)
)
)
)
cmpList
.
add
(
Component
(
"com.topjohnwu.magisk.ui.MainActivity"
,
"DownloadActivity"
,
cmpList
.
add
(
"""
|<activity
| android:name="%s"
...
...
@@ -149,11 +122,9 @@ fun genStubManifest(srcDir: File, outDir: File): String {
| <category android:name="android.intent.category.LAUNCHER" />
| </intent-filter>
|</activity>"""
.
ind
(
2
)
)
)
)
cmpList
.
add
(
Component
(
"com.topjohnwu.magisk.ui.surequest.SuRequestActivity"
,
""
,
cmpList
.
add
(
"""
|<activity
| android:name="%s"
...
...
@@ -167,26 +138,22 @@ fun genStubManifest(srcDir: File, outDir: File): String {
| <category android:name="android.intent.category.DEFAULT"/>
| </intent-filter>
|</activity>"""
.
ind
(
2
)
)
)
)
cmpList
.
add
(
Component
(
"com.topjohnwu.magisk.core.download.DownloadService"
,
""
,
cmpList
.
add
(
"""
|<service
| android:name="%s"
| android:exported="false" />"""
.
trimIndent
().
ind
(
2
)
)
)
| android:exported="false" />"""
.
ind
(
2
)
)
cmpList
.
add
(
Component
(
"com.topjohnwu.magisk.core.JobService"
,
""
,
cmpList
.
add
(
"""
|<service
| android:name="%s"
| android:exported="false"
| android:permission="android.permission.BIND_JOB_SERVICE" />"""
.
ind
(
2
)
)
)
)
val
names
=
mutableListOf
<
String
>()
names
.
addAll
(
c1
)
...
...
@@ -194,33 +161,38 @@ fun genStubManifest(srcDir: File, outDir: File): String {
names
.
addAll
(
c3
.
subList
(
0
,
10
))
names
.
shuffle
(
RANDOM
)
val
pkgNames
=
names
.
subList
(
0
,
50
)
val
pkgNames
=
names
// Distinct by lower case to support case insensitive file systems
.
distinctBy
{
it
.
toLowerCase
(
Locale
.
ROOT
)
}
// Old Android does not support capitalized package names
// Check Android 7.0.0 PackageParser#buildClassName
.
map
{
it
.
decapitalize
(
Locale
.
ROOT
)
}
var
idx
=
0
fun
isJavaKeyword
(
name
:
String
)
=
when
(
name
)
{
"do"
,
"if"
,
"for"
,
"int"
,
"new"
,
"try"
->
true
else
->
false
}
fun
genCmpName
()
:
String
{
var
pkgName
:
String
val
cmps
=
mutableListOf
<
String
>()
val
usedNames
=
mutableListOf
<
String
>()
fun
genCmpName
():
String
{
var
pkgName
:
String
do
{
pkgName
=
pkgNames
[
idx
++]
pkgName
=
pkgNames
.
random
(
kRANDOM
)
}
while
(
isJavaKeyword
(
pkgName
))
var
clzName
:
String
var
clzName
:
String
do
{
clzName
=
names
.
random
(
kRANDOM
)
}
while
(
isJavaKeyword
(
clzName
))
return
"${pkgName}.${clzName}"
val
cmp
=
"${pkgName}.${clzName}"
usedNames
.
add
(
cmp
)
return
cmp
}
fun
genClass
(
clzName
:
String
,
type
:
String
)
{
fun
genClass
(
type
:
String
)
{
val
clzName
=
genCmpName
()
val
(
pkg
,
name
)
=
clzName
.
split
(
'.'
)
val
pkgDir
=
File
(
outDir
,
pkg
)
pkgDir
.
mkdir
()
...
...
@@ -230,41 +202,20 @@ fun genStubManifest(srcDir: File, outDir: File): String {
}
}
val
cmps
=
mutableListOf
<
String
>()
val
usedNames
=
mutableListOf
<
String
>(
)
val
maps
=
StringBuilder
(
)
// Generate 2 non redirect-able classes
genClass
(
"DelegateComponentFactory"
)
genClass
(
"DelegateApplication"
)
for
(
gen
in
cmpList
)
{
val
name
=
genCmpName
()
usedNames
.
add
(
name
)
maps
.
append
(
"|map.put(\"$name\", \"${gen.real}\");"
.
ind
(
2
))
maps
.
append
(
'\n'
)
if
(
gen
.
stub
.
isNotEmpty
())
{
if
(
gen
.
stub
!=
"DelegateComponentFactory"
)
{
maps
.
append
(
"|internalMap.put(\"$name\", com.topjohnwu.magisk.${gen.stub}.class);"
.
ind
(
2
))
maps
.
append
(
'\n'
)
}
if
(
gen
.
genClass
)
{
genClass
(
name
,
gen
.
stub
)
}
}
if
(
gen
.
xml
.
isNotEmpty
())
{
cmps
.
add
(
gen
.
xml
.
format
(
name
))
}
cmps
.
add
(
gen
.
format
(
name
))
}
// Shuffle the order of the components
cmps
.
shuffle
(
RANDOM
)
val
xml
=
File
(
srcDir
,
"AndroidManifest.xml"
).
readText
()
val
genXml
=
xml
.
format
(
usedNames
[
0
],
usedNames
[
1
],
cmps
.
joinToString
(
"\n\n"
))
// Write mapping information to code
val
mapping
=
File
(
srcDir
,
"Mapping.java"
).
readText
().
format
(
maps
)
PrintStream
(
File
(
mainPkgDir
,
"Mapping.java"
)).
use
{
it
.
print
(
mapping
)
}
return
genXml
val
xml
=
File
(
srcDir
,
"AndroidManifest.xml"
).
readText
()
return
xml
.
format
(
usedNames
[
0
],
usedNames
[
1
],
cmps
.
joinToString
(
"\n\n"
))
}
fun
genEncryptedResources
(
res
:
InputStream
,
outDir
:
File
)
{
...
...
stub/src/main/java/com/topjohnwu/magisk/ClassLoaders.java
View file @
c09b4dab
...
...
@@ -3,6 +3,7 @@ package com.topjohnwu.magisk;
import
com.topjohnwu.magisk.utils.DynamicClassLoader
;
import
java.io.File
;
import
java.util.Map
;
// Wrap the actual classloader as we only want to resolve classname
// mapping when loading from platform (via LoadedApk.mClassLoader)
...
...
@@ -14,19 +15,24 @@ class InjectedClassLoader extends ClassLoader {
@Override
protected
Class
<?>
loadClass
(
String
name
,
boolean
resolve
)
throws
ClassNotFoundException
{
return
super
.
loadClass
(
Mapping
.
get
(
name
),
resolve
);
String
clz
=
DynLoad
.
componentMap
.
get
(
name
);
name
=
clz
!=
null
?
clz
:
name
;
return
super
.
loadClass
(
name
,
resolve
);
}
}
class
RedirectClassLoader
extends
ClassLoader
{
RedirectClassLoader
()
{
private
final
Map
<
String
,
Class
<?>>
mapping
;
RedirectClassLoader
(
Map
<
String
,
Class
<?>>
m
)
{
super
(
RedirectClassLoader
.
class
.
getClassLoader
());
mapping
=
m
;
}
@Override
protected
Class
<?>
loadClass
(
String
name
,
boolean
resolve
)
throws
ClassNotFoundException
{
Class
<?>
clz
=
Mapping
.
internalMap
.
get
(
name
);
Class
<?>
clz
=
mapping
.
get
(
name
);
return
clz
==
null
?
super
.
loadClass
(
name
,
resolve
)
:
clz
;
}
}
...
...
stub/src/main/java/com/topjohnwu/magisk/DelegateComponentFactory.java
View file @
c09b4dab
...
...
@@ -11,6 +11,10 @@ import android.content.Intent;
import
android.content.pm.ApplicationInfo
;
import
android.os.Process
;
import
com.topjohnwu.magisk.dummy.DummyProvider
;
import
com.topjohnwu.magisk.dummy.DummyReceiver
;
import
com.topjohnwu.magisk.dummy.DummyService
;
@SuppressLint
(
"NewApi"
)
public
class
DelegateComponentFactory
extends
AppComponentFactory
{
...
...
@@ -40,7 +44,7 @@ public class DelegateComponentFactory extends AppComponentFactory {
throws
ClassNotFoundException
,
IllegalAccessException
,
InstantiationException
{
if
(
receiver
!=
null
)
return
receiver
.
instantiateActivity
(
DynLoad
.
loader
,
className
,
intent
);
return
create
(
className
);
return
create
(
className
,
DownloadActivity
.
class
);
}
@Override
...
...
@@ -48,7 +52,7 @@ public class DelegateComponentFactory extends AppComponentFactory {
throws
ClassNotFoundException
,
IllegalAccessException
,
InstantiationException
{
if
(
receiver
!=
null
)
return
receiver
.
instantiateReceiver
(
DynLoad
.
loader
,
className
,
intent
);
return
create
(
className
);
return
create
(
className
,
DummyReceiver
.
class
);
}
@Override
...
...
@@ -56,7 +60,7 @@ public class DelegateComponentFactory extends AppComponentFactory {
throws
ClassNotFoundException
,
IllegalAccessException
,
InstantiationException
{
if
(
receiver
!=
null
)
return
receiver
.
instantiateService
(
DynLoad
.
loader
,
className
,
intent
);
return
create
(
className
);
return
create
(
className
,
DummyService
.
class
);
}
@Override
...
...
@@ -64,12 +68,17 @@ public class DelegateComponentFactory extends AppComponentFactory {
throws
ClassNotFoundException
,
IllegalAccessException
,
InstantiationException
{
if
(
receiver
!=
null
)
return
receiver
.
instantiateProvider
(
DynLoad
.
loader
,
className
);
return
create
(
className
);
return
create
(
className
,
DummyProvider
.
class
);
}
private
<
T
>
T
create
(
String
name
)
throws
ClassNotFoundException
,
IllegalAccessException
,
InstantiationException
{
private
<
T
>
T
create
(
String
name
,
Class
<
T
>
fallback
)
throws
IllegalAccessException
,
InstantiationException
{
try
{
// noinspection unchecked
return
(
T
)
DynLoad
.
loader
.
loadClass
(
name
).
newInstance
();
}
catch
(
ClassNotFoundException
e
)
{
return
fallback
.
newInstance
();
}
}
}
stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java
View file @
c09b4dab
...
...
@@ -49,6 +49,14 @@ public class DownloadActivity extends Activity {
@Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
if
(
DynLoad
.
isDynLoader
())
{
// For some reason activity is created before Application.attach(),
// relaunch the activity using the same intent
finishAffinity
();
startActivity
(
getIntent
());
return
;
}
themed
=
new
ContextThemeWrapper
(
this
,
android
.
R
.
style
.
Theme_DeviceDefault
);
// Only download and dynamic load full APK if hidden
...
...
stub/src/main/java/com/topjohnwu/magisk/DynLoad.java
View file @
c09b4dab
...
...
@@ -4,14 +4,21 @@ import static com.topjohnwu.magisk.BuildConfig.APPLICATION_ID;
import
android.app.AppComponentFactory
;
import
android.app.Application
;
import
android.app.job.JobService
;
import
android.content.Context
;
import
android.content.ContextWrapper
;
import
android.content.pm.ActivityInfo
;
import
android.content.pm.ApplicationInfo
;
import
android.content.pm.PackageInfo
;
import
android.content.pm.PackageManager
;
import
android.content.pm.ServiceInfo
;
import
android.os.Build
;
import
android.os.Environment
;
import
android.util.Log
;
import
com.topjohnwu.magisk.dummy.DummyProvider
;
import
com.topjohnwu.magisk.dummy.DummyReceiver
;
import
com.topjohnwu.magisk.dummy.DummyService
;
import
com.topjohnwu.magisk.utils.APKInstall
;
import
java.io.File
;
...
...
@@ -20,6 +27,8 @@ import java.io.FileOutputStream;
import
java.io.IOException
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Method
;
import
java.util.HashMap
;
import
java.util.Map
;
import
io.michaelrocks.paranoid.Obfuscate
;
...
...
@@ -28,15 +37,20 @@ import io.michaelrocks.paranoid.Obfuscate;
public
class
DynLoad
{
// The current active classloader
static
ClassLoader
loader
=
new
Redirec
tClassLoader
();
static
ClassLoader
loader
=
DynLoad
.
class
.
ge
tClassLoader
();
static
Object
componentFactory
;
static
Map
<
String
,
String
>
componentMap
=
new
HashMap
<>();
private
static
boolean
loadedApk
=
false
;
static
StubApk
.
Data
createApkData
()
{
var
data
=
new
StubApk
.
Data
();
data
.
setVersion
(
BuildConfig
.
STUB_VERSION
);
data
.
setClassToComponent
(
Mapping
.
inverseMap
);
Map
<
String
,
String
>
map
=
new
HashMap
<>();
for
(
var
e
:
componentMap
.
entrySet
())
{
map
.
put
(
e
.
getValue
(),
e
.
getKey
());
}
data
.
setClassToComponent
(
map
);
data
.
setRootService
(
DelegateRootService
.
class
);
return
data
;
}
...
...
@@ -135,23 +149,40 @@ public class DynLoad {
if
(
Build
.
VERSION
.
SDK_INT
<
29
)
replaceClassLoader
(
context
);
if
(!
loadApk
(
context
))
int
flags
=
PackageManager
.
GET_ACTIVITIES
|
PackageManager
.
GET_SERVICES
|
PackageManager
.
GET_PROVIDERS
|
PackageManager
.
GET_RECEIVERS
;
final
PackageInfo
info
;
try
{
info
=
context
.
getPackageManager
()
.
getPackageInfo
(
context
.
getPackageName
(),
flags
);
}
catch
(
PackageManager
.
NameNotFoundException
e
)
{
// Impossible
throw
new
RuntimeException
(
e
);
}
if
(!
loadApk
(
context
))
{
loader
=
new
RedirectClassLoader
(
createInternalMap
(
info
));
return
null
;
}
File
apk
=
StubApk
.
current
(
context
);
PackageManager
pm
=
context
.
getPackageManager
();
try
{
var
info
=
pm
.
getPackageArchiveInfo
(
apk
.
getPath
(),
0
).
applicationInfo
;
var
pkgInfo
=
pm
.
getPackageArchiveInfo
(
apk
.
getPath
(),
flags
);
var
appInfo
=
pkgInfo
.
applicationInfo
;
updateComponentMap
(
info
,
pkgInfo
);
// Create the receiver Application
var
data
=
createApkData
();
var
app
=
(
Application
)
loader
.
loadClass
(
i
nfo
.
className
)
var
app
=
(
Application
)
loader
.
loadClass
(
appI
nfo
.
className
)
.
getConstructor
(
Object
.
class
)
.
newInstance
(
data
.
getObject
());
// Create the receiver component factory
if
(
Build
.
VERSION
.
SDK_INT
>=
28
&&
componentFactory
!=
null
)
{
Object
factory
=
loader
.
loadClass
(
i
nfo
.
appComponentFactory
).
newInstance
();
Object
factory
=
loader
.
loadClass
(
appI
nfo
.
appComponentFactory
).
newInstance
();
var
delegate
=
(
DelegateComponentFactory
)
componentFactory
;
delegate
.
receiver
=
(
AppComponentFactory
)
factory
;
}
...
...
@@ -168,7 +199,90 @@ public class DynLoad {
return
null
;
}
private
static
boolean
isDynLoader
()
{
private
static
Map
<
String
,
Class
<?>>
createInternalMap
(
PackageInfo
info
)
{
Map
<
String
,
Class
<?>>
map
=
new
HashMap
<>();
for
(
var
c
:
info
.
activities
)
{
map
.
put
(
c
.
name
,
DownloadActivity
.
class
);
}
for
(
var
c
:
info
.
services
)
{
map
.
put
(
c
.
name
,
DummyService
.
class
);
}
for
(
var
c
:
info
.
providers
)
{
map
.
put
(
c
.
name
,
DummyProvider
.
class
);
}
for
(
var
c
:
info
.
receivers
)
{
map
.
put
(
c
.
name
,
DummyReceiver
.
class
);
}
return
map
;
}
private
static
void
updateComponentMap
(
PackageInfo
from
,
PackageInfo
to
)
{
{
var
src
=
from
.
activities
;
var
dest
=
to
.
activities
;
final
ActivityInfo
sa
;
final
ActivityInfo
da
;
final
ActivityInfo
sb
;
final
ActivityInfo
db
;
if
(
src
[
0
].
exported
)
{
sa
=
src
[
0
];
sb
=
src
[
1
];
}
else
{
sa
=
src
[
1
];
sb
=
src
[
0
];
}
if
(
dest
[
0
].
exported
)
{
da
=
dest
[
0
];
db
=
dest
[
1
];
}
else
{
da
=
dest
[
1
];
db
=
dest
[
0
];
}
componentMap
.
put
(
sa
.
name
,
da
.
name
);
componentMap
.
put
(
sb
.
name
,
db
.
name
);
}
{
var
src
=
from
.
services
;
var
dest
=
to
.
services
;
final
ServiceInfo
sa
;
final
ServiceInfo
da
;
final
ServiceInfo
sb
;
final
ServiceInfo
db
;
if
(
JobService
.
PERMISSION_BIND
.
equals
(
src
[
0
].
permission
))
{
sa
=
src
[
0
];
sb
=
src
[
1
];
}
else
{
sa
=
src
[
1
];
sb
=
src
[
0
];
}
if
(
JobService
.
PERMISSION_BIND
.
equals
(
dest
[
0
].
permission
))
{
da
=
dest
[
0
];
db
=
dest
[
1
];
}
else
{
da
=
dest
[
1
];
db
=
dest
[
0
];
}
componentMap
.
put
(
sa
.
name
,
da
.
name
);
componentMap
.
put
(
sb
.
name
,
db
.
name
);
}
{
var
src
=
from
.
receivers
;
var
dest
=
to
.
receivers
;
componentMap
.
put
(
src
[
0
].
name
,
dest
[
0
].
name
);
}
{
var
src
=
from
.
providers
;
var
dest
=
to
.
providers
;
componentMap
.
put
(
src
[
0
].
name
,
dest
[
0
].
name
);
}
}
static
boolean
isDynLoader
()
{
return
loader
instanceof
InjectedClassLoader
;
}
...
...
stub/src/main/java/com/topjohnwu/magisk/dummy/DummyService.java
0 → 100644
View file @
c09b4dab
package
com
.
topjohnwu
.
magisk
.
dummy
;
import
android.app.Service
;
import
android.content.Intent
;
import
android.os.IBinder
;
public
class
DummyService
extends
Service
{
@Override
public
IBinder
onBind
(
Intent
intent
)
{
return
null
;
}
}
stub/template/Mapping.java
deleted
100644 → 0
View file @
a4aa4a91
package
com
.
topjohnwu
.
magisk
;
import
java.util.HashMap
;
import
java.util.Map
;
import
io.michaelrocks.paranoid.Obfuscate
;
@Obfuscate
public
class
Mapping
{
private
static
final
Map
<
String
,
String
>
map
=
new
HashMap
<>();
public
static
final
Map
<
String
,
Class
<?>>
internalMap
=
new
HashMap
<>();
public
static
final
Map
<
String
,
String
>
inverseMap
=
new
HashMap
<>();
static
{
%
s
for
(
Map
.
Entry
<
String
,
String
>
e
:
map
.
entrySet
())
{
inverseMap
.
put
(
e
.
getValue
(),
e
.
getKey
());
}
}
public
static
String
get
(
String
name
)
{
String
n
=
map
.
get
(
name
);
return
n
!=
null
?
n
:
name
;
}
}
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