|
|
@@ -0,0 +1,137 @@
|
|
|
+package com.example;
|
|
|
+
|
|
|
+import android.content.Context;
|
|
|
+import android.content.Intent;
|
|
|
+import android.telephony.PhoneNumberUtils;
|
|
|
+import android.telephony.SmsManager;
|
|
|
+import android.telephony.SubscriptionInfo;
|
|
|
+import android.telephony.SubscriptionManager;
|
|
|
+import android.telephony.TelephonyManager;
|
|
|
+import android.util.Log;
|
|
|
+
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.UnsupportedEncodingException;
|
|
|
+import java.lang.reflect.Method;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Calendar;
|
|
|
+import java.util.GregorianCalendar;
|
|
|
+
|
|
|
+public class RcsHackTool {
|
|
|
+ private static final String TAG = "RcsHackTool";
|
|
|
+
|
|
|
+ // 获取对应的Intent数据
|
|
|
+ public static Intent createSmsIntent(Context context, String number, String body) {
|
|
|
+ TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
|
|
+ SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
|
|
|
+ final int simCount = subscriptionManager.getActiveSubscriptionInfoCountMax();
|
|
|
+ int subId = 0;
|
|
|
+ int slot = 0;
|
|
|
+ for (int i = 0; i < simCount; i++) {
|
|
|
+ SubscriptionInfo subInfo = subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(i);
|
|
|
+ if (subInfo != null) {
|
|
|
+ Log.d(TAG, "subId: " + subInfo.getSubscriptionId());
|
|
|
+ subId = subInfo.getSubscriptionId();
|
|
|
+ slot = subInfo.getSimSlotIndex();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ SmsManager smsManager = SmsManager.getDefault();
|
|
|
+ // 防止短信过长
|
|
|
+ ArrayList<String> messages = smsManager.divideMessage(body);
|
|
|
+ int size = messages.size();
|
|
|
+ byte[][] objArray = new byte[size][];
|
|
|
+ for (int i = 0; i < size; ++i) {
|
|
|
+ byte[] pduu = createFakeSms(number, messages.get(i));
|
|
|
+ objArray[i] = pduu;
|
|
|
+ }
|
|
|
+ Intent intent = new Intent();
|
|
|
+ intent.setAction("android.provider.Telephony.SMS_DELIVER");
|
|
|
+
|
|
|
+ intent.putExtra("android.telephony.extra.SUBSCRIPTION_INDEX", subId);
|
|
|
+ intent.putExtra("messageId", Long.valueOf((int) Math.floor(Math.random() * 100000000)).longValue());
|
|
|
+ intent.putExtra("pdus", objArray);
|
|
|
+ intent.putExtra("format", "3gpp");
|
|
|
+ intent.putExtra("android.telephony.extra.SLOT_INDEX", slot);
|
|
|
+ intent.putExtra("phone", slot);
|
|
|
+ intent.putExtra("subscription", subId);
|
|
|
+ return intent;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建pdu
|
|
|
+ public static byte[] createFakeSms(String sender, String body) {
|
|
|
+ byte[] pdu = null;
|
|
|
+ byte[] scBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD("0000000000");
|
|
|
+ byte[] senderBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(sender);
|
|
|
+ int lsmcs = scBytes.length;
|
|
|
+ // 时间处理,包括年月日时分秒以及时区和夏令时
|
|
|
+ byte[] dateBytes = new byte[7];
|
|
|
+ Calendar calendar = new GregorianCalendar();
|
|
|
+ dateBytes[0] = reverseByte((byte) (calendar.get(Calendar.YEAR)));
|
|
|
+ dateBytes[1] = reverseByte((byte) (calendar.get(Calendar.MONTH) + 1));
|
|
|
+ dateBytes[2] = reverseByte((byte) (calendar.get(Calendar.DAY_OF_MONTH)));
|
|
|
+ dateBytes[3] = reverseByte((byte) (calendar.get(Calendar.HOUR_OF_DAY)));
|
|
|
+ dateBytes[4] = reverseByte((byte) (calendar.get(Calendar.MINUTE)));
|
|
|
+ dateBytes[5] = reverseByte((byte) (calendar.get(Calendar.SECOND)));
|
|
|
+ dateBytes[6] = reverseByte((byte) ((calendar.get(Calendar.ZONE_OFFSET)
|
|
|
+ + calendar.get(Calendar.DST_OFFSET)) / (60 * 1000 * 15)));
|
|
|
+ try {
|
|
|
+ ByteArrayOutputStream bo = new ByteArrayOutputStream();
|
|
|
+ bo.write(lsmcs);// 短信服务中心长度
|
|
|
+ bo.write(scBytes);// 短信服务中心号码
|
|
|
+ bo.write(0x04);
|
|
|
+ bo.write((byte) sender.length());// 发送方号码长度
|
|
|
+ bo.write(senderBytes);// 发送方号码
|
|
|
+ bo.write(0x00);// 协议标示,00为普通GSM,点对点方式
|
|
|
+ try {
|
|
|
+ String className = "com.android.internal.telephony.GsmAlphabet";
|
|
|
+ Class<?> clazz = Class.forName(className);
|
|
|
+ Method method = clazz.getMethod("stringToGsm7BitPacked", new Class[]{String.class});
|
|
|
+ method.setAccessible(true);
|
|
|
+ byte[] bodybytes = (byte[]) method.invoke(null, body);
|
|
|
+
|
|
|
+ bo.write(0x00); // encoding: 0 for default 7bit
|
|
|
+ bo.write(dateBytes);
|
|
|
+ bo.write(bodybytes);
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 下面是UCS-2编码的处理,中文短信就需要用此种方式
|
|
|
+ byte[] bodyBytes = encodeUCS2(body, null);
|
|
|
+ bo.write(0x08); // encoding: 8 for UCS-2
|
|
|
+ bo.write(dateBytes);
|
|
|
+ bo.write(bodyBytes);// 其中encodeUCS2是从系统中复制过来的,并不是我写的
|
|
|
+ // 源码具体位置在
|
|
|
+ // frameworks/base/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
|
|
|
+ }
|
|
|
+
|
|
|
+ pdu = bo.toByteArray();
|
|
|
+ } catch (IOException e) {
|
|
|
+ }
|
|
|
+ return pdu;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte reverseByte(byte b) {
|
|
|
+ return (byte) ((b & 0xF0) >> 4 | (b & 0x0F) << 4);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] encodeUCS2(String message, byte[] header) throws UnsupportedEncodingException {
|
|
|
+ byte[] userData, textPart;
|
|
|
+ textPart = message.getBytes(StandardCharsets.UTF_16BE);
|
|
|
+
|
|
|
+ if (header != null) {
|
|
|
+ // Need 1 byte for UDHL
|
|
|
+ userData = new byte[header.length + textPart.length + 1];
|
|
|
+
|
|
|
+ userData[0] = (byte) header.length;
|
|
|
+ System.arraycopy(header, 0, userData, 1, header.length);
|
|
|
+ System.arraycopy(textPart, 0, userData, header.length + 1, textPart.length);
|
|
|
+ } else {
|
|
|
+ userData = textPart;
|
|
|
+ }
|
|
|
+ byte[] ret = new byte[userData.length + 1];
|
|
|
+ ret[0] = (byte) (userData.length & 0xff);
|
|
|
+ System.arraycopy(userData, 0, ret, 1, userData.length);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+}
|