Zoom 简单分析

最近闲来无事儿,找个app学习学习
想起之前某个机缘巧合的情况下,与zoom的大佬们有过简单的交流,大佬们还是很强的。
就它吧!
Zoom Version: iOS 5.6.1(172)

0x00 去除重打包检测相关

直接使用MonkeyDev 重打包,提示‘LLVM Profile Error: “default.profraw”‘并直接退出

直接丢进ida中看,

有检测embedded.mobileprovision 文件,同时瞄到ptrace检测调试, 伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
v4 = objc_msgSend(&OBJC_CLASS___NSBundle, "mainBundle");
v5 = objc_msgSend(v4, "pathForResource:ofType:", CFSTR("embedded"), CFSTR("mobileprovision"));
if ( objc_msgSend(v5, "length") )
exit(0);
v6 = (__int64)dlopen(0LL, 10);
if ( v6 )
{
v14 = (void *)v6;
v15 = dlsym((void *)v6, "ptrace");
if ( v15 )
((void (__fastcall *)(signed __int64, _QWORD, _QWORD, _QWORD))v15)(31LL, 0LL, 0LL, 0LL);
v6 = dlclose(v14);
}
v16 = sub_1000356A0(v6, v7, v8, v9, v10, v11, v12, v13, v22, v23, v24);

绕过也很简单:

1
2
3
4
5
6
7
8
9
10
11
12
%hook NSBundle
- (NSString *)pathForResource:(NSString *)name
ofType:(NSString *)ext
{
if([name isEqualToString:@"embedded"] && [ext isEqualToString:@"mobileprovision"]){
NSLog(@"find embedded.mobileprovision...");
return nil;
}else{
return %orig();
}
}
%end

绕过以后却一直提示这个

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
Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState]
PID: 1829, TID: 61332, Thread name: com.apple.CoreMotion.MotionThread, Queue name: com.apple.root.default-qos.overcommit, QoS: 0
Backtrace:
4 libobjc.A.dylib 0x0000000224f1b6f4 <redacted> + 56
5 CoreMotion 0x000000022b695d9c CoreMotion + 294300
6 CoreMotion 0x000000022b6962cc CoreMotion + 295628
7 CoreMotion 0x000000022b6961dc CoreMotion + 295388
8 CoreMotion 0x000000022b6c401c CoreMotion + 483356
9 CoreMotion 0x000000022b6c4060 CoreMotion + 483424
10 CoreFoundation 0x0000000225caa27c <redacted> + 28
11 CoreFoundation 0x0000000225ca9b64 <redacted> + 276
12 CoreFoundation 0x0000000225ca4e58 <redacted> + 2276
13 CoreFoundation 0x0000000225ca4254 CFRunLoopRunSpecific + 452
14 CoreFoundation 0x0000000225ca4f88 CFRunLoopRun + 84
15 CoreMotion 0x000000022b6c39f4 CoreMotion + 481780
16 libsystem_pthread.dylib 0x0000000225922908 <redacted> + 132
17 libsystem_pthread.dylib 0x0000000225922864 _pthread_start + 48
18 libsystem_pthread.dylib 0x000000022592adcc thread_start + 4

