针对 Xposed hook 密码框的防护

原理

TextWatcher接口为EditText控件的响应字符输入事件提供了3个方法,这三个方法分别为:

  • public void beforeTextChanged(CharSequence s, int start, int count, int after)
  • public void onTextChanged(CharSequence s, int start, int before, int count)
  • public void afterTextChanged(Editable s)

我们通过自定义一个类,实现该接口,然后重写这三个方法,将输入的字符保存到StringBuilder中,而在EditText实际显示的是*号。这样当你hook住EditText.getText()的时候,获取的密码肯定是多个*,而实际的密码保存在StringBuilder中。

步骤

自定义TextWatcher类

public class EditChangedListener implements TextWatcher {

private static final String TAG = "EditChangedListener";
private CharSequence temp;
private EditText editText;
private boolean add = false;
private StringBuilder password = new StringBuilder();
private int mStart;

public EditChangedListener(EditText editText) {
this.editText = editText;
}

//after表示这次新加了几个字符,count表示旧的内容中被修改了几个字符,start表示修改的开始位置
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Log.i(TAG, "beforeTextChanged : " + s + ",start = " + start + ",count = " + count + ",after = " + after);


}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//before 原有内容被修改的数量,count为新增的数量,start为操作位置
if (s.length() == 0) return;
// Log.i(TAG, "onTextChanged : " + s + ",start = " + start + ",count = " + count + ",before = " + before);

if (count == 0 && password.length() > start) {
password.deleteCharAt(start);
Log.i(TAG, "删除了第" + start + "个字符 : " + s.charAt(start - 1));
inputStarForEdit(password.length());
add = false;
return;
}

if (count > 0) {
add = true;
}
}


@Override
public void afterTextChanged(Editable s) {

if (!add || s.length() == 0) return;
int length = s.length();
char c = s.charAt(length - 1);

if (c == '*') return;
password.append(c);
inputStarForEdit(length);
}

private void inputStarForEdit(int length) {

editText.getText().clear();

for (int i = 0; i < length; ++i) {
editText.getText().append("*");
}
Log.i(TAG, "真实的为密码: " + password.toString() + ",伪装的密码为 : " + editText.getText());
}
}

这里面的代码逻辑,一定要理解,核心在这

设置密码框的响应器为上面的类


mPasswordView = (EditText) findViewById(R.id.password);

mPasswordView.addTextChangedListener(new EditChangedListener(mPasswordView));

效果

总结

具体应用到你项目中,可能要考虑的问题比上面的demo要多,但是原理基本差不多,招行银行的策略与上面类似。