Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added User-Agent to requests, fixed access to GV preferences, and fixed Intents being spoofed #2

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />

<application
android:allowBackup="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,17 @@ private void hookBroadcastPermissionCheck(final LoadPackageParam lpparam) {
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// Index of the callingUid argument
final int ARGUMENT_INDEX_CALLING_UID = 14;
final int ARGUMENT_INDEX_INTENT = 2;
int callingUid = (int) param.args[ARGUMENT_INDEX_CALLING_UID];
Intent intent = (Intent) param.args[ARGUMENT_INDEX_INTENT];

// Get our UID
int appUid = AndroidAppHelper.currentApplication().getPackageManager()
.getApplicationInfo(XVOICE_PLUS_PACKAGE, PackageManager.GET_META_DATA).uid;
// If the broadcast is from us
if ((boolean) callStaticMethod(UserHandle.class, "isSameApp", callingUid, appUid)) {
if (intent.getAction().startsWith("android.provider.Telephony") &&
(boolean) callStaticMethod(UserHandle.class, "isSameApp", callingUid, appUid)) {

Log.d(TAG, "Hooking broadcast permissions: Overriding callingUid "
+ callingUid + " with Process.PHONE_UID (UID 1001)");
// Spoof the broadcast as if it's coming from PHONE_UID, so the system will let it through
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.koushikdutta.ion.Ion;

import java.util.ArrayList;
Expand All @@ -35,7 +37,7 @@ public class GoogleVoiceManager {

private final Context mContext;
private String mRnrse = null;

public GoogleVoiceManager(Context context) {
mContext = context;
}
Expand All @@ -47,15 +49,15 @@ private SharedPreferences getSettings() {
private String getAccount() {
return getSettings().getString("account", null);
}

public boolean refreshAuth() {
return getRnrse(true) != null;
}

private void saveRnrse(String rnrse) {
getSettings().edit().putString("_rnr_se", rnrse).apply();
}

private String getRnrse() {
return getRnrse(false);
}
Expand All @@ -70,7 +72,7 @@ private String getRnrse(boolean force) {
}
return mRnrse;
}

/**
* Fetch the weirdo opaque token Google Voice needs...
*
Expand All @@ -81,10 +83,13 @@ private String getRnrse(boolean force) {
private String fetchRnrSe() throws Exception {
final String authToken = getAuthToken();
JsonObject userInfo = Ion.with(mContext).load("https://www.google.com/voice/request/user")
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36")
.setHeader("Authorization", "GoogleLogin auth=" + authToken)
.asJsonObject()
.get();

Log.d(TAG, "fetchRnrSe: "+userInfo.getAsString());

String rnrse = userInfo.get("r").getAsString();
verifySmsForwarding(userInfo, authToken, rnrse);

Expand All @@ -106,6 +111,7 @@ private void verifySmsForwarding(JsonObject userInfo, String authToken, String r
if (PhoneNumberUtils.compare(number, phone.get("phoneNumber").getAsString())) {
Log.i(TAG, "Disabling SMS forwarding to phone.");
Ion.with(mContext).load("https://www.google.com/voice/settings/editForwardingSms/")
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36")
.setHeader("Authorization", "GoogleLogin auth=" + authToken)
.setBodyParameter("phoneId", entry.getKey())
.setBodyParameter("enabled", "0")
Expand All @@ -129,12 +135,12 @@ private static Bundle getAccountBundle(Context context, String account) throws E
}
return null;
}

private String getAuthToken() throws Exception {
Bundle bundle = getAccountBundle(mContext, getAccount());
return bundle.getString(AccountManager.KEY_AUTHTOKEN);
}

/**
* Hit the Google Voice API to send a text
*
Expand All @@ -145,21 +151,25 @@ private String getAuthToken() throws Exception {
*/
public void sendGvMessage(String number, String text) throws Exception {
final String authToken = getAuthToken();
JsonObject json = Ion.with(mContext).load("https://www.google.com/voice/sms/send/")
String response = Ion.with(mContext).load("https://www.google.com/voice/sms/send/")
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36")
.onHeaders(new GvHeadersCallback(mContext, authToken))
.setHeader("Authorization", "GoogleLogin auth=" + authToken)
.setBodyParameter("phoneNumber", number)
.setBodyParameter("sendErrorSms", "0")
.setBodyParameter("text", text)
.setBodyParameter("_rnr_se", getRnrse())
.asJsonObject()
.asString()
.get();

if (!json.get("ok").getAsBoolean()) {
throw new Exception(json.toString());
} else {
Log.d(TAG, "Message sent with Google Voice successfully");
try {
boolean isOk = new JsonParser().parse(response).getAsJsonObject().get("ok").getAsBoolean();
if (!isOk){
throw new Exception(response);
}
} catch (JsonParseException e){
throw new Exception(response);
}
Log.d(TAG, "Message sent with Google Voice successfully");
}

/**
Expand All @@ -174,6 +184,7 @@ public void markGvMessageRead(String id, int read) throws Exception {
final String authToken = getAuthToken();
Log.d(TAG, "Marking messsage " + id + " as read");
Ion.with(mContext).load("https://www.google.com/voice/inbox/mark/")
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36")
.onHeaders(new GvHeadersCallback(mContext, authToken))
.setHeader("Authorization", "GoogleLogin auth=" + authToken)
.setBodyParameter("messages", id)
Expand All @@ -199,16 +210,19 @@ public List<Conversation> retrieveMessages() throws Exception {

// tokens!
final String authToken = getAuthToken();
try {
return Ion.with(mContext).load("https://www.google.com/voice/request/messages")
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36")
.onHeaders(new GvHeadersCallback(mContext, authToken))
.setHeader("Authorization", "GoogleLogin auth=" + authToken)
.as(Payload.class).get().conversations;

Payload payload = Ion.with(mContext).load("https://www.google.com/voice/request/messages")
.onHeaders(new GvHeadersCallback(mContext, authToken))
.setHeader("Authorization", "GoogleLogin auth=" + authToken)
.as(Payload.class)
.get();

return payload.conversations;
} catch (Throwable e){
Log.e(TAG, "Unable to retrieve messages: "+e.getMessage(), e);
throw e;
}
}

public static void invalidateToken(final Context context, final String account) {
if (account == null) return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ private boolean verifyUserHash(final String userHash) {
}

final SharedPreferences gvSharedPrefs = new XSharedPreferences(LEGACY_GOOGLE_VOICE_PACKAGE);
final String registeredAccounts = gvSharedPrefs.getString("registered_accounts", null);
final String registeredAccounts = gvSharedPrefs.getString("accounts", null);
if (registeredAccounts == null) {
Log.e(TAG, "Error accessing registered_accounts from GV SharedPreferences");
return false;
Expand Down