作者归档:softsim

iPhone切换运营商

iPhone:/var/mobile/Library root# ls -ld Carrier*
lrwxr-xr-x 1 mobile mobile  44 Apr 15 16:17 Carrier\ Bundle.bundle -> /System/Library/Carrier\ Bundles/iPhone/46000/
drwxr-xr-x 5 mobile mobile 160 Apr 15 16:13 Carrier\ Bundles/
lrwxr-xr-x 1 mobile mobile  44 Apr 15 16:17 Carrier1Bundle.bundle -> /System/Library/Carrier\ Bundles/iPhone/46000/
lrwxr-xr-x 1 mobile mobile  53 Feb 27 08:50 CarrierDefault.bundle -> /System/Library/Carrier\ Bundles/iPhone/Default.bundle/

ICCID

/var/mobile/Library/Carrier Bundles/ICCIDCache/ICCIDCache.plist

插入电信卡

iPhone:/var/mobile/Library root# ls -ld Carrier*
lrwxr-xr-x 1 mobile mobile  44 Apr 15 17:25 Carrier\ Bundle.bundle -> /System/Library/Carrier\ Bundles/iPhone/46011/
drwxr-xr-x 5 mobile mobile 160 Apr 15 16:13 Carrier\ Bundles/
lrwxr-xr-x 1 mobile mobile  44 Apr 15 17:25 Carrier1Bundle.bundle -> /System/Library/Carrier\ Bundles/iPhone/46011/
lrwxr-xr-x 1 mobile mobile  53 Feb 27 08:50 CarrierDefault.bundle -> /System/Library/Carrier\ Bundles/iPhone/Default.bundle/

插入联通卡

iPhone:/var/mobile/Library root# ls -ld Carrier*
lrwxr-xr-x 1 mobile mobile  48 Apr 15 17:33 Carrier\ Bundle.bundle -> /var/mobile/Library/Carrier\ Bundles/iPhone/46001/
drwxr-xr-x 5 mobile mobile 160 Apr 15 16:13 Carrier\ Bundles/
lrwxr-xr-x 1 mobile mobile  48 Apr 15 17:33 Carrier1Bundle.bundle -> /var/mobile/Library/Carrier\ Bundles/iPhone/46001/
lrwxr-xr-x 1 mobile mobile  53 Feb 27 08:50 CarrierDefault.bundle -> /System/Library/Carrier\ Bundles/iPhone/Default.bundle/

中国

lrwxr-xr-x 1 root wheel 14 Nov  3 10:59 46000 -> CMCC_cn.bundle/
lrwxr-xr-x 1 root wheel 16 Nov  3 10:59 46001 -> Unicom_cn.bundle/
lrwxr-xr-x 1 root wheel 14 Nov  3 10:59 46002 -> CMCC_cn.bundle/
lrwxr-xr-x 1 root wheel 22 Nov  3 10:59 46003 -> ChinaTelecom_cn.bundle/
lrwxr-xr-x 1 root wheel 14 Nov  3 10:59 46007 -> CMCC_cn.bundle/
lrwxr-xr-x 1 root wheel 14 Nov  3 10:59 46008 -> CMCC_cn.bundle/
lrwxr-xr-x 1 root wheel 16 Nov  3 10:59 46009 -> Unicom_cn.bundle/
lrwxr-xr-x 1 root wheel 27 Nov  3 10:59 46011 -> ChinaTelecom_USIM_cn.bundle/

RC4流式对称加密

RC 4生成一个伪随机流(pseduorandom stream of bits) 来同 明文 进行 按位异或(bit-wise exclusive-or) 来 实现加密和解密

KSA(key-scheduling algorithm)

初始化数组S. 键长 1 ≤ keylength ≤ 256, 典型值为 5~ 16, 也就是 40bits ~ 128bits

for i from 0 to 255
S[i] := i
endfor

j := 0
for i from 0 to 255
j := (j + S[i] + key[i mod keylength]) mod 256
swap(&S[i],&S[j])
endfor

PRGA

使用上一步生成的S 数组来生成 伪随机流

i := 0
j := 0
while GeneratingOutput:
i := (i + 1) mod 256
j := (j + S[i]) mod 256
swap(&S[i],&S[j])
output S[(S[i] + S[j]) mod 256]
endwhile

一个C实现

unsigned char S[256];
unsigned int i, j;

void swap(unsigned char *s, unsigned int i, unsigned int j) {
    unsigned char temp = s[i];
    s[i] = s[j];
    s[j] = temp;
}

/* KSA */
void rc4_init(unsigned char *key, unsigned int key_length) {
    for (i = 0; i < 256; i++)
        S[i] = i;

    for (i = j = 0; i < 256; i++) {
        j = (j + key[i % key_length] + S[i]) & 255;
        swap(S, i, j);
    }

    i = j = 0;
}