//------------------------
2021-05-14 21:17:42.684256+0800 Zoom[2148:69541] old data has cpoied done
=================================================================
Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState]
PID: 2148, TID: 69718, Thread name: com.apple.CoreMotion.MotionThread, Queue name: com.apple.root.default-qos.overcommit, QoS: 0
Backtrace:
4 libobjc.A.dylib 0x0000000224f1b6f4 <redacted> + 56
5 CoreMotion 0x000000022b695d9c CoreMotion + 294300
...
2021-05-14 21:17:42.964460+0800 Zoom[2148:69718] [reports] Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState]
PID: 2148, TID: 69718, Thread name: com.apple.CoreMotion.MotionThread, Queue name: com.apple.root.default-qos.overcommit, QoS: 0
Backtrace:
4 libobjc.A.dylib 0x0000000224f1b6f4 <redacted> + 56
5 CoreMotion 0x000000022b695d9c CoreMotion + 294300
6 CoreMotion 0x000000022b6962cc CoreMotion + 295628
...
(lldb) bt
* thread #20, name = 'com.apple.CoreMotion.MotionThread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
* frame #0: 0x0000000000000000
frame #1: 0x0000000105731a6c libMainThreadChecker.dylib`__ASSERT_API_MUST_BE_CALLED_FROM_MAIN_THREAD_FAILED__ + 812
frame #2: 0x0000000105731e0c libMainThreadChecker.dylib`checker_c + 412
frame #3: 0x00000001057316b0 libMainThreadChecker.dylib`trampoline_c + 76
frame #4: 0x00000001056f145c libMainThreadChecker.dylib`handler_start + 52
frame #5: 0x00000001056fce7c libMainThreadChecker.dylib`__trampolines + 47600
frame #6: 0x0000000224f1b6f4 libobjc.A.dylib`-[NSObject performSelector:] + 56
frame #7: 0x000000022b695d9c CoreMotion`___lldb_unnamed_symbol1461$$CoreMotion + 908
...
(phone's lldb)

对于强迫症来说可太难顶了,很快啊,我啪就给丫关了:

对抗

  1. 检测不放在start中,可以多点多次检测
  2. 检测重打包(检测调试状态 也类似)
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
//简单版本:
__attribute__((constructor)) static void (*__result)();

// 垃圾指令
// ...
// __asm__("mov x0, #0\\n"
// "mov x1, #0\\n"
// "mov sp, x1\\n"
// "mov x29, x1\\n"
// "mov x30, x1\\n"
// "mov w16, #1\\n"
// "svc #0x80\\n");
// 也可再加一些垃圾指令
// char asd[]="\\xE0\\x03\\x08\\xAA\\x00\\x10\\x80\\xD2\\x01\\x10\\x80\\xD2\\x00\\x00\\x80\\xd2\\x01\\x00\\x80\\xd2\\x3f\\x00\\x00\\x91\\xfd\\x03\\x01\\xaa\\xfe\\x03\\x01\\xaa\\x30\\x00\\x80\\x52\\x01\\x10\\x00\\xd4";
char dsa[]="\\xB5\\x56\\x5D\\xFF\\x55\\x45\\xD5\\x87\\x54\\x45\\xD5\\x87\\x55\\x55\\xD5\\x87\\x54\\x55\\xD5\\x87\\x6A\\x55\\x55\\xC4\\xA8\\x56\\x54\\xFF\\xAB\\x56\\x54\\xFF\\x65\\x55\\xD5\\x07\\x54\\x45\\x55\\x81\\x55";

__attribute__((constructor)) static void* __objc_alloc(){
void* ptr=mmap(0, 0x100, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE , -1, 0);
if (ptr == MAP_FAILED) {
return nil;
}
memcpy(ptr, dsa, (sizeof(dsa)<0x100?sizeof(dsa):0x99));
char* pptr=(char* )ptr;
for (int k=0; k<(sizeof(dsa)<0x100?sizeof(dsa):0x99); k++) {
pptr[k]=pptr[k]^0x55;
}
int i=mprotect(ptr, 0x100, PROT_EXEC);
if (i!=0){
return nil;
}

__result=ptr;
return __result;
}

__attribute__((constructor)) static void _stack_chk_success(){
__objc_alloc();
NSString* BASE_PATH = [[NSBundle mainBundle] bundlePath];
NSFileManager *myFileManager = [NSFileManager defaultManager];
NSDirectoryEnumerator *myDirectoryEnumerator = [myFileManager enumeratorAtPath:BASE_PATH];

for (NSString *path in myDirectoryEnumerator.allObjects) {
NSData *data = [path dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64String = [data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
#if 1
//embedded.mobileconfig
if ([base64String isEqualToString:@"ZW1iZWRkZWQubW9iaWxlcHJvdmlzaW9u"]){
// __result();
pthread_t thread;
pthread_create(&thread, NULL, __result, NULL);
}
#endif
}
}
  1. 最近看到一个开源库做安全性检测的https://github.com/securing/IOSSecuritySuite, 粗略看了下检测的还算OK,做开发的朋友们有没有用过的 <wink~>

ETC:

其实除了重打包检测和反调试以外,还有越狱检测,但是越狱检测可以直接使用现有的工具(比如Liberty…)直接绕过,这里就不多说了。

0x01 抓包

解决了相关检测问题后,就可以直接调试应用了。既然要看登陆过程,那么抓包就是不可避免的一个步骤。先尝试了直接使用frida hook ssl read/write ,结果什么也没有,就很郁闷。尝试使用Charles设置代理抓包,结果发现:

原来是存在证书校验,具体可以使用wireshark查看:

可以看出是在server hello 完成后客户端检测到证书不匹配,既然问题出在客户端,那么就还是好解决的,拿现成的工具比如ssl kill switch直接干掉就完了,完事收工。

哈哈哈哈,当然不可能这么简单,不然还写什么写。用了ssl kill switch 后也是不起作用的,然后又试了自己hook NSURLSession、Security等系列函数中的检测部分,结果也还是不行,我emo了家人们,多吓人呐。当时在这里卡了挺久,没想到什么原因,更别提解决办法了,就想着先用ida搜搜关键字看能不能找到些线索。

1. 明确无法抓包的原因

在浏览字符串的时候看到了openssl相关关键字,然后搜了下,还挺多:

瞬间就有了点什么想法,立马扭头跟大哥JKSun请教一番。该说不说,大哥就是大哥,很快就帮我确认了我的想法,这丫静态编译的openssl进去,并非使用系统库,难怪各种hook都不起作用。同时还看到在项目目录下有curl7.74.0相关代码目录,这里留意下暂且不表。

2. 解决方式

2.1 一些奇妙的知识

HTTPS经由HTTP进行通信,但利用SSL/TLS来加密数据包。这里https的原理部分不打算细讲,简单说说SSL的握手部分,搞明白这部分是我们后面操作的基础。闲话不多说,先看张图:

http://www.ruanyifeng.com/blogimg/asset/2014/bg2014092003.png

这张图讲解了tls的握手流程,我们这里需要注意几点:

  1. client向server端发送ClientHello数据包(包含支持的ssl版本等基础信息,以及一个随机数 random_num_client)
  2. server端在收到ClientHello后会向client发送ServerHello数据包(包含最终使用ssl配置等相关信息、server的publicKey和证书,以及一个随机数 random_num_server(如果设置了双向校验相关选项,这里server还会向client发送ClientCertificateRequest,后续会校验))
  3. client收到ServerHello相关信息后,会使用RSA或DH算法生成第三个随机数(称之为Premaster Secret)然后使用server的publicKey加密后发送给server端
  4. client和server都知道这三个随机数,且PreMasterSecret 在传输过程中是加密的。利用这三个随机数生成MasterSecret进而生成后续数据传输中使用的SessionKey,我们用下面这张图来表示

想要解开https数据包,我们就得获得这个握手过程中的PreMasterSecret 或者是MasterSecret。

在握手过程中,除了交换random 生成(Pre)MasterSecret 之外,还有个重要的任务就是进行身份认证,也就是我们常说的证书校验。身份认证也分两种:单向认证和双向认证,一般情况下只需要进行单向认证也就是client确认server的身份即可,对于一些重要的业务场景则需要client和server互相确认身份。

2.2 具体方式

自己编译一份对应版本的openssl库,通过特征比对找到对应函数,尔后便可以解决抓包。机灵的小伙伴可以使用ida的sdk生成对应的sig文件,然后就可以直接识别了,我当时好像不太聪明的样子一个一个手动找的特征QaQ

结合上面奇妙的知识,我们可以用的方法就多,具体操作有:

  • 通过hook获取master key 可以解开https的加密数据 –> 可以直接使用wireshark 抓包

  • 绕过证书校验部分, 用burp等工具可以直接抓包 –> 通过burp等代理抓包工具抓包

  • hook ssl read/write 直接获取通讯数据

    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

    //0x01 get client_random, master_secret
    var client
    var master
    // zoom_561_172
    // 0xB945B8
    // 先通过0xB61518 tls1_PRF 确定版本相关信息,再直接hook 获取master scret
    Interceptor.attach(base.add(0xB6176C), {
    onEnter: function (args) {
    var oob = Memory.readU64(args[0].add(0xa8))
    client = oob.add(0xb8)
    master = args[1]
    },
    onLeave: function (retval) {
    console.log("CLIENT_RANDOM", arraybuffer2hexstr(Memory.readByteArray(ptr(client), 32)), arraybuffer2hexstr(Memory.readByteArray(ptr(master), 48)))
    }
    })


    //0x02 bypass 证书校验
    //0x100B39B4C -- ssl_verify_cert_chain
    Interceptor.attach(base.add(0xB39B4C), {
    onEnter: function (args) {
    console.log(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');
    console.log('call ssl_verify_cert_chain')
    },
    onLeave: function (retval) {
    console.log("ret:", retval)
    retval.replace(1)
    }
    })

    function ssl_session_get_id_func(ssl, len) {
    if (len) {
    var lenn = Memory.readU64(ptr(ssl.add(0x150)))
    Memory.writeU32(len, lenn)
    }
    return ptr(ssl.add(0x158))
    }
    // ; SSL_SESSION *__cdecl SSL_get_session(const SSL *ssl)
    // __text:0000000000025E64 EXPORT _SSL_get_session
    // __text:0000000000025E64 _SSL_get_session ; CODE XREF: _SSL_copy_session_id+18↑p
    // __text:0000000000025E64 ; _ssl_validate_ct+D4↑p ...
    // __text:0000000000025E64 00 88 42 F9 LDR X0, [X0,#0x510]
    // __text:0000000000025E68 C0 03 5F D6 RET
    // __text:0000000000025E68 ; End of function _SSL_get_session
    function ssl_get_session_func(ssl) {
    return Memory.readU64(ssl.add(0x510))
    }


    function getSslSessionId(ssl) {
    var session = ssl_get_session_func(ssl);
    if (session == 0) {
    return 0;
    }
    var len = Memory.alloc(4);
    var p = ssl_session_get_id_func(session, len);
    len = Memory.readU32(len);
    var session_id = "";
    for (var i = 0; i < len; i++) {
    session_id += ("0" + Memory.readU8(p.add(i)).toString(16).toUpperCase()).substr(-2);
    }
    return session_id;
    }

    function arraybuffer2hexstr(buffer) {
    var hexArr = Array.prototype.map.call(
    new Uint8Array(buffer),
    function (bit) {
    return ('00' + bit.toString(16)).slice(-2)
    }
    )
    return hexArr.join('');
    }

    // 0x05 SSL_read 0xB40E38
    Interceptor.attach(base.add(0xB40E38), {
    onEnter: function (args) {
    this.message = "SSL_read"
    this.buf = args[1]
    console.log('call 0xB40E38 ssl_read', args[2])
    console.log(" -> ssl_session_id is:", getSslSessionId(args[0]))
    },
    onLeave: function (retval) {
    console.log("SSL_read ret:", retval)
    var byteArray = Memory.readByteArray(this.buf, parseInt(retval))
    console.log(byteArray, '\n')
    }
    })

    // 0x06 SSL_write 0xB40FEC
    Interceptor.attach(base.add(0xB40FEC), {
    onEnter: function (args) {
    //bad command
    console.log('call 0xB40FEC ssl_write', args[0], args[2])
    console.log(" ->ssl_session_id is:", getSslSessionId(args[0]))
    var byteArray = Memory.readByteArray(args[1], parseInt(args[2]))
    console.log(byteArray)
    },
    onLeave: function (retval) {
    console.log("SSL_Write ret:", retval, '\n')
    }
    })

经过测试三种方式都是可以正常抓包的,这里贴一下抓包内容:

通过zoom.com 重定向到zoom.us,但是抓包的内容是一样的。下面我们就登陆部分做个简单分析

0x02 登陆字段分析

登陆字段的计算部分都是用的C++写的,对这种无符号的代码,分析起来也没什么技巧,就是头铁。在识别加密算法的时候,由于经验不足,就直接上搜索引擎上去搜关键变量,然后结合openssl源码找特征。

总之能hook的就hook,不能hook的再调试分析。

通过捕获的数据包可以看到login相关字段如下:

  1. ZM-CID==deviceId==cid==hash_macaddress
  2. Cookies ==>ZM-SESS-KEY + srid(SaaSbeeTestMode00123578)
  3. password
  4. ecp
  5. ZM-DID

在分析这些字段之前,我们还需要一个小tips,zoom中使用了自定义的string结构,姑且命名为CmmCstringT,结构大致如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// --------------------------------------------------
// |00|01|02|03|04|05|06|07|08|09|0a|0b|0c|0d|0e|0f|
// --------------------------------------------------
// addr Cmm::CStringT | str or ptr2str
// len | |flag

QWORD *__fastcall Cmm_CStringT(_QWORD *a1, __int64 a2)
{
_QWORD *v2; // x20

v2 = a1;
*a1 = &off_10268E010;
a1[1] = 0LL;
a1[2] = 0LL;
a1[3] = 0LL;
if ( a2 )
std::__1::basic_string<char,std::__1::char_traits<char>,std::__1::allocator<char>>::assign();
return v2;
}

我们hook这个Cmm_CStringT函数,可以得到蛮多有趣的消息。

hash_macaddress

对于hash_macaddress字段直接hook 对应关键字即可分析出流程,

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
void __fastcall generateDeviceIdentifier(void *bundleIdentifier)
{
void *v1; // x19
__int64 v2; // x22
char *v3; // x19
void **v4; // [xsp+10h] [xbp-150h]
__int64 v5; // [xsp+18h] [xbp-148h]
__int64 (__fastcall *v6)(); // [xsp+20h] [xbp-140h]
void *v7; // [xsp+28h] [xbp-138h]
__int64 *v8; // [xsp+30h] [xbp-130h]
__int64 v9; // [xsp+38h] [xbp-128h]
__int64 *v10; // [xsp+40h] [xbp-120h]
__int64 v11; // [xsp+48h] [xbp-118h]
void *v12; // [xsp+50h] [xbp-110h]
__int64 v13; // [xsp+58h] [xbp-108h]
char v14[16]; // [xsp+C0h] [xbp-A0h]
__int128 v15; // [xsp+D0h] [xbp-90h]
__int128 v16; // [xsp+E0h] [xbp-80h]
__int128 v17; // [xsp+F0h] [xbp-70h]
char v18; // [xsp+100h] [xbp-60h]
char v19[32]; // [xsp+108h] [xbp-58h]

if ( bundleIdentifier )
{
v1 = bundleIdentifier;
if ( objc_msgSend(bundleIdentifier, "length") )
{
v9 = 0LL;
v10 = &v9;
v11 = 584383987712LL;
v12 = &unk_10249643D;
CC_SHA256_Init(&v13);
v4 = _NSConcreteStackBlock;
v5 = 0xC2000000LL;
v6 = sub_100312B08;
v7 = &unk_102697FC0;
v8 = &v9;
objc_msgSend(v1, "enumerateByteRangesUsingBlock:", &v4);
CC_SHA256_Final();
v2 = 0LL;
v18 = 0;
v16 = 0u;
v17 = 0u;
*(_OWORD *)v14 = 0u;
v15 = 0u;
v3 = v14;
do
{
__sprintf_chk(v3, 0, 0xFFFFFFFFFFFFFFFFLL, "%02x", (unsigned __int8)v19[v2++]);
v3 += 2;
}
while ( v2 != 32 );
v18 = 0;
objc_msgSend(&OBJC_CLASS___NSString, "stringWithUTF8String:", v14);
_Block_object_dispose(&v9, 8);
}
}
}
----> +[ZPLogHelper infoLog:withString] UIDevice getDeviceIdentifier from DB/Keychain: a2458afb3e281029a1b9635b0e271d1301e246176b4e8710666ad4bd70866f37
通过对base64指定结果进行栈回溯可以看到调用流程,以及加密deviceid的位置sub_1013F7BA4

这里直接写结果:

  1. 第一次使用app, 生成DeviceIdentifier,存入keychain/DB, 后续都直接取这个值使用

    1
    2
    3
    4
    5
    6
    # 第一次启动app的时候,使用这种方式生成id
    def gen_identifier(bundleid):
    _str=str(uuid.uuid1())+bundleid
    _out=hashlib.sha256(_str.encode()).hexdigest()
    print (_out)
    return _out
  2. 计算hash_macaddress 字段的值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #  这个identifier 是可以直接从keychain 中读取的, 利用keychain_dumper 或 frida 
    # 从keychain 中读取64位字符的id当作字符串传入即可
    def compute_hash_macaddress(identifier):
    if isinstance(identifier, str):
    identifier = identifier.encode()
    a=hashlib.sha256(identifier).hexdigest()
    b=hashlib.sha256(bytes.fromhex(a)).hexdigest()
    c=hashlib.sha256(bytes.fromhex(b)).hexdigest()
    result=base64.b64encode(bytes.fromhex(c))
    print (result)
    return result

ZM-SESS-KEY

==》读取hash_macaddress的值,将‘+’‘=’等符号转义,再拼接一个32位的随机字节数组

  1. 计算拼接后的内容的sha256,记作_input
  2. 使用内置公钥,RSA加密_input,得到密文 _encd
  3. 将 _encd base64编码,再拼接’,v1’,得到最终数据,用作Cookie中的ZM-SESS-KEY 字段,再拼接一些常量即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def RSA_enc():
a=compute_hash_macaddress(DEVICE_ID)
# b=random.randbytes(32)
b = bytes([random.randrange(0, 256) for _ in range(0, 32)])
_in=a+b
# print ("_in:", _in, type(_in))
c=hashlib.sha256(_in).hexdigest() # 记住这个sha256 的值
PASSWD_KEY=c
print (c)
_rsa_encd=openssl_rsa_enc(c)
_out=base64.b64encode(_rsa_encd)
_out=_out+b',v1'
_out=quote(_out.decode())
print(_out)

password

password和ecp字段使用的方式都是一样的,取一个key来加密我们输入的password,ecp的key 是生成的一个udid。

但是passwprd字段的key却让我一通好找,最终使用的方式是在进入函数分配好内存(new(0xC28uLL)) 后便对存储key的区域下内存写入断点,断下后再查看调用栈,最终明白使用的key便是计算ZM-SESS-KEY 字段时的加密数据明文。

==》

  1. 使用计算Cookie字段中的_input 当作是AES 加密使用的key
  2. 将上面的key计算sha256,所得结果用作aes_key,将aes_key计算hash用作iv
  3. iv长度与分块大小一致,恒为16, 使用的aes加密模式为aes_128_cbc,所以aes_key 实际上为sha256(_input)[0:16]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

def AES_enc(_key, _passwd):
if isinstance(_key, str):
_key=_key.encode('utf-8')
_aes_key=hashlib.sha256(_key).hexdigest()
# print (_aes_key)
_aes_iv=hashlib.sha256(bytes.fromhex(_aes_key)).hexdigest()
# print (_aes_iv)
cipher=AES.new(bytes.fromhex(_aes_key)[0:16], AES.MODE_CBC,bytes.fromhex(_aes_iv)[0:16])
x=__BLOCK_SIZE_16 - (len(_passwd) % __BLOCK_SIZE_16)
if x!=0:
_passwd=_passwd+chr(x)*x
_enc = cipher.encrypt(_passwd)
_out=base64.b64encode(_enc)
print(_out)
return _out

看到这里,很明显的数据使用对称加密算法加密、密钥使用非对称算法加密。

脚本丢在GitHub上了。

0x03 “加入会议” 功能分析

参考链接

  1. frida 官方文档
  2. LLDB常用命令–飘云整理
  3. openssl evp 对称加密(AES_ecb,ccb)
  4. Openssl Doc
  5. 图解SSL/TLS协议
  6. SSL/TLS原理详解
  7. Transport Layer Security (TLS) Session Hash andExtended Master Secret Extension
  8. SSL/TLS协议详解
  9. C++ OpenSSL Verify X509 Certificate
  10. IoT设备逆向工程中的函数识别
作者

vlinkstone

发布于

2021-07-03

更新于

2021-09-28

许可协议

评论