Dy设备注册激活算法分析
本篇所讲调用libEncryptor.so进行注册激活设备,附上分析及部分核心JAVA代码片段提供参考。 抖音设备版本15.0.0
看下设备注册的包:
POST https://log-lq.snssdk.com/service/2/device_register/?device_id=56581003169& is_activated=1&aid=1128&tt_data=a&version_code=12.4.0&app_name=aweme&app_version=12.4.0&vid=84185501-88C0-4E38-BA3D-F3656A073020&device_id=56581003169&channel=App%20Store&mcc_mnc=46001&aid=1128&screen_width=1125&openudid=690af6a89ae78c69306c8a07bca241b840sd156&cdid=AD072FE4-41B7-4890-BEBC-2D57A4F3F253&os_api=18&ac=4G&os_version=12.4&build_number=123011&device_platform=android&iid=3125112636576938&device_type=Huawei%C8600&is_vcd=1 HTTP/1.1 Host: log-lq.snssdk.com Connection: keep-alive Content-Length: 856 Accept: application/json Cookie: odin_tt=458f8eb0d95b7b9adb4b6fc6591918bfb996096967a7aa4305bd81b5150a8199d2e29ed21883cdd7709c5beaa2be3baa; sessionid=66e5128a179ef5f03f5187bae265bf9c; sessionid_ss=ff5128a179ef5f03f5187baecss22; sid_guard=d2da128a179ef5f03f5187bae265bf9c%7C1598662639%7C5183349%7CWed%2C+28-Oct-2020+00%3A46%3A28+GMT; sid_tt=66e5128a179ef5f03f5187bae265bf9c; uid_tt=fsffdc1fa00bb0ddddd400f58d5795df3; uid_tt_ss=232ec1fa00bb0ddddd400f58d5795321; install_id=3125112636576938; ttreq=1$432133fb80de2e267dd1a8478e5394cdd0cd23d1 Content-Type: application/octet-stream;tt-data=a X-SS-Cookie: install_id=3125112636576938; ttreq=432133fb80de2e267dd1a8478e5394cdd0cd23d1; sessionid=ff5128a179ef5f03f5187baecss22; sessionid_ss=ff5128a179ef5f03f5187baecss22; sid_guard=ff5128a179ef5f03f5187baecss22%7C1598662639%7C5183349%7CWed%2C+28-Oct-2020+00%3A46%3A28+GMT; sid_tt=ff5128a179ef5f03f5187baecss22; uid_tt=d36ec1fa00bb0ddddd400f58d5795df3; uid_tt_ss=d36ec1fa00bb0ddddd400f58d5795df3; odin_tt=a09d8eb0d95b7b9adb4b6fc6591918bfb996096967a7aa4305bd81b5150a8199d2e29ed21883cdd7709c5beaa2be3baa tt-request-time: 15994653422123 User-Agent: Aweme 12.3.0 rv:123015 (iPhone; iOS 12.2; zh_CN) Cronet aid: 1128 x-Tt-Token: 00632e5128a179ef5f03f5187bae265bf9cdded28ab77be5f36e4259d59aa924b6c7f5284cdd07bf788188bb5185d61c32145 sdk-version: 2 passport-sdk-version: 5.12.1 X-SS-STUB: 34C32EDE4A3C45F55403E5EBD44A321B X-SS-DP: 1128 x-tt-trace-id: 00-6123a32f7a09f80897b9fe6e7cc5490468-62a08f7a09f32457-01 Accept-Encoding: gzip, deflate, br X-Khronos: 1599465342 X-Gorgon: 040802f0000e09020319e1d6be6194bfbd32024c25831c86bce
抖音判断一台设备无非就是根据手机的IMEI,IMSI,MAC,系统版本型号之类的来判断时候同一台设备
我们利用JADX来逆向,关键函数在ttEncrypt方法:
func IIDEncrypt(plainStr []byte) []byte { CplainStr := C.CString(string(plainStr)) defer C.free(unsafe.Pointer(CplainStr)) plainlength := len(plainStr) libPathC := C.CString(EXTENSION_DIR + OIDB_API) defer C.free(unsafe.Pointer(libPathC)) encryptedLength := plainlength + 4 + (16 - plainlength%16) CKeyStr := C.CString("!*ss!_defaul%t54K&EY") defer C.free(unsafe.Pointer(CKeyStr)) CEncryptedStr := C.encode(libPathC, CplainStr, C.int(encryptedLength), CKeyStr, C.int(20)) defer C.free(unsafe.Pointer(CEncryptedStr)) encryptedStr := C.GoBytes(unsafe.Pointer(CEncryptedStr), C.int(encryptedLength)) //encryptedHexStr := hex.EncodeToString(encryptedStr) return encryptedStr }
char* Encode(char* plain, int plainlength, char* key, int keylength) { //string plain = hexToStr(hexplain); char *xkey; char *xplain; //int keylength = key.length(); //int plainlength = plain.length(); xkey = (char*) malloc(keylength); xplain = (char*) malloc(plainlength); memcpy(xkey, key, keylength); memcpy(xplain, plain, plainlength); unsigned char *out = (unsigned char*) malloc(plainlength + 100); aweme_aes((__int64)xplain, plainlength, (__int64)xkey, keylength, (__int64)out); free(xkey); free(xplain); xkey = NULL; xplain = NULL; // int cryptedStr_length = plainlength + 4 + (16 - plainlength%16); // std::string hexStr = charsToHex(out, cryptedStr_length); // free(out); //out = NULL; return (char*)out; }
通过上面device_register的url我们可以知道,设备注册其实是向抖音提交当前设备的一些信息,例如MAC、SSID、UUID、IMEI、IMSI、WIFIMAC、device_brand、device_model、device_board、device_type、openudid、clientudid、build_serial等等来注册得到 device_id和install_id。
我们模拟调用so核心代码:
public JniDispatchRegister(){ emulator = createARMEmulator(); final Memory memory = emulator.getMemory(); memory.setLibraryResolver(createLibraryResolver()); vm = emulator.createDalvikVM(null); vm.setJni(this); vm.setVerbose(true); ClassPathResource resource = new ClassPathResource(soPath); File soFile = null; InputStream inputStream = null; try { inputStream = resource.getInputStream(); File tempFile = File.createTempFile(soName, ".so"); FileUtils.copyInputStreamToFile(inputStream,tempFile); soFile = tempFile; } catch (IOException e) { e.printStackTrace(); }finally { IOUtils.closeQuietly(inputStream); } DalvikModule dm = vm.loadLibrary(soFile, false); dm.callJNI_OnLoad(emulator); this.module = dm.getModule(); Native = vm.resolveClass(className); Symbol __system_property_get = module.findSymbolByName("__system_property_get", true); MemoryBlock block = memory.malloc(0x10); Number ret = __system_property_get.call(emulator, "ro.build.version.sdk", block.getPointer())[0]; logger.info("sdk=" + new String(block.getPointer().getByteArray(0, ret.intValue()))); }
然后进行设备的注册:
public String callRegisterUrl(Map<String, Object> deviceParam, byte[] requestBody) { String url = "https://log.snssdk.com/service/2/device_register/?ac=wifi&mac_address=C0%3AEE%3AFB%3AD5%3A4B%3A16&channel=oppo&aid=2329&app_name=douyin_lite&version_code=110500&version_name=11.5.0&device_platform=android&ssmix=a&device_type=ONEPLUS+A3000&device_brand=OnePlus&language=zh&os_api=23&os_version=6.0.1&uuid=860046033047160&openudid=d4080df15130f0d9&manifest_version_code=110500&resolution=1080*1920&dpi=480&update_version_code=11509900&_rticket=1600423440596&mcc_mnc=46011&ts=1600423440&app_type=normal&cdid=4dda0a2b-6572-418c-bdfa-89cfd78a3aaf&tt_data=a&os_api=23&device_type=ONEPLUS%20A3000&ssmix=a&manifest_version_code=110500&dpi=480&uuid=860046033047160&app_name=douyin_lite&version_name=11.5.0&ts=1600423440&app_type=normal&ac=wifi&update_version_code=11509900&channel=oppo&_rticket=1600423440787&device_platform=android&version_code=110500&mac_address=C0%3AEE%3AFB%3AD5%3A4B%3A16&cdid=4dda0a2b-6572-418c-bdfa-89cfd78a3aaf&openudid=d4080df15130f0d9&device_id=40178045261&resolution=1080*1920&os_version=6.0.1&language=zh&device_brand=OnePlus&aid=2329&mcc_mnc=46011"; url = URLUtil.replaceParam(url, deviceParam); OkHttpClient client = new OkHttpClient().newBuilder() .build(); MediaType mediaType = MediaType.parse("application/octet-stream;tt-data=a"); RequestBody body = RequestBody.create(mediaType, requestBody); Request request = new Request.Builder() .url(url) .method("POST", body) .addHeader("Host", "log.snssdk.com") .addHeader("Connection", "keep-alive") .addHeader("sdk-version", "1") .addHeader("Content-Type", "application/octet-stream;tt-data=a") .addHeader("User-Agent", "com.ss.android.ugc.aweme/899 (Linux; U; Android 8.2.3; zh_CN; SM-G9650; Build/N2G47H; Cronet/58.0.2991.0)") .build(); try { Response response = client.newCall(request).execute(); String resp = response.body().string(); return resp; } catch (IOException e) { e.printStackTrace(); } return null; }
激活设备:
public void appAlert(Map<String,Object> device_params){ String url = "https://ichannel.snssdk.com/service/2/app_alert_check/?build_serial=37c96a14&timezone=8.0&carrier=CHN-CT&mcc_mnc=46011&sim_region=cn&sim_serial_number=89860320750105912020&device_id=40178045261&ac=wifi&mac_address=C0%3AEE%3AFB%3AD5%3A4B%3A16&channel=oppo&aid=2329&app_name=douyin_lite&version_code=110500&version_name=11.5.0&device_platform=android&ssmix=a&device_type=ONEPLUS+A3000&device_brand=OnePlus&language=zh&os_api=23&os_version=6.0.1&uuid=860046033047160&openudid=d4080df15130f0d9&manifest_version_code=110500&resolution=1080*1920&dpi=480&update_version_code=11509900&_rticket=1600423440642&ts=1600423440&app_type=normal&cdid=4dda0a2b-6572-418c-bdfa-89cfd78a3aaf&req_id=7522ead3-00cb-435c-9376-5dd2e012bc71&os_api=23&device_type=ONEPLUS%20A3000&ssmix=a&manifest_version_code=110500&dpi=480&uuid=860046033047160&app_name=douyin_lite&version_name=11.5.0&ts=1600423440&app_type=normal&ac=wifi&update_version_code=11509900&channel=oppo&_rticket=1600423440803&device_platform=android&version_code=110500&mac_address=C0%3AEE%3AFB%3AD5%3A4B%3A16&cdid=4dda0a2b-6572-418c-bdfa-89cfd78a3aaf&openudid=d4080df15130f0d9&device_id=40178045261&resolution=1080*1920&os_version=6.0.1&language=zh&device_brand=OnePlus&aid=2329&mcc_mnc=46011"; url = URLUtil.replaceParam(url, device_params); OkHttpClient client = new OkHttpClient().newBuilder() .build(); Request request = new Request.Builder() .url(url) .method("GET", null) .addHeader("Host", "ichannel.snssdk.com") .addHeader("Connection", "keep-alive") .addHeader("X-SS-REQ-TICKET", device_params.get("_rticket").toString()) .addHeader("sdk-version", "12") .addHeader("User-Agent", "com.ss.android.ugc.aweme.lite/110500 (Linux; U; Android 6.0.1; zh_CN; ONEPLUS A3000; Build/MMB29M; Cronet/TTNetVersion:4df3ca9d 2019-11-25)") .build(); try { Response response = client.newCall(request).execute(); } catch (IOException e) { e.printStackTrace(); } }
好了,一些核心的算法就这些,除了调用抖音驱动来做之外,还可以纯算法实现,这里就简单说下实现的几个重点:
获取device_id有几个重点:
(1) carrier、display_name字段:这个字段不是utf-8编码,是GBK编码,要做编码转换
(2) Idfa、VendorID字段:标准UUID算法生成即可
(3) Openudid:随机生成的
device_register接口中post包体是加密的,算法实际上是AES,加密的参数是一些基础信息
实现大致过程:首先使用标准Gzip压缩参数然后调AES进行加密。Android和iOS加密方法相同。
最后关于device_id、xlog、xg04、xg08等算法解析后续将会提供api免费调用(次数限制),关注QdApi吧!
2022年04月12日16:37:06更新
Java开源项目地址:https://gitee.com/ripperTs/tiktok-device-registration 代码仅供学习参考使用
评论
JJJ
回复大佬你好,我想请教一下,我目前能够获取到device_id和install_id 但是使用您文章上面接口来激活并没有用,激活后,使用device_id和install_id仍然是返回空数据,想请教一下又没有解决办法还是我操作不当?
流氓凡
回复@JJJ 确认下xg的算法呢 或者可以qq617498836