/* PRGA */
unsigned char rc4_output() {
    i = (i + 1) & 255;
    j = (j + S[i]) & 255;

    swap(S, i, j);

    return S[(S[i] + S[j]) & 255];
}

#include < stdio.h >
#include < string.h >
#include < stdlib.h >

#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))

int main() {
    unsigned char *test_vectors[][2] =
    {
        {"Key", "Plaintext"},
        {"Wiki", "pedia"},
        {"Secret", "Attack at dawn"}
    };

    int x;
    for (x = 0; x < ARRAY_SIZE(test_vectors); x++) {
        int y;
        rc4_init(test_vectors[x][0], strlen((char*)test_vectors[x][0]));

        for (y = 0; y < strlen((char*)test_vectors[x][1]); y++)
           printf("%02X", test_vectors[x][1][y] ^ rc4_output());
        printf("n");
    }
    return 0;
}

Python实现

def initialize(key):
    """Produce a 256-entry list based on `key` (a sequence of numbers)
    as the first step in RC4.
    Note: indices in key greater than 255 will be ignored.
    """
    k = range(256)
    j = 0
    for i in range(256):
        j = (j + k[i] + key[i % len(key)]) % 256
        k[i], k[j] = k[j], k[i]
    return k

def gen_random_bytes(k):
    """Yield a pseudo-random stream of bytes based on 256-byte array `k`."""
    i = 0
    j = 0
    while True:
        i = (i + 1) % 256
        j = (j + k[i]) % 256
        k[i], k[j] = k[j], k[i]
        yield k[(k[i] + k[j]) % 256]

def run_rc4(k, text):
    cipher_chars = []
    random_byte_gen = gen_random_bytes(k)
    for char in text:
        byte = ord(char)
        cipher_byte = byte ^ random_byte_gen.next()
        cipher_chars.append(chr(cipher_byte))
    return ''.join(cipher_chars)

以上是一种实现方式, run_rc4(k, text)  输入 key 和 待加密的 text, 返回 已经加密的串

class RC4:
   def __init__(self, key = ""):
       if key:
           self._rc4_init(key)

   def _rc4_init(self, key):
       (self.x,self.y) = (0,0)
       key_len = len(key)
       if key_len > 256 or key_len < 1:
           raise IndexError, 'Invalid key length' + key_len
       self.state_array = [i for i in xrange(0,256)] #self.stat_array = range(0,256)
       for i in xrange(0,256):
           self.x = ((ord(key[i%key_len]) & 0xff) + self.state_array[i] + self.x) & 0xff
           self.state_array[i], self.state_array[self.x] = self.state_array[self.x], self.state_array[i]
       self.x = 0

   def engine_crypt(self, input):
       self.out = []
       for i in xrange(0,len(input)):
           self.x = (self.x + 1) & 0xff
           self.y = (self.state_array[self.x] + self.y) & 0xff
           self.state_array[self.x], self.state_array[self.y] = self.state_array[self.y], self.state_array[self.x]
           self.out.append(chr((ord(input[i]) ^ self.state_array[(self.state_array[self.x] + self.state_array[self.y]) & 0xff])))
       return "".join(self.out)

以上是第2种实现,使用了class
使用方法  RC4(key).engine_crypt(text)  ,返回的是加密后的串

def rc4crypt(data, key):
    x = 0
    box = range(256)
    for i in range(256):
        x = (x + box[i] + ord(key[i % len(key)])) % 256
        box[i], box[x] = box[x], box[i]
    x = 0
    y = 0
    out = []
    for char in data:
        x = (x + 1) % 256
        y = (y + box[x]) % 256
        box[x], box[y] = box[y], box[x]
        out.append(chr(ord(char) ^ box[(box[x] + box[y]) % 256]))

    return ''.join(out)

Python3版本


def rc4crypt(data: bytes, key: bytes) -> bytes:
    x = 0
    box = list(range(256))
    for i in range(256):
        x = (x + box[i] + key[i % len(key)]) % 256
        box[i], box[x] = box[x], box[i]
    x = y = 0
    out = []
    for char in data:
        x = (x + 1) % 256
        y = (y + box[x]) % 256
        box[x], box[y] = box[y], box[x]
        out.append((char ^ box[(box[x] + box[y]) % 256]))

    return bytes(out)
   
    
mykey = b'softs.im!'    
mydata = b'android/telephony/TelephonyManager'

x = rc4crypt(mydata, mykey)

import binascii
print(x)
print(binascii.hexlify(x))

y =  rc4crypt(x, mykey)
print(y)
print(binascii.hexlify(y))


