阳光惠生活逆向
注入代码
疑似使用梆梆的加固,不能使用Xposed, Frida注入,即使使用frida-gadget也会被检测到。so文件也做了加固,无法直接lief添加依赖so文件(部分so可以添加成功,但APP启动的时候不会加载)。
后来想到可以通过替换so文件,将原来的so文件加到自己的so中,这样APP运行时就会先加载自己的so,同时不影响APP正常运行。
注入的框架依然是自己魔改的SandHook。
定位网络请求
首先hook了StringBuilder类的toString方法,但APP白屏崩溃,于是进行过滤,只打印”cmps.cebbank.com”相关内容,同时查看堆栈信息,发现使用了类似支付宝的RPC方案。
寻找signKey
计算签名的方式很简单: sign = md5(signKey + content)
由于之前逆向过支付宝的签名方法,偶然在内存中发现了signKey,于是我这次还是尝试通过搜索内存的方式获取signKey,但我使用的SandHook没有编译native 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
| static void find(uint8_t *p, unsigned long memsize) { for (int i = 0; i < memsize-50; ++i) { if (p[i+32]=='&' && p[i+33]=='O' && p[i+34]=='p' && p[i+41]=='n' && p[i+42]=='-' && p[i+47]=='='){ LOGD("FOUND: %s", p+i); } } }
static int scan_maps() { char line[PATH_MAX]; unsigned long start, end; char buf[PATH_MAX]; char path[PATH_MAX]; char tmp[100]; FILE *fp; char maps[] = "/proc/self/maps"; fp = fopen(maps, "r"); if (fp == NULL) { LOGE("cannot open %s", maps); return -1; }
while (fgets(line, PATH_MAX - 1, fp) != NULL) { if (strstr(line, "libc_malloc") == NULL) continue; sscanf(line, "%lx-%lx %s %s %s %s %s", &start, &end, buf, tmp, tmp, tmp, path); uint8_t *buffer = (uint8_t *) start; unsigned long memsize = end - start; LOGD("start: %x, size:%d", buffer, memsize); find(buffer, memsize); }
LOGD("done..."); fclose(fp); return 0;
}
void *myfunc(void * arg) { while(1){ scan_maps(); sleep(5); } return NULL; }
static int start() __attribute__((constructor));
int start() { pthread_t pthid; pthread_create(&pthid, NULL, myfunc, NULL); return 0; }
|

网络请求加解密
加密方法在com.alipay.mobile.common.transport.http.selfencrypt.ClientRpcPack。
实际还是调用了so中的加密。一番逆向后,发现是SM4。密钥生成依赖传入的16字节随机字符,通过hook使其固定,生成的密钥就不会再改变。
简要流程:
- 生成密钥
- 使用RSA加密密钥,本地保留一份密钥明文
- 加密数据(SM4-128-CBC)
- 发送加密密钥+加密数据


通过加密流程看似简单,但实际踩了不少坑。
因为是CBC加密,必须有IV,由下图可知,使用一组已知明文、密钥的加密结果,可以将IV设置为全0的,反推出IV。
但一直没成功,于是动态调试SO,发现IV确实是固定的,也是我推算出的值。原来是被Python坑了。。。


| iv = '\xf4' +'\x0f'*15 print(binascii.hexlify(iv.encode()))
iv = '\xf4' +'\x0f'*15 print(binascii.hexlify(iv))
|
最终使用Python实现的加解密:
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
| def num_convert(n): a = (n>>16)&0xff b = (n>>8)&0xff c = n&0xff s = struct.pack('BBB', a,b,c) return s
def encrypt(data): if not isinstance(data, bytes): data = data.encode() compress_data = gzip.compress(data) key = binascii.unhexlify('7ED8274FCD3C133A253A21E063EAFBD8') iv = binascii.unhexlify('f40f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f') crypt_sm4 = CryptSM4() crypt_sm4.set_key(key, SM4_ENCRYPT) encrypted_data = crypt_sm4.crypt_cbc(iv, compress_data) length = len(encrypted_data) len_data = num_convert(length) result = binascii.unhexlify('03000021031e0a3408cf011170569c89d194cdb6579ec3fd2ff29582e95890385f659db5fd0d') + len_data + encrypted_data return result
def decrypt(data): key = binascii.unhexlify('7ED8274FCD3C133A253A21E063EAFBD8') iv = binascii.unhexlify('f40f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f') crypt_sm4 = CryptSM4() crypt_sm4.set_key(key, SM4_DECRYPT) compress_data = crypt_sm4.crypt_cbc(iv, data) plain = gzip.decompress(compress_data) return plain.decode()
|