在我前面的文章中提到,在首次拿到App的情况下,我选择首先查看日志,因为很多app在编译的时候,还是会残留一些日志信息。
对于有混淆的app来讲,尤其是TCP协议,哪怕app完全没有混淆,定位关键函数都比较麻烦。因此需要借助日志中的关键信息来帮忙定位。
当然,在日志中的关键信息无法准确定位的情况下,选择 Android monitor或者查看堆栈信息也都是一些切入点。
在输出日志的情况下,开启fiddler以及wireshark,运行该app,查看日志发现了一些有意思的信息:
Line 2313: D/duoyi_inc( 2213): startRespond cmd: 0x199 Line 2315: I/duoyi_inc( 2213): 0x199 onRespond
Line 2317: I/duoyi_inc( 2213): LoginOp(genLogin) Cipher rsa DECRYPT_MODE begin
Line 2319: I/duoyi_inc( 2213): LoginOp(genLogin) Cipher rsa DECRYPT_MODE end
Line 2321: I/duoyi_inc( 2213): debugTest : LoginOp(genLogin) parse key length: 16
Line 2323: I/duoyi_inc( 2213): LoginOp(genLogin) hexStringToByte begin
Line 2325: I/duoyi_inc( 2213): LoginOp(genLogin) hexStringToByte end
Line 2327: D/duoyi_inc( 2213): sendCmd0x200
Line 2329: I/duoyi_inc( 2213): CCProtocolHandler,startHandle 0 , 60
Line 2331: D/duoyi_inc( 2213): protocolInfo : CCProtocolHandler(handle), pos 0 , 0 fill:60 en index 0
Line 2333: D/duoyi_inc( 2213): CCProtocolHandler,handleCmd cmd= 200 , 60
Line 2335: I/duoyi_inc( 2213): protocolInfo : ReadBuffer(clear),net: false
Line 2337: D/duoyi_inc( 2213): startRespond cmd: 0x200
Line 2339: I/duoyi_inc( 2213): loginInfo : 0x200, respond, info=time=1565762801, uid=0, digitID=, result =0 _notice=帐号错误,请重新输入
Line 2341: D/duoyi_inc( 2213): protocolInfo : CCNodeServer(disconnect) : start clear data
Line 2343: I/duoyi_inc( 2213): protocol_info : BaseLoopThread(run) :onThreadLoopFinish
try { aa.d("LoginOp(genLogin) Cipher rsa DECRYPT_MODE begin");
Cipher v5 = Cipher.getInstance("RSA");
v5.init(2, ((Key)v1_1));
v10 = v5.doFinal(new Decoder.a().a(v0_4));
aa.d("LoginOp(genLogin) Cipher rsa DECRYPT_MODE end");
v6 = 0;
v5_1 = 0;
v0_12 = 0;
v1_2 = ((byte[])v4);
goto label_93;
}
this.g.r_(); aa.d("new start login");
aa.d("keyPairGenerator generateKeyPair creat begin");
v0_1 = KeyPairGenerator.getInstance("RSA");
v0_1.initialize(512);
v1 = v0_1.generateKeyPair();
aa.d("keyPairGenerator generateKeyPair creat end");
v0_2 = v1.getPublic();
v1_1 = v1.getPrivate();
dp.Auto_setValue(0);
if (dp.a(this.a.f(), new StringBuilder().append(v0_2.getModulus()).append(",").append(v0_2.getPublicExponent()).toString())) {
aa.c("YGD LoginOP, genLogin, 0x199 is time out");
this.a.k().c = -10;
this.a.k().d = "";
this.f.d();
v2 = false;
}else {
aa.d("LoginOp(genLogin) Cipher rsa DECRYPT_MODE begin");
v5 = Cipher.getInstance("RSA");
v5.init(2, v1_1);
v1.Null_<init>();
v10 = v5.doFinal(v1.a(this.a.k().t));
aa.d("LoginOp(genLogin) Cipher rsa DECRYPT_MODE end");
v6 = 0;
v5 = 0;
v0 = 0;
v1 = v4;
while ((v6 < v10.length())) {
if (v0) {
v1[v5]=v10[v6];
v5++;
}
if ((!v10[v6])&&(!v0)) {
v1_2 = new byte[((v10.length()-v6)-1)];
aa.d("debugTest", new StringBuilder().append("LoginOp(genLogin) parse key length: ").append(v1_2.length()).toString());
v0 = v2;
}
v6++;
}
if (!v1) {
aa.a("debugTest", "LoginOp(genLogin) :key is null error ");
v2 = false;
}else {
aa.d("LoginOp(genLogin) hexStringToByte begin");
v0_3 = j.a(v1);
aa.d("LoginOp(genLogin) hexStringToByte end");
this.a.k().t = "";
m.a().a(v0_3);
d.a().a(v0_3);
ba.Auto_setValue(0);
if (ba.a(this.d)) {
this.a.k().c = -10;
this.a.k().d = "";
this.f.d();
v2 = false;
......
00b0019900000000000000000000a131323534393531393534383237393638323936313236323539343730333237393934313238333336393734323737343236333839383635363731363432353031323931313434313936393130313431343134393739363437343432393637313736313038343431323535373139363938393038313736333130323537363733303438303637373936383337383639373931353633353230343636372c3635353337
00 b0 01 99
00 00 00 00
00 00 00 00
00
00 a1
31323534393531393534383237393638323936313236323539343730333237393934313238333336393734323737343236333839383635363731363432353031323931313434313936393130313431343134393739363437343432393637313736313038343431323535373139363938393038313736333130323537363733303438303637373936383337383639373931353633353230343636372c3635353337
try { aa.d("keyPairGenerator generateKeyPair creat begin");
KeyPairGenerator v0_2 = KeyPairGenerator.getInstance("RSA");
v0_2.initialize(0x200);
KeyPair v1 = v0_2.generateKeyPair();
aa.d("keyPairGenerator generateKeyPair creat end");
PublicKey v0_3 = v1.getPublic();
v1_1 = v1.getPrivate();
v0_4 = ((RSAPublicKey)v0_3).getModulus() + "," + ((RSAPublicKey)v0_3).getPublicExponent();
}
006c019900000000000000000000584469544c6357534e79786e48655a72433868686c67496557616e4d6a446c422b2b4b6a4c2b727230417a4b656f5036756855497a655470314a674f6e686e683851354773706b47755663334c65366f3454366b4957773d3d5d5956d600
00 6c 01 99
00 00 00 00
00 00 00 00
00
00 58
4469544c6357534e79786e48655a72433868686c67496557616e4d6a446c422b2b4b6a4c2b727230417a4b656f5036756855497a655470314a674f6e686e683851354773706b47755663334c65366f3454366b4957773d3d
5d5956d6
00
try { aa.d("LoginOp(genLogin) Cipher rsa DECRYPT_MODE begin");
Cipher v5 = Cipher.getInstance("RSA");
v5.init(2, ((Key)v1_1));
v10 = v5.doFinal(new Decoder.a().a(v0_4));
aa.d("LoginOp(genLogin) Cipher rsa DECRYPT_MODE end");
v6 = 0;
v5_1 = 0;
v0_12 = 0;
v1_2 = ((byte[])v4);
goto label_93;
}
int __fastcall Java_com_duoyiCC2_jni_CCJNI_SetKeyN(JNIEnv *a1, jobject a2, int a3, char *a4) {
result = ((*a1)->GetArrayLength)();if ( result == 8 )
{
v8 = ((*v6)->GetByteArrayElements)(v6, v5, 0);CEncryptor::SetKey((&en + 12 * v4), v8);
result = ((*v6)->ReleaseByteArrayElements)(v6, v5, v8, 0);}
return result;
}
int __fastcall CEncryptor::SetKey(CEncryptor *this, char *key) {
memcpy(this + 4, key, 8u);
index = 0;
v4 = 0;
do
{
v5 = this + index;
result = index << 8;
LOBYTE(v4) = CEncryptor::s_keybox[256 * index + (v4 ^ *(this + index + 4))];
++index;
*(v5 + 4) = v4;
}
while ( index != 8 );
return result;
}
00 43 02 00
00 00 00 00
00 00 00 00
00
03 1e
00 07
38 37 33 37 30 38 34
00 00
00 20
65 34 36 30 39 30 36 38 34 39 34 62 36 31 65 30 33 35 38 30 34 37 33 32 31 32 30 63 33 65 65 33
00 00 00 00
00 03
00
设备ID=MD5("865166023795217pd8633666688247ee4ec0a6604c85b300:81:f2:9e:68:ecnull") 865166023795217为imei
pd8633666688247算法:
"pd" + Build.BOARD.length() % 10 + Build.BRAND.length() % 10 + Build.DEVICE.length() % 10 + Build.HOST.length() % 10 + Build.ID.length() % 10 + Build.MANUFACTURER.length() % 10 + Build.MODEL.length() % 10 + Build.PRODUCT.length() % 10 + Build.TAGS.length() % 10 + Build.TYPE.length() % 10 + Build.USER.length() % 10;
ee4ec0a6604c85b3为android_id
00:81:f2:9e:68:ec为网卡地址
00 37
02 00
00 00 00 00 00 00 00 00 00
00 10 35 63 30 64 30 36 30 33 37 33 30 65 32 35 35 62
5d 57 9a a6
01 35 56 2b
00 07 38 37 33 37 30 38 34
00 00 00 00 00 00 00
00 ab 02 01
01 35 56 2b
00 00 00 00
00
00 20 34 66 33 66 39 64 35 35 32 30 32 33 36 30 32 36 64 37 61 32 62 36 37 30 66 66 33 35 37 64 62 65
01 03
00 06 32 2e 34 2e 32 37
00 70
00 34 68 61 72 64 77 61 72 65 3a 20 78 69 61 6f 6d 69 20 36 3b 6f 73 3a 20 61 6e 64 72 6f 69 64 20 35 2e 31 2e 31 3b 63 6f 6d 70 75 74 65 72 3a 78 69 61 6f 6d 69
00
00 30 7b 22 63 6c 69 65 6e 74 5f 69 64 22 3a 22 65 34 36 30 39 30 36 38 34 39 34 62 36 31 65 30 33 35 38 30 34 37 33 32 31 32 30 63 33 65 65 33 22 7d
00 02 7b 7d
00 00 00
00 62 02 01
00 00 00 00
00 00 00 00
00
00 00
00
00 36 ......
switch(result) { case 0: {
arg8.d();
arg8.k();
arg8.k();
int v2 = arg8.d();
aa.c("personalAccount", "NsLogin(onRespond): " + v2);
this.m_service.k().y = v2;
this.mProtocolHandler.setCurState(1);
break;
}
case 8450: {
this.m_service.k().c = -13;
arg8.k();
v2_1 = arg8.j();
aa.g("测试", "NsLogin(onRespond): 2102->" + v2_1 + "(" + arg8.j() + ")");
this.m_service.k().p = v2_1;
break;
}
case 8454: {
this.m_service.k().d = arg8.k();
this.m_service.k().c = -12;
break;
}
case 8456: {
arg8.k();
arg8.j();
arg8.j();
v2_1 = arg8.j();
aa.d("mirror_zh", "NsLogin:onRespond:137:cellPhone=" + v2_1);
this.m_service.k().j = v2_1;
this.m_service.k().c = -24;
break;
}
case 8457: {
this.m_service.k().d = arg8.k();
this.m_service.k().c = v4;
break;
}
default: {
this.m_service.k().d = arg8.k();
this.m_service.k().c = v4;
break;
}
}
int __fastcall Java_com_duoyiCC2_jni_CCJNI_EncryptRangeN(JNIEnv *env, jobject thiz, jint object, jint position, jint length, jbyteArray *data) {
obj = object;
pos = position;
v8 = env;
if ( data + length > ((*env)->GetArrayLength)() )
return _android_log_print(6, "duoyi_cc", "Encrypt range fail");
v9 = ((*v8)->GetByteArrayElements)(v8, pos, 0);CEncryptor::EncryptRange(&en + 12 * obj, v9, length, data);
return ((*v8)->ReleaseByteArrayElements)(v8, pos, v9, 0);
}
int __fastcall CEncryptor::EncryptRange(int result, char *ele, jint length, char *data) {
v4 = &data[length];
if ( length < v4 )
{
v5 = &ele[length];
v6 = *result;
v7 = 0;
do
{
v7[v5] = CEncryptor::s_encryptbox[256 * v6 + (v7[v5] ^ *(result + v6 + 4))];
++v7;
v6 = (*result + 1) & 7;
*result = v6;
}
while ( v7 != &v4[-length] );
}
return result;
}
loc_156A ADDS R2, R0, R5
LDRB R2, [R2,#4] ; 加载一个密钥
LSLS R5, R5, #8
MOV R12, R2
LDRB R2, [R1,R4] ; 加载一个明文字符
MOV R3, R12
EORS R2, R3 ; 密钥与明文字符异或得到R2
ADDS R5, R5, R2 ; R2与R5相加得到偏移R5
LDRB R2, [R5,R7] ; 在密钥表中找到偏移为R5的字符R2
STRB R2, [R1,R4] ; R2放入结果中
LDR R5, [R0]
ADDS R4, #1
ADDS R5, #1
ANDS R5, R6
STR R5, [R0]
CMP R4, R8
BNE loc_156A
tmpKey = RSA(server_send_base64);
length = tmpKey.length - 1;
key = setKey(tmpKey[length - 8 : length]);
看雪ID: 小堆
https://bbs.pediy.com/user-717647.htm
推荐文章++++
* App免Root加载Xposed插件工具Xpatch源码解析(二)