以上是一个单个函数形式的实现

PHP实现

function Encrypt($key, $pt) {
    $s = array();
    for ($i=0; $i<256; $i++) {
        $s[$i] = $i;
    }

    $j = 0;
    $key_len = strlen($key);
    for ($i=0; $i<256; $i++) {
        $j = ($j + $s[$i] + ord($key[$i % $key_len])) % 256;
        //swap
        $x = $s[$i];
        $s[$i] = $s[$j];
        $s[$j] = $x;
    }
    $i = 0;
    $j = 0;
    $ct = '';
    $data_len = strlen($pt);
    for ($y=0; $y< $data_len; $y++) {
        $i = ($i + 1) % 256;
        $j = ($j + $s[$i]) % 256;
        //swap
        $x = $s[$i];
        $s[$i] = $s[$j];
        $s[$j] = $x;
        $ct .= $pt[$y] ^ chr($s[($s[$i] + $s[$j]) % 256]);
    }
    return $ct;
}

Java也可以实现

   private static byte[] rc4_encrypt(byte[] key, byte[] plaintext) {
        byte[] S = new byte[256];
        byte[] T = new byte[256];
        int keylen = key.length;

        for (int i = 0; i < 256; i++) {
            S[i] = (byte) i;
            T[i] = key[i % keylen];
        }
        int j = 0;
        for (int i = 0; i < 256; i++) {
            j = (j + S[i] + T[i]) & 0xFF;
            S[i] ^= S[j];
            S[j] ^= S[i];
            S[i] ^= S[j];
        }

        byte[] ciphertext = new byte[plaintext.length];
        int i = 0, k, t;
        j = 0;
        for (int counter = 0; counter < plaintext.length; counter++) {
            i = (i + 1) & 0xFF;
            j = (j + S[i]) & 0xFF;
            S[i] ^= S[j];
            S[j] ^= S[i];
            S[i] ^= S[j];
            t = (S[i] + S[j]) & 0xFF;
            k = S[t];
            ciphertext[counter] = (byte) (plaintext[counter] ^ k);
        }
        return ciphertext;
    }

GSM GID1和GID2

The GID1 and GID2 elementary files on the SIM are specified in GSM 11.11 (ETS 300 977)

启用它,须在SIM Service Talbe中做出合适的标记

GID1 (Group Identifier Level 1) – an identifier for a particular SIM and handset association, which can be used to identify a group of SIMs involved in a particular application.

GID2 (Group Identifier Level 2) – a GID1-like identifier

主要用来标识虚拟运营商

XCode11中使用StoryBoard创建Scroll View无需任何代码

在XCode11中,跟”XCode8中使用StoryBoard创建Scroll View”有所区别。
虽然,也可以手动编辑 Main.stroyboard, 删除 Safe Area之后,采用跟XCode8一样的方法。

在 XCode11, Object Library默认隐藏, 可以用 Command + Shift + L 快捷键打开
或者 View / ShowLibrary打开

  1. 创建 Scroll View, 并添加约束
  2. 添加View到Scroll View 上
  3. 在树形图中,选中View, 按住Ctrl键盘, 拖向 Content Layout Guide

  4. 在弹出的约束菜单里,按住Shift, 同时选中leading / top / trailing / bottom 4个约束

  5. 选中View, 拖向 Frame Layout Guide, 在弹出的菜单里,选择Equal Width

  6. 为ContentView 设置高度
  7. 为Scroll View和Content View 检查约束常量  Constant 是否为0, Multiplier是否为1

XCode8中使用StoryBoard创建Scroll View

  1. XCode创建一个 Single View Application
  2. 在左侧的Project navigator中选中 Main.storyboard, 中间会出现 View Controller Scene的树型列表, 选中 View
  3. 在右下侧的 object libary 筛选出 Scroll View, 拖到预览图中的View上面, 或者 拖到 树型图的 View 下面。
  4. 在树形图中选中 Scroll View, 为其添加约束(Add New Constraints)
  5. 上下左右,都为0, 共4个

  6. 然后在 Object Libaray中筛选uiview,  将View拖到预览图的ScrollView之上(也就是树形图的ScrollView之下)
  7. 树形图中选中 新建立的View, 右侧选 Identity inspector, 在Document里为新View设置Label ,名为 ContentView.
  8. (这一步可以不要)

  9. 然后为新的ContentView,添加约束
  10. 在树形图中选中ContentView,  按住Ctrl键, 拖向  Scroll View. 同时松开鼠标左键和键盘Ctrl键, 出现菜单,选 Equal Widths
  11. 所有的Constraints 如下图
  12. 再在ContentView上添加的东西,就可以滚动了

Java SIM OTA

[Acess Domain] specifying the applets rights on file system

