Xposed开发之短信Hook

我的 Xposed 模块 - 短信验证码提取 中比较关键的就是如何通过 Hook 短信接收过程,并获取短信内容。

在前一篇文章(Android O SMS 接收过程源码分析)已经分析了 Android O 上的短信接收过程。其他版本的源码分析便不再展开,核心思路基本一致,都是通过状态机中不同状态之间的转换,对不同类型的事件消息进行处理,从而完成对新短信的接收、广播等操作。

Xposed 开发中,下面几点很重要:

  • Hook 的方法函数在保证一定会被调用的同时,要尽量保证不是被频繁调用的,能精准打击就不要无差别范围攻击。能达到这一效果,就能很好地提升性能。
  • Hook 的方法尽量保证在不同版本上保持一致,当然也不排除可能因为版本大更迭,方法签名都发生巨大变化,这时就需要针对不同版本来进行不同的 Hook 操作了。

如何做到以上两点,其实就是阅读源码。这是最简单最有效的方法,却又是最困难最麻烦的方法(阅读源码确实麻烦,有时候往往会陷于各种细节之处无法自拔)。

通过阅读和比较从 4.4 到 8.1 系统中关于短信接收源码,可以发现 dispatchIntent() 方法是发送接收到短信过程中最后一道关卡,同时该方法的 Intent intent 参数即为携带了短信数据的参数,所以也能很方便获取短信数据。所以,Hook 短信获取,围绕方法 dispatchIntent() 即可。

编码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
public class SmsHandlerHook implements IHook {

private static final String TELEPHONY_PACKAGE = "com.android.internal.telephony";
private static final String SMS_HANDLER_CLASS = TELEPHONY_PACKAGE + ".InboundSmsHandler";
private static final String SMSCODE_PACKAGE = BuildConfig.APPLICATION_ID;

private Context mModContext;

@Override
public void onLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if ("com.android.phone".equals(lpparam.packageName)) {
XLog.i("SmsCode initializing");
printDeviceInfo();
try {
hookSmsHandler(lpparam);
} catch (Throwable e) {
XLog.e("Failed to hook SmsHandler", e);
throw e;
}
XLog.i("SmsCode initialize completely");
}
}

@SuppressWarnings("deprecation")
private static void printDeviceInfo() {
XLog.i("Phone manufacturer: %s", Build.MANUFACTURER);
XLog.i("Phone model: %s", Build.MODEL);
XLog.i("Android version: %s", Build.VERSION.RELEASE);
int xposedVersion;
try {
xposedVersion = XposedBridge.getXposedVersion();
} catch (Throwable e) {
xposedVersion = XposedBridge.XPOSED_BRIDGE_VERSION;
}
XLog.i("Xposed bridge version: %d", xposedVersion);
XLog.i("SmsCode version: %s (%d)", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE);
}

private void hookSmsHandler(XC_LoadPackage.LoadPackageParam lpparam) {
hookConstructor(lpparam);
hookDispatchIntent(lpparam);
}

private void hookConstructor(XC_LoadPackage.LoadPackageParam lpparam) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
hookConstructor24(lpparam);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
hookConstructor19(lpparam);
}
}

private void hookConstructor24(XC_LoadPackage.LoadPackageParam lpparam) {
XLog.i("Hooking InboundSmsHandler constructor for android v24+");
XposedHelpers.findAndHookConstructor(SMS_HANDLER_CLASS, lpparam.classLoader,
/* name */ String.class,
/* context */ Context.class,
/* storageMonitor */ TELEPHONY_PACKAGE + ".SmsStorageMonitor",
/* phone */ TELEPHONY_PACKAGE + ".Phone",
/* cellBroadcastHandler */ TELEPHONY_PACKAGE + ".CellBroadcastHandler",
new ConstructorHook());
}

