当我编码/解码SMS PDU(GSM 7位)用户数据时,是否需要首先预先安装UDH?

问题描述:

虽然在UDH为而不是存在的情况下,我可以成功地编码和解码SMS消息的用户数据部分,但是当存在UDH (在这种情况下,用于级联SMS)时,我遇到了麻烦。当我编码/解码SMS PDU(GSM 7位)用户数据时,是否需要首先预先安装UDH?

当我对用户数据进行解码或编码时,在这样做之前是否需要在文本上预先添加UDH?

本文提供了一个编码例程示例,它用填充位补偿UDH(但我仍不完全明白),但它没有给出传递给例程的数据示例,所以我没有一个明确的用例(我在网站上找不到解码示例): http://mobiletidings.com/2009/07/06/how-to-pack-gsm7-into-septets/

到目前为止,我已经能够得到一些结果,如果我在解码之前将用户数据添加到UDH中,但我怀疑这只是一个巧合。

作为一个例子(利用来自https://en.wikipedia.org/wiki/Concatenated_SMS值):

UDH := '050003000302'; 
ENCODED_USER_DATA_PART := 'D06536FB0DBABFE56C32'; // with padding, evidently 
DecodedUserData := Decode7Bit(UDH + ENCODED_USER_DATA_PART); 
Writeln(DecodedUserData); 

输出: “ß@ø¿Æ@hello世界”

EncodedUserData := Encode7Bit(DecodedUserData); 
DecodedUserData := Decode7Bit(EncodedEncodedUserData); 
Writeln(DecodedUserData); 

相同输出:“ß@ø¿Æ@ Hello World”的

没有前面加上UDH我收到的垃圾:

DecodedUserData := Decode7Bit(ENCODED_USER_DATA_PART); 
Writeln(DecodedUserData); 

输出: “PKYY§An§eYI”

什么是处理这个正确的方式?

我在编码用户数据时应该在文本中包含UDH吗?

我应该在解码后剥离垃圾字符,还是我(我怀疑)完全脱离了这个假设的基础?

虽然这里的解码算法似乎没有UDH,但它似乎没有考虑任何UDH信息: Looking for GSM 7bit encode/decode algorithm

如果有人能够以正确的方式继续进行操作,我将永远感激不尽。任何明确的例子/代码示例将非常感激。 ;-)

我还将提供一个包含算法的小示例应用程序,如果有人认为它有助于解决谜题。

编辑1:

我使用德尔福XE2更新4修补程序1

编辑2:

感谢来自@whosrdaddy帮助,我是能够成功地让我的编码/解码例程工作。

作为一个附注,我很好奇为什么用户数据需要在7位边界上,而UDH不是用它编码的,而是ETSI规范中引用的段落中的最后一句话@whosrdaddy回答说:

如果使用7位数据和TP-UD-头并不一个七重峰边界上完成然后填充比特的最后 信息元素数据八位字节之后插入,使得有一个整个TP-UD标题的整数 个字节。 这是为了确保该SM 本身八位字节边界开始的,使得较早的相位移动 将能够显示SM本身虽然TP-UD部首 在TP-UD字段可以不被理解

我的代码是基于部分从以下资源的例子:

Looking for GSM 7bit encode/decode algorithm

https://en.wikipedia.org/wiki/Concatenated_SMS

http://mobiletidings.com/2009/02/18/combining-sms-messages/

http://mobiletidings.com/2009/07/06/how-to-pack-gsm7-into-septets/

http://mobileforensics.files.wordpress.com/2007/06/understanding_sms.pdf

http://www.dreamfabric.com/sms/

http://www.mediaburst.co.uk/blog/concatenated-sms/

这里是为别人谁是有短信编码/解码故障代码。我确信它可以被简化/优化(并且欢迎评论),但是我已经用几种不同的排列和UDH头部长度对它进行了测试并取得了成功。我希望它有帮助。

unit SmsUtils; 

interface 

uses Windows, Classes, Math; 

function Encode7Bit(const AText: string; AUdhLen: Byte; 
    out ATextLen: Byte): string; 

function Decode7Bit(const APduData: string; AUdhLen: Integer): string; 

