Skip to content

Commit

Permalink
First version of project
Browse files Browse the repository at this point in the history
  • Loading branch information
BondarenkoArtur committed Apr 11, 2017
0 parents commit 399ed88
Show file tree
Hide file tree
Showing 16 changed files with 890 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .gitignore
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 added apk/xkate_v0.0.1.apk
Binary file not shown.
25 changes: 25 additions & 0 deletions app/build.gradle
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'
}
17 changes: 17 additions & 0 deletions app/proguard-rules.pro
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 *;
#}
21 changes: 21 additions & 0 deletions app/src/main/AndroidManifest.xml
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>
1 change: 1 addition & 0 deletions app/src/main/assets/xposed_init
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ga.uabart.xkate.XModule
63 changes: 63 additions & 0 deletions app/src/main/java/ga/uabart/xkate/TextCipher.java
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;
}
}
}
196 changes: 196 additions & 0 deletions app/src/main/java/ga/uabart/xkate/XModule.java
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();
}

}
Loading

0 comments on commit 399ed88

Please sign in to comment.