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
963edfe8
Commit
963edfe8
authored
Oct 04, 2017
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add InputStream mode for signing zips
parent
53237f3a
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
258 additions
and
81 deletions
+258
-81
ByteArrayStream.java
.../java/com/topjohnwu/magisk/container/ByteArrayStream.java
+34
-0
JarMap.java
app/src/main/java/com/topjohnwu/magisk/container/JarMap.java
+156
-0
ZipUtils.java
app/src/main/java/com/topjohnwu/magisk/utils/ZipUtils.java
+68
-81
No files found.
app/src/main/java/com/topjohnwu/magisk/container/ByteArrayStream.java
0 → 100644
View file @
963edfe8
package
com
.
topjohnwu
.
magisk
.
container
;
import
java.io.ByteArrayInputStream
;
import
java.io.ByteArrayOutputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
public
class
ByteArrayStream
extends
ByteArrayOutputStream
{
public
byte
[]
getBuf
()
{
return
buf
;
}
public
synchronized
void
readFrom
(
InputStream
is
)
{
readFrom
(
is
,
Integer
.
MAX_VALUE
);
}
public
synchronized
void
readFrom
(
InputStream
is
,
int
len
)
{
int
read
;
byte
buffer
[]
=
new
byte
[
4096
];
try
{
while
((
read
=
is
.
read
(
buffer
,
0
,
len
<
buffer
.
length
?
len
:
buffer
.
length
))
>
0
)
{
write
(
buffer
,
0
,
read
);
len
-=
read
;
}
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
}
public
synchronized
void
writeTo
(
OutputStream
out
,
int
off
,
int
len
)
throws
IOException
{
out
.
write
(
buf
,
off
,
len
);
}
public
ByteArrayInputStream
getInputStream
()
{
return
new
ByteArrayInputStream
(
buf
,
0
,
count
);
}
}
app/src/main/java/com/topjohnwu/magisk/container/JarMap.java
0 → 100644
View file @
963edfe8
package
com
.
topjohnwu
.
magisk
.
container
;
import
java.io.BufferedInputStream
;
import
java.io.ByteArrayInputStream
;
import
java.io.ByteArrayOutputStream
;
import
java.io.Closeable
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.io.FileNotFoundException
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
import
java.util.Collections
;
import
java.util.Enumeration
;
import
java.util.LinkedHashMap
;
import
java.util.jar.JarEntry
;
import
java.util.jar.JarFile
;
import
java.util.jar.JarInputStream
;
import
java.util.jar.Manifest
;
import
java.util.zip.ZipEntry
;
import
java.util.zip.ZipFile
;
/*
* A universal random access interface for both JarFile and JarInputStream
*
* In the case when JarInputStream is provided to constructor, the whole stream
* will be loaded into memory for random access purposes.
* On the other hand, when a JarFile is provided, it simply works as a wrapper.
* */
public
class
JarMap
implements
Closeable
,
AutoCloseable
{
private
JarFile
jarFile
;
private
JarInputStream
jis
;
private
InputStream
is
;
private
File
file
;
private
boolean
isInputStream
=
false
,
hasLoaded
=
false
,
verify
;
private
LinkedHashMap
<
String
,
JarEntry
>
bufMap
=
new
LinkedHashMap
<>();
public
JarMap
(
File
file
)
throws
IOException
{
this
(
file
,
true
);
}
public
JarMap
(
File
file
,
boolean
verify
)
throws
IOException
{
this
(
file
,
verify
,
ZipFile
.
OPEN_READ
);
}
public
JarMap
(
File
file
,
boolean
verify
,
int
mode
)
throws
IOException
{
this
.
file
=
file
;
jarFile
=
new
JarFile
(
file
,
verify
,
mode
);
}
public
JarMap
(
String
name
)
throws
IOException
{
this
(
new
File
(
name
));
}
public
JarMap
(
String
name
,
boolean
verify
)
throws
IOException
{
this
(
new
File
(
name
),
verify
);
}
public
JarMap
(
InputStream
is
)
throws
IOException
{
this
(
is
,
true
);
}
public
JarMap
(
InputStream
is
,
boolean
verify
)
throws
IOException
{
isInputStream
=
true
;
this
.
is
=
is
;
this
.
verify
=
verify
;
}
private
void
loadJarInputStream
()
{
if
(!
isInputStream
||
hasLoaded
)
return
;
hasLoaded
=
true
;
JarEntry
entry
;
try
{
jis
=
new
JarInputStream
(
is
,
verify
);
while
((
entry
=
jis
.
getNextJarEntry
())
!=
null
)
{
bufMap
.
put
(
entry
.
getName
(),
new
JarMapEntry
(
entry
,
jis
));
}
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
}
public
InputStream
getInputStream
()
{
try
{
return
isInputStream
?
is
:
new
FileInputStream
(
file
);
}
catch
(
FileNotFoundException
e
)
{
return
null
;
}
}
public
Manifest
getManifest
()
throws
IOException
{
loadJarInputStream
();
return
isInputStream
?
jis
.
getManifest
()
:
jarFile
.
getManifest
();
}
public
InputStream
getInputStream
(
ZipEntry
ze
)
throws
IOException
{
loadJarInputStream
();
return
isInputStream
?
((
JarMapEntry
)
bufMap
.
get
(
ze
.
getName
())).
getInputStream
()
:
jarFile
.
getInputStream
(
ze
);
}
public
OutputStream
getOutputStream
(
ZipEntry
ze
)
{
if
(!
isInputStream
)
// Only support inputstream mode
return
null
;
loadJarInputStream
();
ByteArrayStream
bs
=
((
JarMapEntry
)
bufMap
.
get
(
ze
.
getName
())).
data
;
bs
.
reset
();
return
bs
;
}
public
byte
[]
getRawData
(
ZipEntry
ze
)
throws
IOException
{
if
(
isInputStream
)
{
loadJarInputStream
();
return
((
JarMapEntry
)
bufMap
.
get
(
ze
.
getName
())).
data
.
toByteArray
();
}
else
{
ByteArrayStream
bytes
=
new
ByteArrayStream
();
bytes
.
readFrom
(
jarFile
.
getInputStream
(
ze
));
return
bytes
.
toByteArray
();
}
}
public
Enumeration
<
JarEntry
>
entries
()
{
loadJarInputStream
();
return
isInputStream
?
Collections
.
enumeration
(
bufMap
.
values
())
:
jarFile
.
entries
();
}
public
ZipEntry
getEntry
(
String
name
)
{
return
getJarEntry
(
name
);
}
public
JarEntry
getJarEntry
(
String
name
)
{
loadJarInputStream
();
return
isInputStream
?
bufMap
.
get
(
name
)
:
jarFile
.
getJarEntry
(
name
);
}
@Override
public
void
close
()
throws
IOException
{
if
(
isInputStream
)
is
.
close
();
else
jarFile
.
close
();
}
private
static
class
JarMapEntry
extends
JarEntry
{
ByteArrayStream
data
;
JarMapEntry
(
JarEntry
je
,
InputStream
is
)
{
super
(
je
);
data
=
new
ByteArrayStream
();
data
.
readFrom
(
is
);
}
InputStream
getInputStream
()
{
return
data
.
getInputStream
();
}
}
}
app/src/main/java/com/topjohnwu/magisk/utils/ZipUtils.java
View file @
963edfe8
package
com
.
topjohnwu
.
magisk
.
utils
;
import
android.content.Context
;
import
android.text.TextUtils
;
import
com.topjohnwu.magisk.container.ByteArrayStream
;
import
com.topjohnwu.magisk.container.JarMap
;
import
org.bouncycastle.asn1.ASN1InputStream
;
import
org.bouncycastle.asn1.ASN1ObjectIdentifier
;
...
...
@@ -90,60 +92,43 @@ public class ZipUtils {
public
native
static
void
zipAdjust
(
String
filenameIn
,
String
filenameOut
);
public
static
String
generateUnhide
(
Context
context
,
File
output
)
{
File
temp
=
new
File
(
context
.
getCacheDir
(),
"temp.apk"
);
String
pkg
=
""
;
try
{
JarInputStream
source
=
new
JarInputStream
(
context
.
getAssets
().
open
(
UNHIDE_APK
));
JarOutputStream
dest
=
new
JarOutputStream
(
new
FileOutputStream
(
temp
));
JarEntry
entry
;
int
size
;
byte
buffer
[]
=
new
byte
[
4096
];
while
((
entry
=
source
.
getNextJarEntry
())
!=
null
)
{
dest
.
putNextEntry
(
new
JarEntry
(
entry
.
getName
()));
if
(
TextUtils
.
equals
(
entry
.
getName
(),
ANDROID_MANIFEST
))
{
ByteArrayOutputStream
baos
=
new
ByteArrayOutputStream
();
while
((
size
=
source
.
read
(
buffer
))
!=
-
1
)
{
baos
.
write
(
buffer
,
0
,
size
);
}
int
offset
=
-
1
;
byte
xml
[]
=
baos
.
toByteArray
();
// Linear search pattern offset
for
(
int
i
=
0
;
i
<
xml
.
length
-
UNHIDE_PKG_NAME
.
length
;
++
i
)
{
boolean
match
=
true
;
for
(
int
j
=
0
;
j
<
UNHIDE_PKG_NAME
.
length
;
++
j
)
{
if
(
xml
[
i
+
j
]
!=
UNHIDE_PKG_NAME
[
j
])
{
match
=
false
;
break
;
}
}
if
(
match
)
{
offset
=
i
;
break
;
}
}
if
(
offset
<
0
)
return
""
;
// Patch binary XML with new package name
pkg
=
Utils
.
genPackageName
(
"com."
,
UNHIDE_PKG_NAME
.
length
-
1
);
System
.
arraycopy
(
pkg
.
getBytes
(),
0
,
xml
,
offset
,
pkg
.
length
());
dest
.
write
(
xml
);
}
else
{
while
((
size
=
source
.
read
(
buffer
))
!=
-
1
)
{
dest
.
write
(
buffer
,
0
,
size
);
String
pkg
;
JarMap
apk
=
new
JarMap
(
context
.
getAssets
().
open
(
UNHIDE_APK
));
JarEntry
je
=
new
JarEntry
(
ANDROID_MANIFEST
);
byte
xml
[]
=
apk
.
getRawData
(
je
);
int
offset
=
-
1
;
// Linear search pattern offset
for
(
int
i
=
0
;
i
<
xml
.
length
-
UNHIDE_PKG_NAME
.
length
;
++
i
)
{
boolean
match
=
true
;
for
(
int
j
=
0
;
j
<
UNHIDE_PKG_NAME
.
length
;
++
j
)
{
if
(
xml
[
i
+
j
]
!=
UNHIDE_PKG_NAME
[
j
])
{
match
=
false
;
break
;
}
}
if
(
match
)
{
offset
=
i
;
break
;
}
}
source
.
close
();
dest
.
close
();
signZip
(
context
,
temp
,
output
,
false
);
temp
.
delete
();
if
(
offset
<
0
)
return
""
;
// Patch binary XML with new package name
pkg
=
Utils
.
genPackageName
(
"com."
,
UNHIDE_PKG_NAME
.
length
-
1
);
System
.
arraycopy
(
pkg
.
getBytes
(),
0
,
xml
,
offset
,
pkg
.
length
());
apk
.
getOutputStream
(
je
).
write
(
xml
);
// Sign the APK
signZip
(
context
,
apk
,
output
,
false
);
return
pkg
;
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
return
pkg
;
return
""
;
}
return
pkg
;
}
public
static
void
removeTopFolder
(
InputStream
in
,
File
output
)
throws
IOException
{
...
...
@@ -174,7 +159,6 @@ public class ZipUtils {
dest
.
close
();
in
.
close
();
}
catch
(
IOException
e
)
{
Logger
.
dev
(
"ZipUtils: removeTopFolder IO error!"
);
throw
e
;
}
}
...
...
@@ -201,7 +185,6 @@ public class ZipUtils {
}
else
{
name
=
entry
.
getName
();
}
Logger
.
dev
(
"ZipUtils: Extracting: "
+
entry
);
File
dest
=
new
File
(
folder
,
name
);
dest
.
getParentFile
().
mkdirs
();
FileOutputStream
out
=
new
FileOutputStream
(
dest
);
...
...
@@ -218,9 +201,24 @@ public class ZipUtils {
}
}
public
static
void
signZip
(
Context
context
,
InputStream
is
,
File
output
,
boolean
minSign
)
{
try
{
signZip
(
context
,
new
JarMap
(
is
,
false
),
output
,
minSign
);
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
}
public
static
void
signZip
(
Context
context
,
File
input
,
File
output
,
boolean
minSign
)
{
try
{
signZip
(
context
,
new
JarMap
(
new
FileInputStream
(
input
),
false
),
output
,
minSign
);
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
}
public
static
void
signZip
(
Context
context
,
JarMap
input
,
File
output
,
boolean
minSign
)
{
int
alignment
=
4
;
JarFile
inputJar
=
null
;
BufferedOutputStream
outputFile
=
null
;
int
hashes
=
0
;
try
{
...
...
@@ -235,9 +233,8 @@ public class ZipUtils {
outputFile
=
new
BufferedOutputStream
(
new
FileOutputStream
(
output
));
if
(
minSign
)
{
ZipUtils
.
signWholeFile
(
input
,
publicKey
,
privateKey
,
outputFile
);
ZipUtils
.
signWholeFile
(
input
.
getInputStream
()
,
publicKey
,
privateKey
,
outputFile
);
}
else
{
inputJar
=
new
JarFile
(
input
,
false
);
// Don't verify.
JarOutputStream
outputJar
=
new
JarOutputStream
(
outputFile
);
// For signing .apks, use the maximum compression to make
// them as small as possible (since they live forever on
...
...
@@ -246,16 +243,16 @@ public class ZipUtils {
// and produces output that is only a tiny bit larger
// (~0.1% on full OTA packages I tested).
outputJar
.
setLevel
(
9
);
Manifest
manifest
=
addDigestsToManifest
(
input
Jar
,
hashes
);
copyFiles
(
manifest
,
input
Jar
,
outputJar
,
timestamp
,
alignment
);
signFile
(
manifest
,
input
Jar
,
publicKey
,
privateKey
,
outputJar
);
Manifest
manifest
=
addDigestsToManifest
(
input
,
hashes
);
copyFiles
(
manifest
,
input
,
outputJar
,
timestamp
,
alignment
);
signFile
(
manifest
,
input
,
publicKey
,
privateKey
,
outputJar
);
outputJar
.
close
();
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
finally
{
try
{
if
(
input
Jar
!=
null
)
inputJar
.
close
();
if
(
input
!=
null
)
input
.
close
();
if
(
outputFile
!=
null
)
outputFile
.
close
();
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
...
...
@@ -335,7 +332,7 @@ public class ZipUtils {
* Add the hash(es) of every file to the manifest, creating it if
* necessary.
*/
private
static
Manifest
addDigestsToManifest
(
Jar
File
jar
,
int
hashes
)
private
static
Manifest
addDigestsToManifest
(
Jar
Map
jar
,
int
hashes
)
throws
IOException
,
GeneralSecurityException
{
Manifest
input
=
jar
.
getManifest
();
Manifest
output
=
new
Manifest
();
...
...
@@ -490,12 +487,12 @@ public class ZipUtils {
* reduce variation in the output file and make incremental OTAs
* more efficient.
*/
private
static
void
copyFiles
(
Manifest
manifest
,
Jar
File
in
,
JarOutputStream
out
,
private
static
void
copyFiles
(
Manifest
manifest
,
Jar
Map
in
,
JarOutputStream
out
,
long
timestamp
,
int
alignment
)
throws
IOException
{
byte
[]
buffer
=
new
byte
[
4096
];
int
num
;
Map
<
String
,
Attributes
>
entries
=
manifest
.
getEntries
();
ArrayList
<
String
>
names
=
new
ArrayList
<
String
>(
entries
.
keySet
());
ArrayList
<
String
>
names
=
new
ArrayList
<>(
entries
.
keySet
());
Collections
.
sort
(
names
);
boolean
firstEntry
=
true
;
long
offset
=
0L
;
...
...
@@ -564,15 +561,15 @@ public class ZipUtils {
// Used for signWholeFile
private
static
class
CMSProcessableFile
implements
CMSTypedData
{
private
File
file
;
private
InputStream
is
;
private
ASN1ObjectIdentifier
type
;
private
byte
[]
buffer
;
int
bufferSize
=
0
;
ByteArrayStream
bos
;
CMSProcessableFile
(
File
file
)
{
this
.
file
=
file
;
CMSProcessableFile
(
InputStream
is
)
{
this
.
is
=
is
;
type
=
new
ASN1ObjectIdentifier
(
CMSObjectIdentifiers
.
data
.
getId
());
buffer
=
new
byte
[
4096
];
bos
=
new
ByteArrayStream
();
bos
.
readFrom
(
is
);
}
@Override
...
...
@@ -582,30 +579,20 @@ public class ZipUtils {
@Override
public
void
write
(
OutputStream
out
)
throws
IOException
,
CMSException
{
FileInputStream
input
=
new
FileInputStream
(
file
);
long
len
=
file
.
length
()
-
2
;
while
((
bufferSize
=
input
.
read
(
buffer
))
>
0
)
{
if
(
len
<=
bufferSize
)
{
out
.
write
(
buffer
,
0
,
(
int
)
len
);
break
;
}
else
{
out
.
write
(
buffer
,
0
,
bufferSize
);
}
len
-=
bufferSize
;
}
bos
.
writeTo
(
out
,
0
,
bos
.
size
()
-
2
);
}
@Override
public
Object
getContent
()
{
return
file
;
return
is
;
}
byte
[]
getTail
()
{
return
Arrays
.
copyOfRange
(
b
uffer
,
0
,
bufferSize
);
return
Arrays
.
copyOfRange
(
b
os
.
getBuf
(),
bos
.
size
()
-
22
,
bos
.
size
()
);
}
}
private
static
void
signWholeFile
(
File
input
,
X509Certificate
publicKey
,
private
static
void
signWholeFile
(
InputStream
input
,
X509Certificate
publicKey
,
PrivateKey
privateKey
,
OutputStream
outputStream
)
throws
Exception
{
ByteArrayOutputStream
temp
=
new
ByteArrayOutputStream
();
...
...
@@ -666,7 +653,7 @@ public class ZipUtils {
outputStream
.
write
((
total_size
>>
8
)
&
0xff
);
temp
.
writeTo
(
outputStream
);
}
private
static
void
signFile
(
Manifest
manifest
,
Jar
File
inputJar
,
private
static
void
signFile
(
Manifest
manifest
,
Jar
Map
inputJar
,
X509Certificate
publicKey
,
PrivateKey
privateKey
,
JarOutputStream
outputJar
)
throws
Exception
{
...
...
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