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
fba83e23
Commit
fba83e23
authored
Jan 26, 2021
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support stub APK loading down to Android 5.0
parent
f1295cb7
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
141 additions
and
116 deletions
+141
-116
AndroidManifest.xml
stub/src/main/AndroidManifest.xml
+3
-3
ii.java
stub/src/main/java/a/ii.java
+0
-5
r.java
stub/src/main/java/a/r.java
+0
-5
u7.java
stub/src/main/java/a/u7.java
+0
-5
ClassLoaders.java
stub/src/main/java/com/topjohnwu/magisk/ClassLoaders.java
+33
-0
DelegateApplication.java
...c/main/java/com/topjohnwu/magisk/DelegateApplication.java
+9
-10
DelegateComponentFactory.java
...n/java/com/topjohnwu/magisk/DelegateComponentFactory.java
+18
-31
InjectAPK.java
stub/src/main/java/com/topjohnwu/magisk/InjectAPK.java
+54
-22
Mapping.java
stub/src/main/java/com/topjohnwu/magisk/Mapping.java
+24
-9
DummyActivity.java
...c/main/java/com/topjohnwu/magisk/dummy/DummyActivity.java
+0
-13
DummyService.java
...rc/main/java/com/topjohnwu/magisk/dummy/DummyService.java
+0
-13
No files found.
stub/src/main/AndroidManifest.xml
View file @
fba83e23
...
...
@@ -13,7 +13,7 @@
tools:ignore=
"GoogleAppIndexingWarning,MissingApplicationIcon,UnusedAttribute"
>
<!-- Splash -->
<activity
android:name=
"
a
.u7"
>
<activity
android:name=
"
f
.u7"
>
<intent-filter>
<action
android:name=
"android.intent.action.MAIN"
/>
<category
android:name=
"android.intent.category.LAUNCHER"
/>
...
...
@@ -42,7 +42,7 @@
<!-- Receiver -->
<receiver
android:name=
"
a.r
"
android:name=
"
yy.E
"
android:directBootAware=
"true"
android:exported=
"false"
>
<intent-filter>
...
...
@@ -61,7 +61,7 @@
<!-- FileProvider -->
<provider
android:name=
"
a.ii
"
android:name=
"
fxQ.lk
"
android:authorities=
"${applicationId}.provider"
android:directBootAware=
"true"
android:exported=
"false"
...
...
stub/src/main/java/a/ii.java
deleted
100644 → 0
View file @
f1295cb7
package
a
;
import
com.topjohnwu.magisk.FileProvider
;
public
class
ii
extends
FileProvider
{}
stub/src/main/java/a/r.java
deleted
100644 → 0
View file @
f1295cb7
package
a
;
import
com.topjohnwu.magisk.dummy.DummyReceiver
;
public
class
r
extends
DummyReceiver
{}
stub/src/main/java/a/u7.java
deleted
100644 → 0
View file @
f1295cb7
package
a
;
import
com.topjohnwu.magisk.DownloadActivity
;
public
class
u7
extends
DownloadActivity
{}
stub/src/main/java/com/topjohnwu/magisk/ClassLoaders.java
0 → 100644
View file @
fba83e23
package
com
.
topjohnwu
.
magisk
;
import
com.topjohnwu.magisk.utils.DynamicClassLoader
;
import
java.io.File
;
class
InjectedClassLoader
extends
DynamicClassLoader
{
InjectedClassLoader
(
File
apk
)
{
super
(
apk
,
/* Use the base classloader as we do not want stub
* APK classes accessible from the main app */
Object
.
class
.
getClassLoader
());
}
@Override
protected
Class
<?>
loadClass
(
String
name
,
boolean
resolve
)
throws
ClassNotFoundException
{
return
super
.
loadClass
(
Mapping
.
get
(
name
),
resolve
);
}
}
class
RedirectClassLoader
extends
ClassLoader
{
RedirectClassLoader
()
{
super
(
RedirectClassLoader
.
class
.
getClassLoader
());
}
@Override
protected
Class
<?>
loadClass
(
String
name
,
boolean
resolve
)
throws
ClassNotFoundException
{
Class
<?>
clz
=
Mapping
.
internalMap
.
get
(
name
);
return
clz
==
null
?
super
.
loadClass
(
name
,
resolve
)
:
clz
;
}
}
stub/src/main/java/com/topjohnwu/magisk/DelegateApplication.java
View file @
fba83e23
...
...
@@ -10,32 +10,31 @@ import java.lang.reflect.Method;
public
class
DelegateApplication
extends
Application
{
private
Application
delegate
;
static
boolean
dynLoad
=
false
;
private
Application
receiver
;
@Override
protected
void
attachBaseContext
(
Context
base
)
{
super
.
attachBaseContext
(
base
);
// Only dynamic load full APK if hidden and
possible
dynLoad
=
Build
.
VERSION
.
SDK_INT
>=
2
8
&&
// Only dynamic load full APK if hidden and
supported
dynLoad
=
Build
.
VERSION
.
SDK_INT
>=
2
1
&&
!
base
.
getPackageName
().
equals
(
BuildConfig
.
APPLICATION_ID
);
if
(!
dynLoad
)
return
;
delegate
=
InjectAPK
.
setup
(
this
);
if
(
delegate
!=
null
)
try
{
receiver
=
InjectAPK
.
setup
(
this
);
if
(
receiver
!=
null
)
try
{
// Call attachBaseContext without ContextImpl to show it is being wrapped
Method
m
=
ContextWrapper
.
class
.
getDeclaredMethod
(
"attachBaseContext"
,
Context
.
class
);
m
.
setAccessible
(
true
);
m
.
invoke
(
delegate
,
this
);
m
.
invoke
(
receiver
,
this
);
}
catch
(
Exception
ignored
)
{
/* Impossible */
}
}
@Override
public
void
onConfigurationChanged
(
Configuration
newConfig
)
{
super
.
onConfigurationChanged
(
newConfig
);
if
(
delegate
!=
null
)
delegate
.
onConfigurationChanged
(
newConfig
);
if
(
receiver
!=
null
)
receiver
.
onConfigurationChanged
(
newConfig
);
}
}
stub/src/main/java/com/topjohnwu/magisk/DelegateComponentFactory.java
View file @
fba83e23
...
...
@@ -9,22 +9,15 @@ import android.content.BroadcastReceiver;
import
android.content.ContentProvider
;
import
android.content.Intent
;
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
{
static
DelegateComponentFactory
INSTANCE
;
ClassLoader
loader
;
AppComponentFactory
delegate
;
interface
DummyFactory
<
T
>
{
T
create
();
}
AppComponentFactory
receiver
;
public
DelegateComponentFactory
()
{
I
njectAPK
.
factory
=
this
;
I
NSTANCE
=
this
;
}
@Override
...
...
@@ -36,45 +29,39 @@ public class DelegateComponentFactory extends AppComponentFactory {
@Override
public
Activity
instantiateActivity
(
ClassLoader
cl
,
String
className
,
Intent
intent
)
throws
ClassNotFoundException
,
IllegalAccessException
,
InstantiationException
{
if
(
delegate
!=
null
)
return
delegate
.
instantiateActivity
(
loader
,
Mapping
.
get
(
className
)
,
intent
);
return
create
(
className
,
DownloadActivity:
:
new
);
if
(
receiver
!=
null
)
return
receiver
.
instantiateActivity
(
loader
,
className
,
intent
);
return
create
(
className
);
}
@Override
public
BroadcastReceiver
instantiateReceiver
(
ClassLoader
cl
,
String
className
,
Intent
intent
)
throws
ClassNotFoundException
,
IllegalAccessException
,
InstantiationException
{
if
(
delegate
!=
null
)
return
delegate
.
instantiateReceiver
(
loader
,
Mapping
.
get
(
className
)
,
intent
);
return
create
(
className
,
DummyReceiver:
:
new
);
if
(
receiver
!=
null
)
return
receiver
.
instantiateReceiver
(
loader
,
className
,
intent
);
return
create
(
className
);
}
@Override
public
Service
instantiateService
(
ClassLoader
cl
,
String
className
,
Intent
intent
)
throws
ClassNotFoundException
,
IllegalAccessException
,
InstantiationException
{
if
(
delegate
!=
null
)
return
delegate
.
instantiateService
(
loader
,
Mapping
.
get
(
className
)
,
intent
);
return
create
(
className
,
DummyService:
:
new
);
if
(
receiver
!=
null
)
return
receiver
.
instantiateService
(
loader
,
className
,
intent
);
return
create
(
className
);
}
@Override
public
ContentProvider
instantiateProvider
(
ClassLoader
cl
,
String
className
)
throws
ClassNotFoundException
,
IllegalAccessException
,
InstantiationException
{
if
(
loader
==
null
)
loader
=
cl
;
if
(
delegate
!=
null
)
return
delegate
.
instantiateProvider
(
loader
,
Mapping
.
get
(
className
)
);
return
create
(
className
,
DummyProvider:
:
new
);
if
(
receiver
!=
null
)
return
receiver
.
instantiateProvider
(
loader
,
className
);
return
create
(
className
);
}
/**
* Create the class or dummy implementation if creation failed
*/
private
<
T
>
T
create
(
String
name
,
DummyFactory
<
T
>
factory
)
{
try
{
return
(
T
)
loader
.
loadClass
(
name
).
newInstance
();
}
catch
(
Exception
ignored
)
{
return
factory
.
create
();
}
private
<
T
>
T
create
(
String
name
)
throws
ClassNotFoundException
,
IllegalAccessException
,
InstantiationException
{
return
(
T
)
loader
.
loadClass
(
name
).
newInstance
();
}
}
stub/src/main/java/com/topjohnwu/magisk/InjectAPK.java
View file @
fba83e23
...
...
@@ -4,65 +4,97 @@ import android.app.AppComponentFactory;
import
android.app.Application
;
import
android.content.ContentResolver
;
import
android.content.Context
;
import
android.content.ContextWrapper
;
import
android.net.Uri
;
import
android.os.Build
;
import
android.util.Log
;
import
com.topjohnwu.magisk.utils.DynamicClassLoader
;
import
java.io.File
;
import
java.io.FileOutputStream
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
import
java.lang.reflect.Field
;
public
class
InjectAPK
{
static
DelegateComponentFactory
factory
;
@SuppressWarnings
(
"ResultOfMethodCallIgnored"
)
static
Application
setup
(
Context
context
)
{
// Get ContextImpl
while
(
context
instanceof
ContextWrapper
)
{
context
=
((
ContextWrapper
)
context
).
getBaseContext
();
}
File
apk
=
DynAPK
.
current
(
context
);
File
update
=
DynAPK
.
update
(
context
);
if
(
update
.
exists
())
update
.
renameTo
(
apk
);
Application
delegate
=
null
;
Application
result
=
null
;
if
(!
apk
.
exists
())
{
// Try copying APK
Uri
uri
=
new
Uri
.
Builder
().
scheme
(
"content"
)
.
authority
(
"com.topjohnwu.magisk.provider"
)
.
encodedPath
(
"apk_file"
).
build
();
ContentResolver
resolver
=
context
.
getContentResolver
();
try
(
InputStream
is
=
resolver
.
openInputStream
(
uri
))
{
if
(
is
!=
null
)
{
try
(
InputStream
src
=
resolver
.
openInputStream
(
uri
))
{
if
(
src
!=
null
)
{
try
(
OutputStream
out
=
new
FileOutputStream
(
apk
))
{
byte
[]
buf
=
new
byte
[
4096
];
for
(
int
read
;
(
read
=
is
.
read
(
buf
))
>=
0
;)
{
for
(
int
read
;
(
read
=
src
.
read
(
buf
))
>=
0
;)
{
out
.
write
(
buf
,
0
,
read
);
}
}
}
}
catch
(
Exception
e
)
{
Log
.
e
(
InjectAPK
.
class
.
getSimpleName
(),
""
,
e
);
}
}
catch
(
Exception
ignored
)
{}
}
if
(
apk
.
exists
())
{
ClassLoader
cl
=
new
DynamicClassLoader
(
apk
,
factory
.
loader
);
ClassLoader
cl
=
new
InjectedClassLoader
(
apk
);
try
{
// Create the delegate AppComponentFactory
AppComponentFactory
df
=
(
AppComponentFactory
)
cl
.
loadClass
(
"androidx.core.app.CoreComponentFactory"
).
newInstance
();
// Create the delegate Application
delegate
=
(
Application
)
cl
.
loadClass
(
"a.e"
).
getConstructor
(
Object
.
class
)
// Create the receiver Application
Object
app
=
cl
.
loadClass
(
Mapping
.
get
(
"APP"
)).
getConstructor
(
Object
.
class
)
.
newInstance
(
DynAPK
.
pack
(
dynData
()));
// If everything went well, set our loader and delegate
factory
.
delegate
=
df
;
factory
.
loader
=
cl
;
// Create the receiver component factory
Object
factory
=
null
;
if
(
Build
.
VERSION
.
SDK_INT
>=
28
)
{
factory
=
cl
.
loadClass
(
Mapping
.
get
(
"ACF"
)).
newInstance
();
}
setClassLoader
(
context
,
cl
);
// Finally, set variables
result
=
(
Application
)
app
;
if
(
Build
.
VERSION
.
SDK_INT
>=
28
)
{
DelegateComponentFactory
.
INSTANCE
.
loader
=
cl
;
DelegateComponentFactory
.
INSTANCE
.
receiver
=
(
AppComponentFactory
)
factory
;
}
}
catch
(
Exception
e
)
{
Log
.
e
(
InjectAPK
.
class
.
getSimpleName
(),
""
,
e
);
apk
.
delete
();
}
}
else
{
ClassLoader
cl
=
new
RedirectClassLoader
();
try
{
setClassLoader
(
context
,
cl
);
if
(
Build
.
VERSION
.
SDK_INT
>=
28
)
{
DelegateComponentFactory
.
INSTANCE
.
loader
=
cl
;
}
}
catch
(
Exception
e
)
{
// Should never happen
Log
.
e
(
InjectAPK
.
class
.
getSimpleName
(),
""
,
e
);
}
}
return
delegate
;
return
result
;
}
// Replace LoadedApk mClassLoader
private
static
void
setClassLoader
(
Context
impl
,
ClassLoader
cl
)
throws
NoSuchFieldException
,
IllegalAccessException
{
Field
mInfo
=
impl
.
getClass
().
getDeclaredField
(
"mPackageInfo"
);
mInfo
.
setAccessible
(
true
);
Object
loadedApk
=
mInfo
.
get
(
impl
);
Field
mcl
=
loadedApk
.
getClass
().
getDeclaredField
(
"mClassLoader"
);
mcl
.
setAccessible
(
true
);
mcl
.
set
(
loadedApk
,
cl
);
}
private
static
DynAPK
.
Data
dynData
()
{
...
...
stub/src/main/java/com/topjohnwu/magisk/Mapping.java
View file @
fba83e23
package
com
.
topjohnwu
.
magisk
;
import
com.topjohnwu.magisk.dummy.DummyReceiver
;
import
java.util.HashMap
;
import
java.util.Map
;
/**
* These are just some random class names hardcoded as an example.
* For the actual release builds, these mappings will be auto generated.
*/
public
class
Mapping
{
private
static
Map
<
String
,
String
>
map
=
new
HashMap
<>();
public
static
Map
<
String
,
String
>
inverseMap
;
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
;
static
{
map
.
put
(
"a.Q
zw"
,
"a.e
"
);
map
.
put
(
"
a.u7"
,
"a.c
"
);
map
.
put
(
"
a.ii"
,
"a.p
"
);
map
.
put
(
"
a.r"
,
"a.h
"
);
map
.
put
(
"xt.R"
,
"
a.b
"
);
map
.
put
(
"lt5.a"
,
"
a.m
"
);
map
.
put
(
"d.s"
,
"
a.j
"
);
map
.
put
(
"a.Q
"
,
"com.topjohnwu.magisk.core.App
"
);
map
.
put
(
"
f.u7"
,
"com.topjohnwu.magisk.core.SplashActivity
"
);
map
.
put
(
"
fxQ.lk"
,
"com.topjohnwu.magisk.core.Provider
"
);
map
.
put
(
"
yy.E"
,
"com.topjohnwu.magisk.core.Receiver
"
);
map
.
put
(
"xt.R"
,
"
com.topjohnwu.magisk.ui.MainActivity
"
);
map
.
put
(
"lt5.a"
,
"
com.topjohnwu.magisk.ui.surequest.SuRequestActivity
"
);
map
.
put
(
"d.s"
,
"
com.topjohnwu.magisk.core.download.DownloadService
"
);
map
.
put
(
"w.d"
,
"androidx.work.impl.background.systemjob.SystemJobService"
);
internalMap
.
put
(
"a.Q"
,
DelegateApplication
.
class
);
internalMap
.
put
(
"f.u7"
,
DownloadActivity
.
class
);
internalMap
.
put
(
"fxQ.lk"
,
FileProvider
.
class
);
internalMap
.
put
(
"yy.E"
,
DummyReceiver
.
class
);
inverseMap
=
new
HashMap
<>(
map
.
size
());
for
(
Map
.
Entry
<
String
,
String
>
e
:
map
.
entrySet
())
{
inverseMap
.
put
(
e
.
getValue
(),
e
.
getKey
());
}
map
.
put
(
"APP"
,
"com.topjohnwu.magisk.core.App"
);
map
.
put
(
"ACF"
,
"androidx.core.app.CoreComponentFactory"
);
}
public
static
String
get
(
String
name
)
{
...
...
stub/src/main/java/com/topjohnwu/magisk/dummy/DummyActivity.java
deleted
100644 → 0
View file @
f1295cb7
package
com
.
topjohnwu
.
magisk
.
dummy
;
import
android.app.Activity
;
import
android.os.Bundle
;
public
class
DummyActivity
extends
Activity
{
@Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
finish
();
}
}
stub/src/main/java/com/topjohnwu/magisk/dummy/DummyService.java
deleted
100644 → 0
View file @
f1295cb7
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
)
{
stopSelf
();
return
null
;
}
}
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