最近在做毕设,用到了这个,搜了下网友有实现,我稍微修改了下把android库去掉了,纯java库
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Calendar;
/**
* Created by ashqal on 15-1-16.
*/
public class PduEncoder
{
public static Object[] createFakeSms( String sender, String body) {
byte[] pdu = null;
byte[] scBytes = networkPortionToCalledPartyBCD("0000000000"); //短信中心号码,调用networkPortionToCalledPartyBCD转换为BCD二进制码格式.
byte[] senderBytes = networkPortionToCalledPartyBCD(sender); //伪造的发信号码.
int lsmcs = scBytes.length; //短信中心的号码长度。
try {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
bo.write(lsmcs);
bo.write(scBytes); //以上两项是SCA
bo.write(0x04); //PDU TYPE
bo.write((byte) sender.length()); //所伪造发件号码的长度。
bo.write(senderBytes); //以上两项是OA
bo.write(0x00); //PID
try {
byte[] bodybytes = EncodeUCS2(body,null); //用EncodeUCS2函数将短信内容编码为UCS2,短信的长度在函数中已经计算并加入body的头部.
bo.write(0x08); // 0x08代表UCS-2编码格式
//Log.e("编码结果",getBCDString());
bo.write(str2Bcd(getBCDString())); //以BCD格式写入当前时间
bo.write(bodybytes); //写入短信内容
} catch (Exception e) {
}
pdu = bo.toByteArray(); //最后把结果转换成byte
} catch (IOException e) {
}
return new Object[] { pdu };
}
/**
* 取当前日期并转换为BCD格式
* 输出String共14位,每两位为一时间单位,每个时间单位均为位数倒置,最后两位是offset:
* 年+月+日+时+分+秒+offset
* 只适用于offset取值23的情况
* @return
*/
private static String getBCDString(){
String BCDDate="";
String YEAR,MONTH,DAY,HOUR,MINUTE,SECOND;
Calendar now = Calendar.getInstance();
YEAR=String.valueOf(now.get(Calendar.YEAR));
MONTH=String.valueOf(now.get(Calendar.MONTH)+1); //Calendar的月份是从0开始的,即0代表1月份,故此处要加1.
DAY=String.valueOf(now.get(Calendar.DAY_OF_MONTH));
HOUR=String.valueOf(now.get(Calendar.HOUR_OF_DAY));
MINUTE=String.valueOf(now.get(Calendar.MINUTE));
SECOND=String.valueOf(now.get(Calendar.SECOND));
BCDDate=FmtBCDDate(YEAR.substring(2))+FmtBCDDate(MONTH)+FmtBCDDate(DAY)+
FmtBCDDate(HOUR)+FmtBCDDate(MINUTE)+FmtBCDDate(SECOND)+"23"; //offset为23
return BCDDate;
}
/**
* 日期格式化函数,一位数后面加零变两位数,两位数则颠倒位数
* @param s
* @return
*/
private static String FmtBCDDate(String s){
if(ReverseInt(Integer.parseInt(s)).equals("0"))
{
return "00";
}
else
{
if(ReverseInt(Integer.parseInt(s)).length()==1)
return ReverseInt(Integer.parseInt(s))+"0";
else
return ReverseInt(Integer.parseInt(s));
}
}
/**
* 整数位数颠倒函数 ,输出String.
* @param i
* @return
*/
private static String ReverseInt(int i){
StringBuffer bs = new StringBuffer(String.valueOf(i));
return bs.reverse().toString();
}
/**
* String转BCD编码函数,来自网络.
* @param asc
* @return
*/
private static byte[] str2Bcd(String asc) {
int len = asc.length();
int mod = len % 2;
if (mod != 0) {
asc = "0" + asc;
len = asc.length();
}
byte abt[] = new byte[len];
if (len >= 2) {
len = len / 2;
}
byte bbt[] = new byte[len];
abt = asc.getBytes();
int j, k;
for (int p = 0; p < asc.length()/2; p++) {
if ( (abt[2 * p] >= '0') && (abt[2 * p] <= '9')) {
j = abt[2 * p] - '0';
} else if ( (abt[2 * p] >= 'a') && (abt[2 * p] <= 'z')) {
j = abt[2 * p] - 'a' + 0x0a;
} else {
j = abt[2 * p] - 'A' + 0x0a;
}
if ( (abt[2 * p + 1] >= '0') && (abt[2 * p + 1] <= '9')) {
k = abt[2 * p + 1] - '0';
} else if ( (abt[2 * p + 1] >= 'a') && (abt[2 * p + 1] <= 'z')) {
k = abt[2 * p + 1] - 'a' + 0x0a;
}else {
k = abt[2 * p + 1] - 'A' + 0x0a;
}
int a = (j << 4) + k;
byte b = (byte) a;
bbt[p] = b;
}
return bbt;
}
/**
* 以UCS-2格式编码String,用于发送汉字,使用此格式编码最多可发送70个字。
* @param message
* @param header
* @return
* @throws java.io.UnsupportedEncodingException
*/
private static byte[] EncodeUCS2(String message, byte[] header)
throws UnsupportedEncodingException {
byte[] userData, textPart;
textPart = message.getBytes("UTF-16BE");
if (header != null) {
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;
}
/**
* Note: calls extractNetworkPortion(), so do not use for
* SIM EF[ADN] style records
*
* Returns null if network portion is empty.
*/
private static byte[]
networkPortionToCalledPartyBCD(String s) {
String networkPortion = extractNetworkPortion(s);
return numberToCalledPartyBCDHelper(networkPortion, false);
}
/** Extracts the network address portion and canonicalizes
* (filters out separators.)
* Network address portion is everything up to DTMF control digit
* separators (pause or wait), but without non-dialable characters.
*
* Please note that the GSM wild character is allowed in the result.
* This must be resolved before dialing.
*
* Returns null if phoneNumber == null
*/
public static String
extractNetworkPortion(String phoneNumber) {
if (phoneNumber == null) {
return null;
}
int len = phoneNumber.length();
StringBuilder ret = new StringBuilder(len);
for (int i = 0; i < len; i++) {
char c = phoneNumber.charAt(i);
// Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
int digit = Character.digit(c, 10);
if (digit != -1) {
ret.append(digit);
} else if (c == '+') {
// Allow '+' as first character or after CLIR MMI prefix
String prefix = ret.toString();
if (prefix.length() == 0 || prefix.equals(CLIR_ON) || prefix.equals(CLIR_OFF)) {
ret.append(c);
}
} else if (isDialable(c)) {
ret.append(c);
} else if (isStartsPostDial (c)) {
break;
}
}
return ret.toString();
}
private static byte[]
numberToCalledPartyBCDHelper(String number, boolean includeLength) {
int numberLenReal = number.length();
int numberLenEffective = numberLenReal;
boolean hasPlus = number.indexOf('+') != -1;
if (hasPlus) numberLenEffective--;
if (numberLenEffective == 0) return null;
int resultLen = (numberLenEffective + 1) / 2; // Encoded numbers require only 4 bits each.
int extraBytes = 1; // Prepended TOA byte.
if (includeLength) extraBytes++; // Optional prepended length byte.
resultLen += extraBytes;
byte[] result = new byte[resultLen];
int digitCount = 0;
for (int i = 0; i < numberLenReal; i++) {
char c = number.charAt(i);
if (c == '+') continue;
int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
digitCount++;
}
// 1-fill any trailing odd nibble/quartet.
if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
int offset = 0;
if (includeLength) result[offset++] = (byte)(resultLen - 1);
result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
return result;
}
/** True if c is ISO-LATIN characters 0-9, *, # , +, WILD */
public final static boolean
isDialable(char c) {
return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == WILD;
}
/** This any anything to the right of this char is part of the
* post-dial string (eg this is PAUSE or WAIT)
*/
public final static boolean
isStartsPostDial (char c) {
return c == PAUSE || c == WAIT;
}
private static int
charToBCD(char c) {
if (c >= '0' && c <= '9') {
return c - '0';
} else if (c == '*') {
return 0xa;
} else if (c == '#') {
return 0xb;
} else if (c == PAUSE) {
return 0xc;
} else if (c == WILD) {
return 0xd;
} else {
throw new RuntimeException ("invalid char for BCD " + c);
}
}
/*
* Special characters
*
* (See "What is a phone number?" doc)
* 'p' --- GSM pause character, same as comma
* 'n' --- GSM wild character
* 'w' --- GSM wait character
*/
public static final char PAUSE = ',';
public static final char WAIT = ';';
public static final char WILD = 'N';
/*
* Calling Line Identification Restriction (CLIR)
*/
private static final String CLIR_ON = "*31#";
private static final String CLIR_OFF = "#31#";
/*
* TOA = TON + NPI
* See TS 24.008 section 10.5.4.7 for details.
* These are the only really useful TOA values
*/
public static final int TOA_International = 0x91;
public static final int TOA_Unknown = 0x81;
}
构造好给intent发送就好了
Intent smsIntent = new Intent();
smsIntent.setAction("android.provider.Telephony.SMS_RECEIVED");
smsIntent.putExtra("pdus", PduEncoder.createFakeSms("1688", msg.obj.toString()));
context.sendOrderedBroadcast(smsIntent, null);
//或者
Intent smsIntent2 = new Intent();
smsIntent2.setAction("android.intent.action.DATA_SMS_RECEIVED");
smsIntent2.putExtra("pdus", PduEncoder.createFakeSms("1688", msg.obj.toString()));
smsIntent2.setData(Uri.parse("sms:"));
context.sendOrderedBroadcast(smsIntent2, null);