private void hookConstructor19(XC_LoadPackage.LoadPackageParam lpparam) {
XLog.i("Hooking InboundSmsHandler constructor for Android v19+");
XposedHelpers.findAndHookConstructor(SMS_HANDLER_CLASS, lpparam.classLoader,
/* name */ String.class,
/* context */ Context.class,
/* storageMonitor */ TELEPHONY_PACKAGE + ".SmsStorageMonitor",
/* phone */ TELEPHONY_PACKAGE + ".PhoneBase",
/* cellBroadcastHandler */ TELEPHONY_PACKAGE + ".CellBroadcastHandler",
new ConstructorHook());
}

private void hookDispatchIntent(XC_LoadPackage.LoadPackageParam lpparam) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
hookDispatchIntent23(lpparam);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
hookDispatchIntent21(lpparam);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
hookDispatchIntent19(lpparam);
}
}

private void hookDispatchIntent19(XC_LoadPackage.LoadPackageParam lpparam) {
XLog.i("Hooking dispatchIntent() for Android v19+");
XposedHelpers.findAndHookMethod(SMS_HANDLER_CLASS, lpparam.classLoader, "dispatchIntent",
/* intent */ Intent.class,
/* permission */ String.class,
/* appOp */ int.class,
/* resultReceiver */ BroadcastReceiver.class,
new DispatchIntentHook(3));
}

private void hookDispatchIntent21(XC_LoadPackage.LoadPackageParam lpparam) {
XLog.i("Hooking dispatchIntent() for Android v21+");
XposedHelpers.findAndHookMethod(SMS_HANDLER_CLASS, lpparam.classLoader, "dispatchIntent",
/* intent */ Intent.class,
/* permission */ String.class,
/* appOp */ int.class,
/* resultReceiver */ BroadcastReceiver.class,
/* user */ UserHandle.class,
new DispatchIntentHook(3));
}

private void hookDispatchIntent23(XC_LoadPackage.LoadPackageParam lpparam) {
XLog.i("Hooking dispatchIntent() for Android v23+");
XposedHelpers.findAndHookMethod(SMS_HANDLER_CLASS, lpparam.classLoader, "dispatchIntent",
/* intent */ Intent.class,
/* permission */ String.class,
/* appOp */ int.class,
/* opts */ Bundle.class,
/* resultReceiver */ BroadcastReceiver.class,
/* user */ UserHandle.class,
new DispatchIntentHook(4));
}

private class ConstructorHook extends XC_MethodHook {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
try {
afterConstructorHandler(param);
} catch (Throwable e) {
XLog.e("Error occurred in constructor hook", e);
throw e;
}
}
}

private void afterConstructorHandler(XC_MethodHook.MethodHookParam param) {
Context context = (Context) param.args[1];
if (mModContext == null) {
mModContext = context;
}
}

private class DispatchIntentHook extends XC_MethodHook {
private final int mReceiverIndex;

DispatchIntentHook(int receiverIndex) {
mReceiverIndex = receiverIndex;
}

@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
try {
beforeDispatchIntentHandler(param, mReceiverIndex);
} catch (Throwable e) {
XLog.e("Error occurred in dispatchIntent() hook, ", e);
throw e;
}
}
}

private void beforeDispatchIntentHandler(XC_MethodHook.MethodHookParam param, int receiverIndex) {
// 此 intent 即为携带短信数据的 intent
Intent intent = (Intent) param.args[0];
String action = intent.getAction();

// 过滤掉 SMS_DELIVER_ACTION 之外的 Action
if (!Telephony.Sms.Intents.SMS_DELIVER_ACTION.equals(action)) {
return;
}

// 发送广播,让接收者处理短信数据
Intent broadcastIntent = new Intent();
broadcastIntent.setComponent(new ComponentName(SMSCODE_PACKAGE, SmsCodeReceiver.class.getName()));
broadcastIntent.putExtra(SmsCodeService.EXTRA_KEY_SMS_INTENT, intent);
mModContext.sendBroadcast(broadcastIntent);
}
}

代码开源在 XposedSmsCode

参考

0%