淘口令效果与粘贴板攻击防护
写在前面的话 – 关于剪切板安全性的思考不感兴趣的可以跳过这段废话
- 早在18年360互联网中心首次监听到一类木马病毒,该类病毒不断的监听剪切板,判断是否为比特币等数字货币地址,如果是数字货币地址则将其替换成自己的地址从而实施盗窃,其实这种漏洞早在16年Telegram就暴露出应用将剪切板文本写到本地的情况,而剪切板的安全一直不为大家所关注,因此应用本身如果需要较高的防护等级的话剪切板的安全就容易变成一个薄弱环节.
如何防护
笔者从两方面考虑防护
- 如何防止写入到剪贴板的数据被其他应用读取并篡改
- 如何简单快速的进行防护而不对现有项目做过多改动
本代码局限性
由于手头并没有三星手机,且三星自己实现了一套复制粘贴的service,所以下述代码没有对三星相关品牌做兼容.诸位看官可自行实现.
直接上代码
-
下面的代码主要是对剪贴板的hook,在应用中如果一旦使用了剪贴板会自动的走到我们hook的地方,在此我们可以将剪贴的数据进行加密处理,或者实现淘口令的功能.具体的各位可以根据自身的业务需求实现.完整代码各位可以下载github代码,如果有用请给个star支持一下.
package com.lhc.hook.myapplication.hook; import android.content.ClipData; import android.content.Context; import android.os.IBinder; import android.text.TextUtils; import android.util.Log; import com.lhc.hook.myapplication.hookutil.ServiceHook; import com.lhc.hook.myapplication.hookutil.ServiceManager; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ClipboardHook { private static final String TAG = ClipboardHook.class.getSimpleName(); private static Context mContext; public static void hookService(Context context) { mContext = context; IBinder clipboardService = ServiceManager.getService(Context.CLIPBOARD_SERVICE); String IClipboard = "android.content.IClipboard"; if (clipboardService != null) { try { IBinder hookClipboardService = (IBinder) Proxy.newProxyInstance(IBinder.class.getClassLoader(), new Class[]{IBinder.class}, new ServiceHook(clipboardService, IClipboard, true, new ClipboardHookHandler())); ServiceManager.setService(Context.CLIPBOARD_SERVICE, hookClipboardService); } catch (Exception e) { e.printStackTrace(); } } else { Log.e(TAG, "ClipboardService hook failed!"); } } /** * 其实在android的粘贴板中有一个ClipData.getDescription()方法, * 各位甚至可以根据自己的需要对粘贴的内容进行描述,在此处就不赘述. */ public static class ClipboardHookHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String name = method.getName(); if ("setPrimaryClip".equals(name)) {//复制 hookSetPrimaryClip(args); return method.invoke(proxy, args); } else if ("getPrimaryClip".equals(name)|| "getUserPrimaryClip".equal(name)) {//粘贴 Object call = method.invoke(proxy, args); call = hookGetPrimaryClip(call); return call; } return method.invoke(proxy, args); } /** * hook粘贴,可对数据加密,或实现淘口令效果 */ private void hookSetPrimaryClip(Object[] args) { for (int i = 0; i < args.length; i++) { if (args[i] instanceof ClipData) { ClipData clipData = (ClipData) args[i]; if (clipData != null && clipData.getItemAt(0) != null && !TextUtils.isEmpty(clipData.getItemAt(0).getText())) { // TODO: 2020/11/19 0019 对数据进行解密,实现淘口令功能 ClipData data = new ClipData(clipData.getDescription(), new ClipData.Item("数据已经加密")); args[i] = data; } } } } /** * hook 粘贴 对数据进行解密或实淘口令解密功能 */ private Object hookGetPrimaryClip(Object call) { if (call instanceof ClipData) { ClipData encodeData = (ClipData) call; if (encodeData != null && encodeData.getItemAt(0) != null && !TextUtils.isEmpty(encodeData.getItemAt(0).getText())) { // TODO: 2020/11/19 0019 对数据进行解密对淘口令解密 ClipData decodeData = new ClipData(encodeData.getDescription(), new ClipData.Item("数据已经解密")); call = decodeData; } } return call; } } }