[Minimum Security Level] specifying minimum security that incoming OTA
message has to present.

当STK应用被安装时,一些参数可以设定 ME和SIM卡的哪些资源可以被applet使用。

访问域(Access Domain): 指定了被授予STK访问GSM文件,在这些文件上按照访问条件执行操作的 实体(identities)或 访问权限(access rights,比如CHV和ADM)
1. 如果所有的访问权限都被授予,那么所有的行为都允许,除了那些NEVEr访问条件的
2. 如果没有任何条件被授予,那么任何行为都不被允许
3. 如果某些条件被允许,只允许那些已定义的实体才允许执行。

STK的访问权限 跟 用户,OTA等实体 的访问权限是无关的,相互独立。
修改用户的CHV1,并不影响STK的权限。

权限级别(priority level):多个stk applet注册同样的事件时,激活的优先级

定时器个数

菜单条目个数

TS123.048中介绍:
访问域由 Access Domain Parameter (ADP) 加上 Access Domain Data (ADD)构成

ADP = 00 时,表示可以完全访问GSM文件系统, ADD为空
ADP = 01 时,表示 APDU级的访问机制, ADD长度为2
ADP = 02 时,表示 3GPP访问机制

参考资料:

Digital cellular telecommunications system (Phase 2+);
Subscriber Identity Module Application Programming
Interface (SIM API) for Java Card;
Stage 2 (ETSI TS 143 019)

Digital cellular telecommunications system (Phase 2+);
Universal Mobile Telecommunications System (UMTS);
Security mechanisms for the (U)SIM application toolkit;
Stage 2 (ETSI TS 123 048)

STK GET INPUT

UICC提示用户应该输入一段信息,用户输入的内容应该由设备透明地传给UICC.
如果UICC提供给你了默认的文字,那么终端在提示用户输入信息时,显示该文字。
用户可以接受,拒绝,编辑文字 作为 响应字符串

提示信息文字可以有3种格式
1. SMS默认字母表的 压缩格式
2. SMS默认字母表的 非压缩格式
3. UCS2 字母表 格式

UICC可以给出 期望输入的字符串的最大和最小长度

用户输入的字符串
1. 只含有数字: 0 到 9, *, # 和 +
2. SMS字符表 或 UCS2

Command Qualifier

bit 1:
0 = digits (0 to 9, *, #, and +) only;
1 = alphabet set.

bit 2:
0 = SMS default alphabet;
1 = UCS2 alphabet.

bit 3:

0 = terminal may echo user input on the display;
1 = user input shall not be revealed in any way (显示**密码).

bit 4:
0 = user input to be in unpacked format;
1 = user input to be in SMS packed format.

bits 5 to 7: = RFU.

bit 8:
0 = no help information available;
1 = help information available.

Command Qualifier 应该设置为 1 或者 3


当终端为 响应 GET INKEY或者GET INPUT命令,发出一个成功的Terminal Response时
它应该提供用户输入的字符串 给UICC

对于GET INPUT, 如果用户输入为空
那么应该设置 Length=01, Value=数据编码(data coding scheme)


真正的null字符串,应该是Length=00, 没有value

数据编码方案 SMS Data coding scheme, 定义在 ETSI TS 123 038 中

推荐这三种编码方案
’00’: GSM default alphabet 7 bits packed;
’04’: GSM default alphabet 8 bits;
’08’: UCS2

Java卡的内存管理

每个实例(intance)放在EEPROM或者RAM里。这依赖于对象(object)的内容
1. 如果对象的内容在卡中从一个session到另一个session都保持不变,它的内容和引用都会保存在EEPROM中,只要它从属的applet 没有被删除。
2. 如果内容会改变,发生下面的事件时会被清零:
CLEAR_ON_RESET
CLEAR_ON_DESELECT


临时内容会存放在RAM中,但它们的引用会放在EEPROM中
内容也保存在EEPROM中的:
所有基本数据类型(byte,short)的全局变量;


实例变量/Instance variables(全局变量/global variables)
在 对象安装时 创建。
局部变量/Local variables:只能在函数内部 或者 代码块 内部访问

可以使用 静态变量时 ,不要用实例变量

但静态变量是类变量, 可能会引起一些安全问题(可能被其他applet访问)


使用new创建出来对象
可以使用 局部的基本类型(byte, short)
但不要使用 局部的 byte[] 和 object
低版本的Java Card没有垃圾回收


常量 加上 static final 关键字
用基本数据类型,而不要对其进行二次封装,因为会带来更多内存消耗

在applet的构造方法里,就把所有的对象都建立好,这样在安装时就能确定自己需要多少资源。


用switch-case代替 if-else

使用复合算术语句而不是单独的赋值