implementation 

var 
    g7BitToAsciiTable: array [0 .. 127] of Byte; 
    gAsciiTo7BitTable: array [0 .. 255] of Byte; 

procedure InitializeTables; 
var 
    AsciiValue: Integer; 
    i: Integer; 
begin 
    // create 7-bit to ascii table 
    g7BitToAsciiTable[0] := 64; // @ 
    g7BitToAsciiTable[1] := 163; 
    g7BitToAsciiTable[2] := 36; 
    g7BitToAsciiTable[3] := 165; 
    g7BitToAsciiTable[4] := 232; 
    g7BitToAsciiTable[5] := 223; 
    g7BitToAsciiTable[6] := 249; 
    g7BitToAsciiTable[7] := 236; 
    g7BitToAsciiTable[8] := 242; 
    g7BitToAsciiTable[9] := 199; 
    g7BitToAsciiTable[10] := 10; 
    g7BitToAsciiTable[11] := 216; 
    g7BitToAsciiTable[12] := 248; 
    g7BitToAsciiTable[13] := 13; 
    g7BitToAsciiTable[14] := 197; 
    g7BitToAsciiTable[15] := 229; 
    g7BitToAsciiTable[16] := 0; 
    g7BitToAsciiTable[17] := 95; 
    g7BitToAsciiTable[18] := 0; 
    g7BitToAsciiTable[19] := 0; 
    g7BitToAsciiTable[20] := 0; 
    g7BitToAsciiTable[21] := 0; 
    g7BitToAsciiTable[22] := 0; 
    g7BitToAsciiTable[23] := 0; 
    g7BitToAsciiTable[24] := 0; 
    g7BitToAsciiTable[25] := 0; 
    g7BitToAsciiTable[26] := 0; 
    g7BitToAsciiTable[27] := 0; 
    g7BitToAsciiTable[28] := 198; 
    g7BitToAsciiTable[29] := 230; 
    g7BitToAsciiTable[30] := 223; 
    g7BitToAsciiTable[31] := 201; 
    g7BitToAsciiTable[32] := 32; 
    g7BitToAsciiTable[33] := 33; 
    g7BitToAsciiTable[34] := 34; 
    g7BitToAsciiTable[35] := 35; 
    g7BitToAsciiTable[36] := 164; 
    g7BitToAsciiTable[37] := 37; 
    g7BitToAsciiTable[38] := 38; 
    g7BitToAsciiTable[39] := 39; 
    g7BitToAsciiTable[40] := 40; 
    g7BitToAsciiTable[41] := 41; 
    g7BitToAsciiTable[42] := 42; 
    g7BitToAsciiTable[43] := 43; 
    g7BitToAsciiTable[44] := 44; 
    g7BitToAsciiTable[45] := 45; 
    g7BitToAsciiTable[46] := 46; 
    g7BitToAsciiTable[47] := 47; 
    g7BitToAsciiTable[48] := 48; 
    g7BitToAsciiTable[49] := 49; 
    g7BitToAsciiTable[50] := 50; 
    g7BitToAsciiTable[51] := 51; 
    g7BitToAsciiTable[52] := 52; 
    g7BitToAsciiTable[53] := 53; 
    g7BitToAsciiTable[54] := 54; 
    g7BitToAsciiTable[55] := 55; 
    g7BitToAsciiTable[56] := 56; 
    g7BitToAsciiTable[57] := 57; 
    g7BitToAsciiTable[58] := 58; 
    g7BitToAsciiTable[59] := 59; 
    g7BitToAsciiTable[60] := 60; 
    g7BitToAsciiTable[61] := 61; 
    g7BitToAsciiTable[62] := 62; 
    g7BitToAsciiTable[63] := 63; 
    g7BitToAsciiTable[64] := 161; 
    g7BitToAsciiTable[65] := 65; 
    g7BitToAsciiTable[66] := 66; 
    g7BitToAsciiTable[67] := 67; 
    g7BitToAsciiTable[68] := 68; 
    g7BitToAsciiTable[69] := 69; 
    g7BitToAsciiTable[70] := 70; 
    g7BitToAsciiTable[71] := 71; 
    g7BitToAsciiTable[72] := 72; 
    g7BitToAsciiTable[73] := 73; 
    g7BitToAsciiTable[74] := 74; 
    g7BitToAsciiTable[75] := 75; 
    g7BitToAsciiTable[76] := 76; 
    g7BitToAsciiTable[77] := 77; 
    g7BitToAsciiTable[78] := 78; 
    g7BitToAsciiTable[79] := 79; 
    g7BitToAsciiTable[80] := 80; 
    g7BitToAsciiTable[81] := 81; 
    g7BitToAsciiTable[82] := 82; 
    g7BitToAsciiTable[83] := 83; 
    g7BitToAsciiTable[84] := 84; 
    g7BitToAsciiTable[85] := 85; 
    g7BitToAsciiTable[86] := 86; 
    g7BitToAsciiTable[87] := 87; 
    g7BitToAsciiTable[88] := 88; 
    g7BitToAsciiTable[89] := 89; 
    g7BitToAsciiTable[90] := 90; 
    g7BitToAsciiTable[91] := 196; 
    g7BitToAsciiTable[92] := 204; 
    g7BitToAsciiTable[93] := 209; 
    g7BitToAsciiTable[94] := 220; 
    g7BitToAsciiTable[95] := 167; 
    g7BitToAsciiTable[96] := 191; 
    g7BitToAsciiTable[97] := 97; 
    g7BitToAsciiTable[98] := 98; 
    g7BitToAsciiTable[99] := 99; 
    g7BitToAsciiTable[100] := 100; 
    g7BitToAsciiTable[101] := 101; 
    g7BitToAsciiTable[102] := 102; 
    g7BitToAsciiTable[103] := 103; 
    g7BitToAsciiTable[104] := 104; 
    g7BitToAsciiTable[105] := 105; 
    g7BitToAsciiTable[106] := 106; 
    g7BitToAsciiTable[107] := 107; 
    g7BitToAsciiTable[108] := 108; 
    g7BitToAsciiTable[109] := 109; 
    g7BitToAsciiTable[110] := 110; 
    g7BitToAsciiTable[111] := 111; 
    g7BitToAsciiTable[112] := 112; 
    g7BitToAsciiTable[113] := 113; 
    g7BitToAsciiTable[114] := 114; 
    g7BitToAsciiTable[115] := 115; 
    g7BitToAsciiTable[116] := 116; 
    g7BitToAsciiTable[117] := 117; 
    g7BitToAsciiTable[118] := 118; 
    g7BitToAsciiTable[119] := 119; 
    g7BitToAsciiTable[120] := 120; 
    g7BitToAsciiTable[121] := 121; 
    g7BitToAsciiTable[122] := 122; 
    g7BitToAsciiTable[123] := 228; 
    g7BitToAsciiTable[124] := 246; 
    g7BitToAsciiTable[125] := 241; 
    g7BitToAsciiTable[126] := 252; 
    g7BitToAsciiTable[127] := 224; 

    // create ascii to 7-bit table 
    ZeroMemory(@gAsciiTo7BitTable, SizeOf(gAsciiTo7BitTable)); 
    for i := 0 to High(g7BitToAsciiTable) do 
    begin 
    AsciiValue := g7BitToAsciiTable[i]; 
    gAsciiTo7BitTable[AsciiValue] := i; 
    end; 
