Future proof to allow spoofing all properties

- remove backwards compatibility cruft for all deprecated fields except FIRST_API_LEVEL (for now)
- iterate through all entries with . or * (see next) to allow modifying any matching DroidGuard-checked system property
- allow leading * wildcard to match multiple system properties in one json entry
- add logging levels using VERBOSE_LOGS as the last json entry, with values of 0, 1, 2, 3 or 100
- spoof sys.usb.state to mtp for DroidGuard by default to hide USB Debugging

Co-authored-by: arda99 <arda99@noreply.xdaforums.com>
This commit is contained in:
osm0sis
2023-12-21 01:41:37 -04:00
parent 18ecfd6e32
commit dc10cae2b6
2 changed files with 71 additions and 98 deletions

View File

@@ -13,7 +13,9 @@
#define JSON_FILE_PATH "/data/adb/modules/playintegrityfix/pif.json" #define JSON_FILE_PATH "/data/adb/modules/playintegrityfix/pif.json"
#define CUSTOM_JSON_FILE_PATH "/data/adb/modules/playintegrityfix/custom.pif.json" #define CUSTOM_JSON_FILE_PATH "/data/adb/modules/playintegrityfix/custom.pif.json"
static std::string FIRST_API_LEVEL, SECURITY_PATCH, BUILD_ID, VNDK_VERSION; static int VERBOSE_LOGS = 0;
static std::map<std::string, std::string> jsonProps;
typedef void (*T_Callback)(void *, const char *, const char *, uint32_t); typedef void (*T_Callback)(void *, const char *, const char *, uint32_t);
@@ -21,44 +23,34 @@ static std::map<void *, T_Callback> callbacks;
static void modify_callback(void *cookie, const char *name, const char *value, uint32_t serial) { static void modify_callback(void *cookie, const char *name, const char *value, uint32_t serial) {
if (cookie == nullptr || name == nullptr || value == nullptr || if (cookie == nullptr || name == nullptr || value == nullptr || !callbacks.contains(cookie)) return;
!callbacks.contains(cookie))
return;
std::string_view prop(name); const char *oldValue = value;
if (prop.ends_with("api_level")) { std::string prop(name);
if (FIRST_API_LEVEL.empty()) {
LOGD("FIRST_API_LEVEL is empty, ignoring it..."); // Spoof specific property values
return; if (prop == "sys.usb.state") {
} else { value = "mtp";
value = FIRST_API_LEVEL.c_str(); }
if (jsonProps.count(prop)) {
// Exact property match
value = jsonProps[prop].c_str();
} else {
// Leading * wildcard property match
for (const auto &p: jsonProps) {
if (p.first.starts_with("*") && prop.ends_with(p.first.substr(1))) {
value = p.second.c_str();
break;
}
} }
LOGD("[%s] -> %s", name, value); }
} else if (prop.ends_with("security_patch")) {
if (SECURITY_PATCH.empty()) { if (oldValue == value) {
LOGD("SECURITY_PATCH is empty, ignoring it..."); if (VERBOSE_LOGS > 99) LOGD("[%s]: %s (unchanged)", name, value);
return; } else {
} else { LOGD("[%s]: %s -> %s", name, oldValue, value);
value = SECURITY_PATCH.c_str();
}
LOGD("[%s] -> %s", name, value);
} else if (prop == "ro.build.id") {
if (BUILD_ID.empty()) {
LOGD("BUILD_ID is empty, ignoring it...");
return;
} else {
value = BUILD_ID.c_str();
}
LOGD("[%s] -> %s", name, value);
} else if (prop.ends_with("vndk.version")) {
if (VNDK_VERSION.empty()) {
LOGD("VNDK_VERSION is empty, ignoring it...");
return;
} else {
value = VNDK_VERSION.c_str();
}
LOGD("[%s] -> %s", name, value);
} }
return callbacks[cookie](cookie, name, value, serial); return callbacks[cookie](cookie, name, value, serial);
@@ -186,75 +178,53 @@ private:
void readJson() { void readJson() {
LOGD("JSON contains %d keys!", static_cast<int>(json.size())); LOGD("JSON contains %d keys!", static_cast<int>(json.size()));
// Direct property spoofing workarounds if (json.contains("VERBOSE_LOGS")) {
if (json.contains("ID")) { if (!json["VERBOSE_LOGS"].is_null() && json["VERBOSE_LOGS"].is_string()) {
if (json["ID"].is_null()) { VERBOSE_LOGS = stoi(json["VERBOSE_LOGS"].get<std::string>());
LOGD("Key ID is null!"); if (VERBOSE_LOGS > 0) LOGD("Verbose logging (level %d) enabled!", VERBOSE_LOGS);
} else if (json["ID"].is_string()) {
BUILD_ID = json["ID"].get<std::string>();
} else { } else {
LOGD("Error parsing ID!"); LOGD("Error parsing VERBOSE_LOGS!");
} }
} else {
LOGD("Key ID doesn't exist in JSON file!");
}
if (json.contains("SECURITY_PATCH")) {
if (json["SECURITY_PATCH"].is_null()) {
LOGD("Key SECURITY_PATCH is null!");
} else if (json["SECURITY_PATCH"].is_string()) {
SECURITY_PATCH = json["SECURITY_PATCH"].get<std::string>();
} else {
LOGD("Error parsing SECURITY_PATCH!");
}
} else {
LOGD("Key SECURITY_PATCH doesn't exist in JSON file!");
}
if (json.contains("DEVICE_INITIAL_SDK_INT")) {
if (json["DEVICE_INITIAL_SDK_INT"].is_null()) {
LOGD("Key DEVICE_INITIAL_SDK_INT is null!");
} else if (json["DEVICE_INITIAL_SDK_INT"].is_string()) {
FIRST_API_LEVEL = json["DEVICE_INITIAL_SDK_INT"].get<std::string>();
} else {
LOGD("Error parsing DEVICE_INITIAL_SDK_INT!");
}
} else {
LOGD("Key DEVICE_INITIAL_SDK_INT doesn't exist in JSON file!");
} }
// Backwards compatibility for chiteroman's alternate API naming // Backwards compatibility for chiteroman's alternate API naming
if (json.contains("BUILD_ID")) {
if (json["BUILD_ID"].is_null()) {
LOGD("Key BUILD_ID is null!");
} else if (json["BUILD_ID"].is_string()) {
BUILD_ID = json["BUILD_ID"].get<std::string>();
} else {
LOGD("Error parsing BUILD_ID!");
}
} else {
LOGD("Key BUILD_ID doesn't exist in JSON file!");
}
if (json.contains("FIRST_API_LEVEL")) { if (json.contains("FIRST_API_LEVEL")) {
if (json["FIRST_API_LEVEL"].is_null()) { if (!json["FIRST_API_LEVEL"].is_null() && json["FIRST_API_LEVEL"].is_string()) {
LOGD("Key FIRST_API_LEVEL is null!"); if (json["FIRST_API_LEVEL"] == "") {
} else if (json["FIRST_API_LEVEL"].is_string()) { LOGD("FIRST_API_LEVEL is empty, skipping...");
FIRST_API_LEVEL = json["FIRST_API_LEVEL"].get<std::string>(); json.erase("FIRST_API_LEVEL");
} else {
LOGD("Using deprecated 'FIRST_API_LEVEL' field for '*.first_api_level' direct property spoofing...");
if (VERBOSE_LOGS > 0) LOGD("Adding '*.first_api_level' to properties list");
jsonProps["*.first_api_level"] = json["FIRST_API_LEVEL"].get<std::string>();
}
} else { } else {
LOGD("Error parsing FIRST_API_LEVEL!"); LOGD("Error parsing FIRST_API_LEVEL!");
json.erase("FIRST_API_LEVEL");
} }
} else {
LOGD("Key FIRST_API_LEVEL doesn't exist in JSON file!");
} }
if (json.contains("VNDK_VERSION")) {
if (json["VNDK_VERSION"].is_null()) { std::vector<std::string> eraseKeys;
LOGD("Key VNDK_VERSION is null!"); for (auto &jsonList: json.items()) {
} else if (json["VNDK_VERSION"].is_string()) { if (VERBOSE_LOGS > 1) LOGD("Parsing %s...", jsonList.key().c_str());
VNDK_VERSION = json["VNDK_VERSION"].get<std::string>(); if (jsonList.key().find_first_of("*.") != std::string::npos) {
} else { // Name contains . or * (wildcard) so assume real property name
LOGD("Error parsing VNDK_VERSION!"); if (!jsonList.value().is_null() && jsonList.value().is_string()) {
if (jsonList.value() == "") {
LOGD("%s is empty, skipping...", jsonList.key().c_str());
} else {
if (VERBOSE_LOGS > 0) LOGD("Adding '%s' to properties list", jsonList.key().c_str());
jsonProps[jsonList.key()] = jsonList.value();
}
} else {
LOGD("Error parsing %s!", jsonList.key().c_str());
}
eraseKeys.push_back(jsonList.key());
} }
json.erase("VNDK_VERSION"); // Doesn't exist in Build or Build.VERSION }
} else { // Remove properties from parsed JSON
LOGD("Key VNDK_VERSION doesn't exist in JSON file!"); for (auto key: eraseKeys) {
if (json.contains(key)) json.erase(key);
} }
} }

View File

@@ -16,6 +16,8 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
public final class EntryPoint { public final class EntryPoint {
private static Integer verboseLogs = 0;
private static final Map<String, String> map = new HashMap<>(); private static final Map<String, String> map = new HashMap<>();
public static void init() { public static void init() {
@@ -66,9 +68,10 @@ public final class EntryPoint {
static void spoofDevice() { static void spoofDevice() {
for (String key : map.keySet()) { for (String key : map.keySet()) {
// Verbose logging if VERBOSE_LOGS with level number is last entry
if (key.equals("VERBOSE_LOGS")) {
verboseLogs = Integer.parseInt(map.get("VERBOSE_LOGS"));
// Backwards compatibility for chiteroman's alternate API naming // Backwards compatibility for chiteroman's alternate API naming
if (key.equals("BUILD_ID")) {
setField("ID", map.get("BUILD_ID"));
} else if (key.equals("FIRST_API_LEVEL")) { } else if (key.equals("FIRST_API_LEVEL")) {
setField("DEVICE_INITIAL_SDK_INT", map.get("FIRST_API_LEVEL")); setField("DEVICE_INITIAL_SDK_INT", map.get("FIRST_API_LEVEL"));
} else { } else {
@@ -100,7 +103,7 @@ public final class EntryPoint {
} else if (classContainsField(Build.VERSION.class, name)) { } else if (classContainsField(Build.VERSION.class, name)) {
field = Build.VERSION.class.getDeclaredField(name); field = Build.VERSION.class.getDeclaredField(name);
} else { } else {
LOG(String.format("Couldn't determine '%s' class name", name)); if (verboseLogs > 1) LOG(String.format("Couldn't determine '%s' class name", name));
return; return;
} }
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {
@@ -115,7 +118,7 @@ public final class EntryPoint {
return; return;
} }
if (value.equals(oldValue)) { if (value.equals(oldValue)) {
LOG(String.format("[%s]: already '%s', skipping...", name, value)); if (verboseLogs > 2) LOG(String.format("[%s]: %s (unchanged)", name, value));
return; return;
} }
Class<?> fieldType = field.getType(); Class<?> fieldType = field.getType();