Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
S
SandHook
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
SandHook
Commits
6f69e00a
Commit
6f69e00a
authored
Apr 10, 2019
by
swift_gan
Committed by
swift_gan
Apr 10, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix XSharePreference
parent
ee13b7b5
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
516 additions
and
2 deletions
+516
-2
XmlUtils.java
...pat/src/main/java/com/android/internal/util/XmlUtils.java
+14
-0
SELinuxHelper.java
...t/src/main/java/de/robv/android/xposed/SELinuxHelper.java
+84
-0
XSharedPreferences.java
.../main/java/de/robv/android/xposed/XSharedPreferences.java
+66
-2
BaseService.java
...ain/java/de/robv/android/xposed/services/BaseService.java
+179
-0
DirectAccessService.java
.../de/robv/android/xposed/services/DirectAccessService.java
+113
-0
FileResult.java
...main/java/de/robv/android/xposed/services/FileResult.java
+60
-0
No files found.
xposedcompat/src/main/java/com/android/internal/util/XmlUtils.java
0 → 100644
View file @
6f69e00a
package
com
.
android
.
internal
.
util
;
import
org.xmlpull.v1.XmlPullParserException
;
import
java.io.InputStream
;
import
java.util.Map
;
public
class
XmlUtils
{
public
static
Map
readMapXml
(
InputStream
inputStream
)
throws
XmlPullParserException
{
return
null
;
}
}
xposedcompat/src/main/java/de/robv/android/xposed/SELinuxHelper.java
0 → 100644
View file @
6f69e00a
package
de
.
robv
.
android
.
xposed
;
import
com.swift.sandhook.xposedcompat.XposedCompat
;
import
de.robv.android.xposed.services.BaseService
;
import
de.robv.android.xposed.services.DirectAccessService
;
/**
* A helper to work with (or without) SELinux, abstracting much of its big complexity.
*/
public
final
class
SELinuxHelper
{
static
{
initForProcess
(
XposedCompat
.
processName
);
}
private
SELinuxHelper
()
{}
/**
* Determines whether SELinux is disabled or enabled.
*
* @return A boolean indicating whether SELinux is enabled.
*/
public
static
boolean
isSELinuxEnabled
()
{
return
sIsSELinuxEnabled
;
}
/**
* Determines whether SELinux is permissive or enforcing.
*
* @return A boolean indicating whether SELinux is enforcing.
*/
public
static
boolean
isSELinuxEnforced
()
{
return
sIsSELinuxEnabled
;
}
/**
* Gets the security context of the current process.
*
* @return A String representing the security context of the current process.
*/
public
static
String
getContext
()
{
return
null
;
}
/**
* Retrieve the service to be used when accessing files in {@code /data/data/*}.
*
* <p class="caution"><strong>IMPORTANT:</strong> If you call this from the Zygote process,
* don't re-use the result in different process!
*
* @return An instance of the service.
*/
public
static
BaseService
getAppDataFileService
()
{
if
(
sServiceAppDataFile
!=
null
)
return
sServiceAppDataFile
;
throw
new
UnsupportedOperationException
();
}
// ----------------------------------------------------------------------------
private
static
boolean
sIsSELinuxEnabled
=
false
;
private
static
BaseService
sServiceAppDataFile
=
null
;
/*package*/
static
void
initOnce
()
{
try
{
//sIsSELinuxEnabled = SELinux.isSELinuxEnabled();
}
catch
(
NoClassDefFoundError
ignored
)
{}
}
/*package*/
static
void
initForProcess
(
String
packageName
)
{
if
(
sIsSELinuxEnabled
)
{
if
(
packageName
==
null
)
{
// Zygote
//sServiceAppDataFile = new ZygoteService();
}
else
if
(
packageName
.
equals
(
"android"
))
{
//system_server
//sServiceAppDataFile = BinderService.getService(BinderService.TARGET_APP);
}
else
{
// app
sServiceAppDataFile
=
new
DirectAccessService
();
}
}
else
{
sServiceAppDataFile
=
new
DirectAccessService
();
}
}
}
xposedcompat/src/main/java/de/robv/android/xposed/XSharedPreferences.java
View file @
6f69e00a
...
...
@@ -5,12 +5,21 @@ import android.content.Context;
import
android.content.SharedPreferences
;
import
android.os.Environment
;
import
android.preference.PreferenceManager
;
import
android.util.Log
;
import
com.android.internal.util.XmlUtils
;
import
org.xmlpull.v1.XmlPullParserException
;
import
java.io.File
;
import
java.io.FileNotFoundException
;
import
java.io.IOException
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.Set
;
import
de.robv.android.xposed.services.FileResult
;
/**
* This class is basically the same as SharedPreferencesImpl from AOSP, but
* read-only and without listeners support. Instead, it is made to be
...
...
@@ -69,7 +78,13 @@ public final class XSharedPreferences implements SharedPreferences {
*/
@SuppressLint
(
"SetWorldReadable"
)
public
boolean
makeWorldReadable
()
{
return
false
;
if
(!
SELinuxHelper
.
getAppDataFileService
().
hasDirectFileAccess
())
return
false
;
// It doesn't make much sense to make the file readable if we wouldn't be able to access it anyway.
if
(!
mFile
.
exists
())
// Just in case - the file should never be created if it doesn't exist.
return
false
;
return
mFile
.
setReadable
(
true
,
false
);
}
/**
...
...
@@ -97,7 +112,47 @@ public final class XSharedPreferences implements SharedPreferences {
@SuppressWarnings
({
"rawtypes"
,
"unchecked"
})
private
void
loadFromDiskLocked
()
{
if
(
mLoaded
)
{
return
;
}
Map
map
=
null
;
FileResult
result
=
null
;
try
{
result
=
SELinuxHelper
.
getAppDataFileService
().
getFileInputStream
(
mFilename
,
mFileSize
,
mLastModified
);
if
(
result
.
stream
!=
null
)
{
map
=
XmlUtils
.
readMapXml
(
result
.
stream
);
result
.
stream
.
close
();
}
else
{
// The file is unchanged, keep the current values
map
=
mMap
;
}
}
catch
(
XmlPullParserException
e
)
{
Log
.
w
(
TAG
,
"getSharedPreferences"
,
e
);
}
catch
(
FileNotFoundException
ignored
)
{
// SharedPreferencesImpl has a canRead() check, so it doesn't log anything in case the file doesn't exist
}
catch
(
IOException
e
)
{
Log
.
w
(
TAG
,
"getSharedPreferences"
,
e
);
}
finally
{
if
(
result
!=
null
&&
result
.
stream
!=
null
)
{
try
{
result
.
stream
.
close
();
}
catch
(
RuntimeException
rethrown
)
{
throw
rethrown
;
}
catch
(
Exception
ignored
)
{
}
}
}
mLoaded
=
true
;
if
(
map
!=
null
)
{
mMap
=
map
;
mLastModified
=
result
.
mtime
;
mFileSize
=
result
.
size
;
}
else
{
mMap
=
new
HashMap
<>();
}
notifyAll
();
}
/**
...
...
@@ -116,7 +171,16 @@ public final class XSharedPreferences implements SharedPreferences {
* <p><strong>Warning:</strong> With enforcing SELinux, this call might be quite expensive.
*/
public
synchronized
boolean
hasFileChanged
()
{
return
false
;
try
{
FileResult
result
=
SELinuxHelper
.
getAppDataFileService
().
statFile
(
mFilename
);
return
mLastModified
!=
result
.
mtime
||
mFileSize
!=
result
.
size
;
}
catch
(
FileNotFoundException
ignored
)
{
// SharedPreferencesImpl doesn't log anything in case the file doesn't exist
return
true
;
}
catch
(
IOException
e
)
{
Log
.
w
(
TAG
,
"hasFileChanged"
,
e
);
return
true
;
}
}
private
void
awaitLoadedLocked
()
{
...
...
xposedcompat/src/main/java/de/robv/android/xposed/services/BaseService.java
0 → 100644
View file @
6f69e00a
package
de
.
robv
.
android
.
xposed
.
services
;
import
java.io.ByteArrayInputStream
;
import
java.io.FileNotFoundException
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
de.robv.android.xposed.SELinuxHelper
;
/**
* General definition of a file access service provided by the Xposed framework.
*
* <p>References to a concrete subclass should generally be retrieved from {@link SELinuxHelper}.
*/
public
abstract
class
BaseService
{
/** Flag for {@link #checkFileAccess}: Read access. */
public
static
final
int
R_OK
=
4
;
/** Flag for {@link #checkFileAccess}: Write access. */
public
static
final
int
W_OK
=
2
;
/** Flag for {@link #checkFileAccess}: Executable access. */
public
static
final
int
X_OK
=
1
;
/** Flag for {@link #checkFileAccess}: File/directory exists. */
public
static
final
int
F_OK
=
0
;
/**
* Checks whether the services accesses files directly (instead of using IPC).
*
* @return {@code true} in case direct access is possible.
*/
public
boolean
hasDirectFileAccess
()
{
return
false
;
}
/**
* Check whether a file is accessible. SELinux might enforce stricter checks.
*
* @param filename The absolute path of the file to check.
* @param mode The mode for POSIX's {@code access()} function.
* @return The result of the {@code access()} function.
*/
public
abstract
boolean
checkFileAccess
(
String
filename
,
int
mode
);
/**
* Check whether a file exists.
*
* @param filename The absolute path of the file to check.
* @return The result of the {@code access()} function.
*/
@SuppressWarnings
(
"BooleanMethodIsAlwaysInverted"
)
public
boolean
checkFileExists
(
String
filename
)
{
return
checkFileAccess
(
filename
,
F_OK
);
}
/**
* Determine the size and modification time of a file.
*
* @param filename The absolute path of the file to check.
* @return A {@link FileResult} object holding the result.
* @throws IOException In case an error occurred while retrieving the information.
*/
public
abstract
FileResult
statFile
(
String
filename
)
throws
IOException
;
/**
* Determine the size time of a file.
*
* @param filename The absolute path of the file to check.
* @return The file size.
* @throws IOException In case an error occurred while retrieving the information.
*/
public
long
getFileSize
(
String
filename
)
throws
IOException
{
return
statFile
(
filename
).
size
;
}
/**
* Determine the size time of a file.
*
* @param filename The absolute path of the file to check.
* @return The file modification time.
* @throws IOException In case an error occurred while retrieving the information.
*/
public
long
getFileModificationTime
(
String
filename
)
throws
IOException
{
return
statFile
(
filename
).
mtime
;
}
/**
* Read a file into memory.
*
* @param filename The absolute path of the file to read.
* @return A {@code byte} array with the file content.
* @throws IOException In case an error occurred while reading the file.
*/
public
abstract
byte
[]
readFile
(
String
filename
)
throws
IOException
;
/**
* Read a file into memory, but only if it has changed since the last time.
*
* @param filename The absolute path of the file to read.
* @param previousSize File size of last read.
* @param previousTime File modification time of last read.
* @return A {@link FileResult} object holding the result.
* <p>The {@link FileResult#content} field might be {@code null} if the file
* is unmodified ({@code previousSize} and {@code previousTime} are still valid).
* @throws IOException In case an error occurred while reading the file.
*/
public
abstract
FileResult
readFile
(
String
filename
,
long
previousSize
,
long
previousTime
)
throws
IOException
;
/**
* Read a file into memory, optionally only if it has changed since the last time.
*
* @param filename The absolute path of the file to read.
* @param offset Number of bytes to skip at the beginning of the file.
* @param length Number of bytes to read (0 means read to end of file).
* @param previousSize Optional: File size of last read.
* @param previousTime Optional: File modification time of last read.
* @return A {@link FileResult} object holding the result.
* <p>The {@link FileResult#content} field might be {@code null} if the file
* is unmodified ({@code previousSize} and {@code previousTime} are still valid).
* @throws IOException In case an error occurred while reading the file.
*/
public
abstract
FileResult
readFile
(
String
filename
,
int
offset
,
int
length
,
long
previousSize
,
long
previousTime
)
throws
IOException
;
/**
* Get a stream to the file content.
* Depending on the service, it may or may not be read completely into memory.
*
* @param filename The absolute path of the file to read.
* @return An {@link InputStream} to the file content.
* @throws IOException In case an error occurred while reading the file.
*/
public
InputStream
getFileInputStream
(
String
filename
)
throws
IOException
{
return
new
ByteArrayInputStream
(
readFile
(
filename
));
}
/**
* Get a stream to the file content, but only if it has changed since the last time.
* Depending on the service, it may or may not be read completely into memory.
*
* @param filename The absolute path of the file to read.
* @param previousSize Optional: File size of last read.
* @param previousTime Optional: File modification time of last read.
* @return A {@link FileResult} object holding the result.
* <p>The {@link FileResult#stream} field might be {@code null} if the file
* is unmodified ({@code previousSize} and {@code previousTime} are still valid).
* @throws IOException In case an error occurred while reading the file.
*/
public
FileResult
getFileInputStream
(
String
filename
,
long
previousSize
,
long
previousTime
)
throws
IOException
{
FileResult
result
=
readFile
(
filename
,
previousSize
,
previousTime
);
if
(
result
.
content
==
null
)
return
result
;
return
new
FileResult
(
new
ByteArrayInputStream
(
result
.
content
),
result
.
size
,
result
.
mtime
);
}
// ----------------------------------------------------------------------------
/*package*/
BaseService
()
{}
/*package*/
static
void
ensureAbsolutePath
(
String
filename
)
{
if
(!
filename
.
startsWith
(
"/"
))
{
throw
new
IllegalArgumentException
(
"Only absolute filenames are allowed: "
+
filename
);
}
}
/*package*/
static
void
throwCommonIOException
(
int
errno
,
String
errorMsg
,
String
filename
,
String
defaultText
)
throws
IOException
{
switch
(
errno
)
{
case
1
:
// EPERM
case
13
:
// EACCES
throw
new
FileNotFoundException
(
errorMsg
!=
null
?
errorMsg
:
"Permission denied: "
+
filename
);
case
2
:
// ENOENT
throw
new
FileNotFoundException
(
errorMsg
!=
null
?
errorMsg
:
"No such file or directory: "
+
filename
);
case
12
:
// ENOMEM
throw
new
OutOfMemoryError
(
errorMsg
);
case
21
:
// EISDIR
throw
new
FileNotFoundException
(
errorMsg
!=
null
?
errorMsg
:
"Is a directory: "
+
filename
);
default
:
throw
new
IOException
(
errorMsg
!=
null
?
errorMsg
:
"Error "
+
errno
+
defaultText
+
filename
);
}
}
}
xposedcompat/src/main/java/de/robv/android/xposed/services/DirectAccessService.java
0 → 100644
View file @
6f69e00a
package
de
.
robv
.
android
.
xposed
.
services
;
import
java.io.BufferedInputStream
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
/** @hide */
public
final
class
DirectAccessService
extends
BaseService
{
@Override
public
boolean
hasDirectFileAccess
()
{
return
true
;
}
@SuppressWarnings
(
"RedundantIfStatement"
)
@Override
public
boolean
checkFileAccess
(
String
filename
,
int
mode
)
{
File
file
=
new
File
(
filename
);
if
(
mode
==
F_OK
&&
!
file
.
exists
())
return
false
;
if
((
mode
&
R_OK
)
!=
0
&&
!
file
.
canRead
())
return
false
;
if
((
mode
&
W_OK
)
!=
0
&&
!
file
.
canWrite
())
return
false
;
if
((
mode
&
X_OK
)
!=
0
&&
!
file
.
canExecute
())
return
false
;
return
true
;
}
@Override
public
boolean
checkFileExists
(
String
filename
)
{
return
new
File
(
filename
).
exists
();
}
@Override
public
FileResult
statFile
(
String
filename
)
throws
IOException
{
File
file
=
new
File
(
filename
);
return
new
FileResult
(
file
.
length
(),
file
.
lastModified
());
}
@Override
public
byte
[]
readFile
(
String
filename
)
throws
IOException
{
File
file
=
new
File
(
filename
);
byte
content
[]
=
new
byte
[(
int
)
file
.
length
()];
FileInputStream
fis
=
new
FileInputStream
(
file
);
fis
.
read
(
content
);
fis
.
close
();
return
content
;
}
@Override
public
FileResult
readFile
(
String
filename
,
long
previousSize
,
long
previousTime
)
throws
IOException
{
File
file
=
new
File
(
filename
);
long
size
=
file
.
length
();
long
time
=
file
.
lastModified
();
if
(
previousSize
==
size
&&
previousTime
==
time
)
return
new
FileResult
(
size
,
time
);
return
new
FileResult
(
readFile
(
filename
),
size
,
time
);
}
@Override
public
FileResult
readFile
(
String
filename
,
int
offset
,
int
length
,
long
previousSize
,
long
previousTime
)
throws
IOException
{
File
file
=
new
File
(
filename
);
long
size
=
file
.
length
();
long
time
=
file
.
lastModified
();
if
(
previousSize
==
size
&&
previousTime
==
time
)
return
new
FileResult
(
size
,
time
);
// Shortcut for the simple case
if
(
offset
<=
0
&&
length
<=
0
)
return
new
FileResult
(
readFile
(
filename
),
size
,
time
);
// Check range
if
(
offset
>
0
&&
offset
>=
size
)
{
throw
new
IllegalArgumentException
(
"Offset "
+
offset
+
" is out of range for "
+
filename
);
}
else
if
(
offset
<
0
)
{
offset
=
0
;
}
if
(
length
>
0
&&
(
offset
+
length
)
>
size
)
{
throw
new
IllegalArgumentException
(
"Length "
+
length
+
" is out of range for "
+
filename
);
}
else
if
(
length
<=
0
)
{
length
=
(
int
)
(
size
-
offset
);
}
byte
content
[]
=
new
byte
[
length
];
FileInputStream
fis
=
new
FileInputStream
(
file
);
fis
.
skip
(
offset
);
fis
.
read
(
content
);
fis
.
close
();
return
new
FileResult
(
content
,
size
,
time
);
}
/**
* {@inheritDoc}
* <p>This implementation returns a BufferedInputStream instead of loading the file into memory.
*/
@Override
public
InputStream
getFileInputStream
(
String
filename
)
throws
IOException
{
return
new
BufferedInputStream
(
new
FileInputStream
(
filename
),
16
*
1024
);
}
/**
* {@inheritDoc}
* <p>This implementation returns a BufferedInputStream instead of loading the file into memory.
*/
@Override
public
FileResult
getFileInputStream
(
String
filename
,
long
previousSize
,
long
previousTime
)
throws
IOException
{
File
file
=
new
File
(
filename
);
long
size
=
file
.
length
();
long
time
=
file
.
lastModified
();
if
(
previousSize
==
size
&&
previousTime
==
time
)
return
new
FileResult
(
size
,
time
);
return
new
FileResult
(
new
BufferedInputStream
(
new
FileInputStream
(
filename
),
16
*
1024
),
size
,
time
);
}
}
xposedcompat/src/main/java/de/robv/android/xposed/services/FileResult.java
0 → 100644
View file @
6f69e00a
package
de
.
robv
.
android
.
xposed
.
services
;
import
java.io.InputStream
;
/**
* Holder for the result of a {@link BaseService#readFile} or {@link BaseService#statFile} call.
*/
public
final
class
FileResult
{
/** File content, might be {@code null} if the file wasn't read. */
public
final
byte
[]
content
;
/** File input stream, might be {@code null} if the file wasn't read. */
public
final
InputStream
stream
;
/** File size. */
public
final
long
size
;
/** File last modification time. */
public
final
long
mtime
;
/*package*/
public
FileResult
(
long
size
,
long
mtime
)
{
this
.
content
=
null
;
this
.
stream
=
null
;
this
.
size
=
size
;
this
.
mtime
=
mtime
;
}
/*package*/
public
FileResult
(
byte
[]
content
,
long
size
,
long
mtime
)
{
this
.
content
=
content
;
this
.
stream
=
null
;
this
.
size
=
size
;
this
.
mtime
=
mtime
;
}
/*package*/
public
FileResult
(
InputStream
stream
,
long
size
,
long
mtime
)
{
this
.
content
=
null
;
this
.
stream
=
stream
;
this
.
size
=
size
;
this
.
mtime
=
mtime
;
}
/** @hide */
@Override
public
String
toString
()
{
StringBuilder
sb
=
new
StringBuilder
(
"{"
);
if
(
content
!=
null
)
{
sb
.
append
(
"content.length: "
);
sb
.
append
(
content
.
length
);
sb
.
append
(
", "
);
}
if
(
stream
!=
null
)
{
sb
.
append
(
"stream: "
);
sb
.
append
(
stream
.
toString
());
sb
.
append
(
", "
);
}
sb
.
append
(
"size: "
);
sb
.
append
(
size
);
sb
.
append
(
", mtime: "
);
sb
.
append
(
mtime
);
sb
.
append
(
"}"
);
return
sb
.
toString
();
}
}
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