end; 

function ConvertAsciiTo7Bit(const AText: string; AUdhLen: Byte): AnsiString; 
const 
    ESC = #27; 
    ESCAPED_ASCII_CODES = [#94, #123, #125, #92, #91, #126, #93, #124, #164]; 
var 
    Septet: Byte; 
    Ch: AnsiChar; 
    i: Integer; 
begin 
    for i := 1 to Length(AText) do 
    begin 
    Ch := AnsiChar(AText[i]); 
    if not(Ch in ESCAPED_ASCII_CODES) then 
     Septet := gAsciiTo7BitTable[Byte(Ch)] 
    else 
    begin 
     Result := Result + ESC; 
     case (Ch) of 
     #12: Septet := 10; 
     #94: Septet := 20; 
     #123: Septet := 40; 
     #125: Septet := 41; 
     #92: Septet := 47; 
     #91: Septet := 60; 
     #126: Septet := 61; 
     #93: Septet := 62; 
     #124: Septet := 64; 
     #164: Septet := 101; 
     else Septet := 0; 
     end; 
    end; 
    Result := Result + AnsiChar(Septet); 
    end; 
end; 

function Convert7BitToAscii(const AText: AnsiString): string; 
const 
    ESC = #27; 
var 
    TextLen: Integer; 
    Ch: Char; 
    i: Integer; 
begin 
    Result := ''; 
    TextLen := Length(AText); 
    i := 1; 
    while (i <= TextLen) do 
    begin 
    Ch := Char(AText[i]); 
    if (Ch <> ESC) then 
     Result := Result + Char(g7BitToAsciiTable[Ord(Ch)]) 
    else 
    begin 
     Inc(i); // skip ESC 
     if (i <= TextLen) then 
     begin 
     Ch := Char(AText[i]); 
     case (Ch) of 
      #10: Ch := #12; 
      #20: Ch := #94; 
      #40: Ch := #123; 
      #41: Ch := #125; 
      #47: Ch := #92; 
      #60: Ch := #91; 
      #61: Ch := #126; 
      #62: Ch := #93; 
      #64: Ch := #124; 
      #101: Ch := #164; 
     end; 
     Result := Result + Ch; 
     end; 
    end; 
    Inc(i); 
    end; 
end; 

function StrToHex(const AText: AnsiString): AnsiString; overload; 
var 
    TextLen: Integer; 
begin 
    // set the text buffer size 
    TextLen := Length(AText); 
    // set the length of the result to double the string length 
    SetLength(Result, TextLen * 2); 
    // convert the string to hex 
    BinToHex(PAnsiChar(AText), PAnsiChar(Result), TextLen); 
end; 

function StrToHex(const AText: string): string; overload; 
begin 
    Result := string(StrToHex(AnsiString(AText))); 
end; 

function HexToStr(const AText: AnsiString): AnsiString; overload; 
var 
    ResultLen: Integer; 
begin 
    // set the length of the result to half the Text length 
    ResultLen := Length(AText) div 2; 
    SetLength(Result, ResultLen); 
    // convert the hex back into a string 
    if (HexToBin(PAnsiChar(AText), PAnsiChar(Result), ResultLen) <> ResultLen) then 
    Result := 'Error Converting Hex To String: ' + AText; 
end; 

function HexToStr(const AText: string): string; overload; 
begin 
    Result := string(HexToStr(AnsiString(AText))); 
end; 

function Encode7Bit(const AText: string; AUdhLen: Byte; 
    out ATextLen: Byte): string; 
// AText: Ascii text 
// AUdhLen: Length of UDH including UDH Len byte (e.g. '050003CC0101' = 6 bytes) 
// ATextLen: returns length of text that was encoded. This can be different 
// than Length(AText) due to escape characters 
// Returns text as encoded PDU hex string 
var 
    Text7Bit: AnsiString; 
    Pdu: AnsiString; 
    PduIdx: Integer; 
    PduLen: Byte; 
    PaddingBits: Byte; 
    BitsToMove: Byte; 
    Septet: Byte; 
    Octet: Byte; 
    PrevOctet: Byte; 
    ShiftedOctet: Byte; 
    i: Integer; 
begin 
    Result := ''; 
    Text7Bit := ConvertAsciiTo7Bit(AText, AUdhLen); 
    ATextLen := Length(Text7Bit); 
    BitsToMove := 0; 
    // determine how many padding bits needed based on the UDH 
    if (AUdhLen > 0) then 
    PaddingBits := 7 - ((AUdhLen * 8) mod 7) 
    else 
    PaddingBits := 0; 
    // calculate the number of bytes needed to store the 7-bit text 
    // along with any padding bits that are required 
    PduLen := Ceil(((ATextLen * 7) + PaddingBits)/8); 
    // reserve space for the PDU bytes 
    Pdu := AnsiString(StringOfChar(#0, PduLen)); 
    PduIdx := 1; 
    for i := 1 to ATextLen do 
    begin 
    if (BitsToMove = 7) then 
     BitsToMove := 0 
    else 
    begin 
     // convert the current character to a septet (7-bits) and make room for 
     // the bits from the next one 
     Septet := (Byte(Text7Bit[i]) shr BitsToMove); 
     if (i = ATextLen) then 
     Octet := Septet 
     else 
     begin 
     // convert the next character to a septet and copy the bits from it 
     // to the octet (PDU byte) 
     Octet := Septet or 
      Byte((Byte(Text7Bit[i + 1]) shl Byte(7 - BitsToMove))); 
     end; 
     Byte(Pdu[PduIdx]) := Octet; 
     Inc(PduIdx); 
     Inc(BitsToMove); 
    end; 
    end; 
    // The following code pads the pdu on the *right* by shifting it to the *left* 
    // by <PaddingBits>. It does this by using the same bit storage convention as 
    // the 7-bit compression routine above, by taking the most significant 
    // <PaddingBits> from each PDU byte and moving them to the least significant 
    // bits of the next PDU byte. If there is no room in the last PDU byte for the 
    // high bits of the previous byte that were removed, then those bits are 
    // placed into an additional byte reserved for this purpose. 
    // Note: <PduLen> has already been set to account for the reserved byte if 
    // it is required. 
    if (PaddingBits > 0) then 
    begin 
    SetLength(Result, (PduLen * 2)); 
    PrevOctet := 0; 
    for PduIdx := 1 to PduLen do 
    begin 
     Octet := Byte(Pdu[PduIdx]); 
     if (PduIdx = 1) then 
     ShiftedOctet := Byte(Octet shl PaddingBits) 
     else 
     ShiftedOctet := Byte(Octet shl PaddingBits) or 
      Byte(PrevOctet shr (8 - PaddingBits)); 
     Byte(Pdu[PduIdx]) := ShiftedOctet; 
     PrevOctet := Octet; 
    end; 
    end; 
    Result := string(StrToHex(Pdu)); 
end; 

function Decode7Bit(const APduData: string; AUdhLen: Integer): string; 
// APduData: Hex string representation of PDU data 
// AUdhLen: Length of UDH including UDH Len (e.g. '050003CC0101' = 6 bytes) 
// Returns decoded Ascii text 
var 
    Pdu: AnsiString; 
    NumSeptets: Byte; 
    Septets: AnsiString; 
    PduIdx: Integer; 
    PduLen: Integer; 
    by: Byte; 
    currBy: Byte; 
    left: Byte; 
    mask: Byte; 
    nextBy: Byte; 
    Octet: Byte; 
    NextOctet: Byte; 
    PaddingBits: Byte; 
    ShiftedOctet: Byte; 
    i: Integer; 
begin 
    Result := ''; 
    PaddingBits := 0; 
    // convert hex string to bytes 
    Pdu := AnsiString(HexToStr(APduData)); 
    PduLen := Length(Pdu); 
    // The following code removes padding at the end of the PDU by shifting it 
    // *right* by <PaddingBits>. It does this by taking the least significant 
    // <PaddingBits> from the following PDU byte and moving them to the most 
    // significant the current PDU byte. 
    if (AUdhLen > 0) then 
    begin 
    PaddingBits := 7 - ((AUdhLen * 8) mod 7); 
    for PduIdx := 1 to PduLen do 
    begin 
     Octet := Byte(Pdu[PduIdx]); 
     if (PduIdx = PduLen) then 
     ShiftedOctet := Byte(Octet shr PaddingBits) 
     else 
     begin 
     NextOctet := Byte(Pdu[PduIdx + 1]); 
     ShiftedOctet := Byte(Octet shr PaddingBits) or 
      Byte(NextOctet shl (8 - PaddingBits)); 
     end; 
     Byte(Pdu[PduIdx]) := ShiftedOctet; 
    end; 
    end; 
    // decode 
    // number of septets in PDU after excluding the padding bits 
    NumSeptets := ((PduLen * 8) - PaddingBits) div 7; 
    Septets := AnsiString(StringOfChar(#0, NumSeptets)); 
    left := 7; 
    mask := $7F; 
    nextBy := 0; 
    PduIdx := 1; 
    for i := 1 to NumSeptets do 
    begin 
    if mask = 0 then 
    begin 
     Septets[i] := AnsiChar(nextBy); 
     left := 7; 
     mask := $7F; 
     nextBy := 0; 
    end 
    else 
    begin 
     if (PduIdx > PduLen) then 
     Break; 
     by := Byte(Pdu[PduIdx]); 
     Inc(PduIdx); 
     currBy := ((by AND mask) SHL (7 - left)) OR nextBy; 
     nextBy := (by AND (NOT mask)) SHR left; 
     Septets[i] := AnsiChar(currBy); 
     mask := mask SHR 1; 
     left := left - 1; 
    end; 
    end; // for 
    // remove last character if unused 
    // this is kind of a hack, but frankly I don't know how else to compensate 
    // for it. 
    if (Septets[NumSeptets] = #0) then 
    SetLength(Septets, NumSeptets - 1); 
    // convert 7-bit alphabet to ascii 
    Result := Convert7BitToAscii(Septets); 
end; 

initialization 
    InitializeTables; 
end. 
+1

请提及您的Delphi版本。 – menjaraz 2012-07-15 06:36:40

+0

为了帮助愿意回答您的问题的人,您必须至少包含所有最小的相关输入,以使他们能够重现问题(编码/ Decode7Bit细节等)。 – menjaraz 2012-07-15 06:51:06

不,你不包括UDH部分进行编码时,但你如果读GSM phase 2 specification 57页,他们提及此事:“如果使用7位数据和TP-UD-头不在七个边界上结束,然后在最后的信息元素数据字节之后插入 ,以便整个 TP-UD报头有整数个七位字节“。当你有一个UDH部分,这可能并非如此,因此,所有你需要做的就是计算偏移(填充比特=数)

计算偏移,这个代码假定UDHPart是AnsiString类型:

Len := Length(UDHPart) shr 1; 
Offset := 7 - ((Len * 8) mod 7); // fill bits 

现在编码7位数据时,则正常进行,但在结束时,你却将数据偏移位到左侧,该代码具有可变结果的编码数据(AnsiString类型):

// fill bits 
if Offset > 0 then 
    begin 
    v := Result; 
    Len := Length(v); 
    BytesRemain := ceil(((Len * 7)+Offset)/8);  
    Result := StringOfChar(#0, BytesRemain); 
    for InPos := 1 to BytesRemain do 
    begin 
    if InPos = 1 then 
     Byte(Result[InPos]) := Byte(v[InPos]) shl offset 
    else 
     Byte(Result[InPos]) := (Byte(v[InPos]) shl offset) or (Byte(v[InPos-1]) shr (8 - offset)); 
    end; 
    end; 

解码也是同样的事情,你首先转移7位数据偏移bi在解码之前ts到右边......

我希望这会让你走上正确的轨道......

+0

非常感谢。除了有偏移位和编码数据位于7位边界的情况(在这种情况下最后一个字符出现乱码),我得到了它的工作。例如,如果您有1个偏移位(Len = 6)并且您的源文本是8个字符(编码为56位或7个完整打包的字节),您是否需要在上面的'结果'中添加一个额外的字节以存储在转换过程中从最后一个字节中丢弃的最重要的位?也许'BytesRemain'包括那个?我正在使用'BytesRemain:= Length(v)'。 – Doug 2012-07-16 23:02:43

在你的情况 数据是D06536FB0DBABFE56C32

获得第一个字符是D0 => H(在第7位,第8位未使用)

剩下的就是6536FB0DBABFE56C32

宾县

(01100101)0011011011111011000011011011101010111111111001010110110000110010

向左右移。 =>每个7位都是char!

001100100110110011100101101111111011101000001101111 1101100 110110(0 1100101)

我转移7到左。你可以从上面得到字符串。但我对易秀做:d

(1100101)(1101100)(1101100)(1101111)(0100000)(1110111)(1101111)(1110010)(1101100)(1100100)00

和字符串是“ello world”

与第一个字符结合“Hello world”