-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 399ed88
Showing
16 changed files
with
890 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
*.iml | ||
.gradle | ||
/local.properties | ||
/.idea/ | ||
.DS_Store | ||
/build | ||
*/build | ||
/captures | ||
.externalNativeBuild |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
apply plugin: 'com.android.application' | ||
|
||
android { | ||
compileSdkVersion 25 | ||
buildToolsVersion "25.0.2" | ||
defaultConfig { | ||
applicationId "ga.uabart.xkate" | ||
minSdkVersion 15 | ||
targetSdkVersion 25 | ||
versionCode 1 | ||
versionName "0.0.1" | ||
} | ||
buildTypes { | ||
release { | ||
minifyEnabled false | ||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | ||
} | ||
} | ||
} | ||
|
||
dependencies { | ||
compile fileTree(dir: 'libs', include: ['*.jar']) | ||
provided 'de.robv.android.xposed:api:53' | ||
provided 'de.robv.android.xposed:api:53:sources' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Add project specific ProGuard rules here. | ||
# By default, the flags in this file are appended to flags specified | ||
# in /home/uabart/Android/Sdk/tools/proguard/proguard-android.txt | ||
# You can edit the include path and order by changing the proguardFiles | ||
# directive in build.gradle. | ||
# | ||
# For more details, see | ||
# http://developer.android.com/guide/developing/tools/proguard.html | ||
|
||
# Add any project specific keep options here: | ||
|
||
# If your project uses WebView with JS, uncomment the following | ||
# and specify the fully qualified class name to the JavaScript interface | ||
# class: | ||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||
# public *; | ||
#} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
package="ga.uabart.xkate"> | ||
|
||
<application | ||
android:allowBackup="true" | ||
android:label="xcate" | ||
android:supportsRtl="true"> | ||
|
||
<meta-data | ||
android:name="xposedmodule" | ||
android:value="true" /> | ||
<meta-data | ||
android:name="xposeddescription" | ||
android:value="Some encryption hacks for Kate Mobile" /> | ||
<meta-data | ||
android:name="xposedminversion" | ||
android:value="48" /> | ||
|
||
</application> | ||
|
||
</manifest> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
ga.uabart.xkate.XModule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package ga.uabart.xkate; | ||
|
||
import java.security.spec.KeySpec; | ||
|
||
import javax.crypto.Cipher; | ||
import javax.crypto.SecretKey; | ||
import javax.crypto.SecretKeyFactory; | ||
import javax.crypto.spec.IvParameterSpec; | ||
import javax.crypto.spec.PBEKeySpec; | ||
import javax.crypto.spec.SecretKeySpec; | ||
|
||
class TextCipher { | ||
private static int iterationCount = 1024; | ||
private static byte[] iv = "XKateCustomC0de1".getBytes(); | ||
private static int keyStrength = 256; | ||
private static byte[] salt = "RandomSaltXKate".getBytes(); | ||
private static final String UTF_LOCK = "\uD83D\uDD12\n"; | ||
private static final String UTF_UNLOCK = "\uD83D\uDD13 "; | ||
|
||
private TextCipher() { | ||
throw new IllegalAccessError("Utility class"); | ||
} | ||
|
||
static String decrypt(String data, String passPhrase) { | ||
try { | ||
if (data.startsWith(UTF_LOCK)) { | ||
String encrypted = data.substring(5); | ||
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); | ||
KeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount, keyStrength); | ||
SecretKey tmp = factory.generateSecret(spec); | ||
SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES"); | ||
Cipher cipher = Cipher.getInstance("AES/CFB/ISO10126Padding"); | ||
iv[7] = jBaseZ85.decode(data.substring(3, 5))[0]; | ||
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); | ||
byte[] decryptedData = jBaseZ85.decode(encrypted); | ||
byte[] utf8 = cipher.doFinal(decryptedData); | ||
return UTF_UNLOCK + new String(utf8, "UTF8"); | ||
} else { | ||
return data; | ||
} | ||
} catch (Exception ignored) { | ||
return null; | ||
} | ||
} | ||
|
||
static String encrypt(String data, String passPhrase) { | ||
try { | ||
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); | ||
KeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount, keyStrength); | ||
SecretKey tmp = factory.generateSecret(spec); | ||
SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES"); | ||
Cipher cipher = Cipher.getInstance("AES/CFB/ISO10126Padding"); | ||
byte[] code = new byte[1]; | ||
code[0] = (byte) (Math.random() * 255); | ||
iv[7] = code[0]; | ||
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); | ||
byte[] utf8EncryptedData = cipher.doFinal(data.getBytes()); | ||
return UTF_LOCK + jBaseZ85.encode(code) + jBaseZ85.encode(utf8EncryptedData); | ||
} catch (Exception ignored) { | ||
return null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
package ga.uabart.xkate; | ||
|
||
import android.app.Activity; | ||
import android.app.AlertDialog; | ||
import android.content.DialogInterface; | ||
import android.content.SharedPreferences; | ||
import android.os.Bundle; | ||
import android.view.LayoutInflater; | ||
import android.view.View; | ||
import android.view.ViewGroup; | ||
import android.widget.EditText; | ||
|
||
import de.robv.android.xposed.IXposedHookInitPackageResources; | ||
import de.robv.android.xposed.IXposedHookLoadPackage; | ||
import de.robv.android.xposed.IXposedHookZygoteInit; | ||
import de.robv.android.xposed.XC_MethodHook; | ||
import de.robv.android.xposed.XC_MethodReplacement; | ||
import de.robv.android.xposed.XposedBridge; | ||
import de.robv.android.xposed.XposedHelpers; | ||
import de.robv.android.xposed.callbacks.XC_InitPackageResources; | ||
import de.robv.android.xposed.callbacks.XC_LoadPackage; | ||
|
||
import static android.content.Context.MODE_PRIVATE; | ||
|
||
|
||
public class XModule implements IXposedHookInitPackageResources, IXposedHookLoadPackage, IXposedHookZygoteInit { | ||
|
||
private Activity activity = null; | ||
private long chatId; | ||
private long groupId; | ||
private long messageUid; | ||
|
||
private String generateName(long messageUid, long chatId, long groupId) { | ||
if (messageUid != 0) { | ||
return "U" + messageUid; | ||
} else if (chatId != 0) { | ||
return "C" + chatId; | ||
} else if (groupId != 0) { | ||
return "G" + groupId; | ||
} | ||
return null; | ||
} | ||
|
||
private String getPass(String user, Activity activity) { | ||
if (user != null) { | ||
SharedPreferences prefs = activity.getSharedPreferences("XKate", MODE_PRIVATE); | ||
String pass = prefs.getString(user, null); | ||
if ("".equals(pass)) { | ||
pass = null; | ||
} | ||
return pass; | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
private String getPassword(long messageUid, long chatId, long groupId, Activity activity) { | ||
return getPass(generateName(messageUid, chatId, groupId), activity); | ||
} | ||
|
||
@Override | ||
public void handleInitPackageResources(XC_InitPackageResources.InitPackageResourcesParam resparam) throws Throwable { | ||
// maybe this will be needed in future | ||
} | ||
|
||
@Override | ||
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam param) throws Throwable { | ||
|
||
if ("com.perm.kate".equals(param.packageName)) { | ||
|
||
try { | ||
|
||
final Class<?> messageThreadFragmentClass = XposedHelpers.findClass("com.perm.kate.MessageThreadFragment", param.classLoader); | ||
|
||
// Button | ||
|
||
XposedHelpers.findAndHookMethod(messageThreadFragmentClass, "onCreateView", | ||
LayoutInflater.class, ViewGroup.class, Bundle.class, new XC_MethodHook() { | ||
|
||
@Override | ||
protected void afterHookedMethod(final MethodHookParam param) throws Throwable { | ||
View sendButton = (View) XposedHelpers.getObjectField(param.thisObject, "sendButton"); | ||
activity = (Activity) XposedHelpers.callMethod(param.thisObject, "getActivity"); | ||
messageUid = XposedHelpers.getLongField(param.thisObject, "message_uid"); // long | ||
chatId = XposedHelpers.getLongField(param.thisObject, "chat_id"); // long | ||
groupId = XposedHelpers.getLongField(param.thisObject, "group_id"); // long | ||
final String userName = generateName(messageUid, chatId, groupId); | ||
sendButton.setOnLongClickListener(new View.OnLongClickListener() { | ||
@Override | ||
public boolean onLongClick(View v) { | ||
showDialogInputKey(userName, activity); | ||
return true; | ||
} | ||
} | ||
); | ||
|
||
} | ||
}); | ||
|
||
// Sender | ||
|
||
XposedHelpers.findAndHookMethod(messageThreadFragmentClass, "sendClicked", new XC_MethodReplacement() { | ||
@Override | ||
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { | ||
|
||
EditText etNewMessage = (EditText) XposedHelpers.getObjectField(param.thisObject, "et_new_message"); // EditText | ||
Object attachments = XposedHelpers.getStaticObjectField(messageThreadFragmentClass, "attachments"); // ArrayList<String> | ||
Object attachmentsObjects = XposedHelpers.getStaticObjectField(messageThreadFragmentClass, "attachments_objects"); // ArrayList<Object> | ||
Object queue = XposedHelpers.getObjectField(param.thisObject, "queue"); // MessageSendQueue | ||
|
||
String password; | ||
password = getPassword(messageUid, chatId, groupId, activity); | ||
|
||
if (password != null) { | ||
String encryptedMsg = TextCipher.encrypt(etNewMessage.getText().toString(), password); | ||
if (encryptedMsg != null) { | ||
etNewMessage.setText(encryptedMsg); | ||
} | ||
} | ||
|
||
XposedHelpers.callMethod(queue, "add", etNewMessage.getText().toString(), messageUid, | ||
chatId, attachments, attachmentsObjects, null, groupId); | ||
|
||
XposedHelpers.callMethod(param.thisObject, "clearNewMessageEditText"); | ||
XposedHelpers.callMethod(param.thisObject, "requeryOnUiThread"); | ||
|
||
return null; | ||
} | ||
}); | ||
|
||
// Receiver | ||
|
||
final Class<?> messageAdapterCoreClass = XposedHelpers.findClass("com.perm.kate.MessageAdapterCore", param.classLoader); | ||
final Class<?> userClass = XposedHelpers.findClass("com.perm.kate.api.User", param.classLoader); | ||
final Class<?> messageClass = XposedHelpers.findClass("com.perm.kate.api.Message", param.classLoader); | ||
final Class<?> userCacheClass = XposedHelpers.findClass("com.perm.utils.UserCache", param.classLoader); | ||
|
||
XposedHelpers.findAndHookMethod(messageAdapterCoreClass, "displayMessage", | ||
userClass, messageClass, userClass, View.class, userCacheClass, new XC_MethodHook() { | ||
@Override | ||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable { | ||
String body = (String) XposedHelpers.getObjectField(param.args[1], "body"); | ||
String password; | ||
password = getPassword(messageUid, chatId, groupId, activity); | ||
if (password != null) { | ||
String decrypted = TextCipher.decrypt(body, password); | ||
if (decrypted != null){ | ||
body = decrypted; | ||
} | ||
} | ||
XposedHelpers.setObjectField(param.args[1], "body", body); | ||
} | ||
}); | ||
|
||
} catch (Exception e) { | ||
XposedBridge.log(e); | ||
} | ||
|
||
|
||
} | ||
|
||
} | ||
|
||
@Override | ||
public void initZygote(StartupParam startupParam) throws Throwable { | ||
// maybe this will be needed in future | ||
} | ||
|
||
private void savePass(String user, String password, Activity activity) { | ||
SharedPreferences.Editor editor = activity.getSharedPreferences("XKate", MODE_PRIVATE).edit(); | ||
editor.putString(user, password); | ||
editor.commit(); | ||
|
||
} | ||
|
||
private void showDialogInputKey(final String userName, final Activity activity) { | ||
final EditText edittext = new EditText(activity); | ||
edittext.setText(getPass(userName, activity)); | ||
|
||
AlertDialog alertDialog = new AlertDialog.Builder(activity).create(); | ||
alertDialog.setMessage("Input password"); | ||
alertDialog.setView(edittext); | ||
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "Save password", | ||
new DialogInterface.OnClickListener() { | ||
public void onClick(DialogInterface dialog, int which) { | ||
|
||
String password = edittext.getText().toString(); | ||
|
||
savePass(userName, password, activity); | ||
dialog.dismiss(); | ||
} | ||
}); | ||
alertDialog.show(); | ||
} | ||
|
||
} |
Oops, something went wrong.