首先需要知道微信語音通話過程中的字節(jié)是怎么輸入的,可以參考之前的微信保存并發(fā)送指定語音 那篇文章,發(fā)現(xiàn)語音通話的過程實(shí)際上跟語音聊天的字節(jié)輸入過程相似,只不過是AudioRecord的read方法在語音通話的全過程中一直不斷地在寫入字節(jié),所以我們的思路就是hook并修改read函數(shù)的寫入過程,同時(shí)為了兼容模擬器,需要hook AudioRecord的其他函數(shù)以維持整個(gè)過程。
核心代碼如下
// hook read 方法,當(dāng)發(fā)送指定語音時(shí)需要hook這個(gè)函數(shù)需要在before操作,當(dāng)錄入自己的語音文件時(shí)需要在after操作
XposedHelpers.findAndHookMethod("android.media.AudioRecord",
loadPackageParam.classLoader, "read", byte[].class, int.class,
int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Log.i(TAG, "AudioRecord#read beforeHookedMethod: ");
int seed = new Random().nextInt(10);
Log.i(TAG, "beforeHookedMethod: seed -- "+seed);
// 真機(jī)得4以上才有,模擬器1就可以了
if(seed > 1){
param.setResult(0);
}else {
byte[] buffer = (byte[]) param.args[0];
int off = (int) param.args[1];
int size = (int) param.args[2];
int min = Math.min(buffer.length - off, size);
byte[] bytes = new byte[min];
// 賦予隨機(jī)值
new Random().nextBytes(bytes);
for (int i = 0;i < bytes.length; i++) {
buffer[off + i] = bytes[i];
}
param.setResult(bytes.length);
}
}
});
其他的hook相關(guān)函數(shù)是為了維持整個(gè)通話過程
XposedHelpers.findAndHookConstructor("android.media.AudioRecord", loadPackageParam.classLoader,
int.class, int.class, int.class, int.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Log.i(TAG, "AudioRecord # 構(gòu)造方法beforeHookedMethod: ");
int audioSource = (int)param.args[0];
int sampleRateInHz = (int)param.args[1];
int channelConfig = (int)param.args[2];
int audioFormat = (int)param.args[3];
int bufferSizeInBytes = (int)param.args[4];
Log.i(TAG, "beforeHookedMethod: 構(gòu)造方法 audioSource -- "+audioSource); // 1
Log.i(TAG, "beforeHookedMethod: 構(gòu)造方法 sampleRateInHz -- "+sampleRateInHz);// 16000
Log.i(TAG, "beforeHookedMethod: 構(gòu)造方法 channelConfig -- "+channelConfig); // 2
Log.i(TAG, "beforeHookedMethod: 構(gòu)造方法 audioFormat -- "+audioFormat); // 2
Log.i(TAG, "beforeHookedMethod: 構(gòu)造方法 bufferSizeInBytes -- "+bufferSizeInBytes);// 12800 模擬器15360
// 修改
param.args[0] = 1;
param.args[1] = 16000;
param.args[2] = 2;
param.args[3] = 2;
param.args[4] = 12800;
}
});
XposedHelpers.findAndHookMethod("android.media.AudioRecord",
loadPackageParam.classLoader, "startRecording", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Log.i(TAG, "AudioRecord#startRecording beforeHookedMethod: ");
AudioRecord record = (AudioRecord) param.thisObject;
int flag = -1;
// 將錄音狀態(tài)置為RECORDSTATE_RECORDING狀態(tài)
if (mRecordingFlagMap.get(record) == null || mRecordingFlagMap.get(record) != AudioRecord.RECORDSTATE_RECORDING) {
flag = AudioRecord.RECORDSTATE_RECORDING;
mRecordingFlagMap.put(record, flag);
}
// 打斷微信的錄音過程
Object o = new Object();
param.setResult(o);
}
});
// hook getRecordingState 方法,當(dāng)發(fā)送指定語音時(shí)需要hook這個(gè)函數(shù)
XposedHelpers.findAndHookMethod("android.media.AudioRecord",
loadPackageParam.classLoader, "getRecordingState", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Log.i(TAG, "AudioRecord#getRecordingState beforeHookedMethod: ");
// 獲取我們?cè)趕tartRecording和Stop中維護(hù)的state的值
AudioRecord record = (AudioRecord) param.thisObject;
int res = mRecordingFlagMap.get(record) == null ? AudioRecord.RECORDSTATE_STOPPED : mRecordingFlagMap.get(record);
// 將返回值給微信
param.setResult(res);
// 清理mRecordFlagMap
mRecordingFlagMap.remove(record);
}
});
// hook stop 方法,當(dāng)發(fā)送指定語音時(shí)需要hook這個(gè)函數(shù)需要在before操作,當(dāng)錄入自己的語音文件時(shí)需要在after操作
XposedHelpers.findAndHookMethod("android.media.AudioRecord",
loadPackageParam.classLoader, "stop", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Log.i(TAG, "AudioRecord#stop beforeHookedMethod: ");
AudioRecord record = (AudioRecord) param.thisObject;
// 將錄音狀態(tài)設(shè)置為stopped
int flag = -1;
if (mRecordingFlagMap.get(record) == null || mRecordingFlagMap.get(record) != AudioRecord.RECORDSTATE_STOPPED) {
flag = AudioRecord.RECORDSTATE_STOPPED;
mRecordingFlagMap.put(record, flag);
}
// 打斷微信,完成發(fā)送指定的語音文件
Object o = new Object();
param.setResult(o);
}
});
// hook release 方法,當(dāng)發(fā)送指定語音時(shí)需要hook這個(gè)函數(shù),打斷微信
XposedHelpers.findAndHookMethod("android.media.AudioRecord", loadPackageParam.classLoader,
"release", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Log.i(TAG, "AudioRecord#release beforeHookedMethod: ");
Object o = new Object();
param.setResult(o);
}
});