图片看不到的话,下载word文档观看吧 App名字:北京时间 加固情况:未加固 分析工具:jeb2.x 雷电模拟器FD 过程 2、 提示邮箱未注册。切换到FD看一下提交表单 3、 通过对比发现,变动的数据 Password:对js有经验的人,可以大致锁定为RSA加密 u_time:时间戳(10位) u_salt:(像MD5的某个数据段) u_sign:(32位MD5+u_salt) 完整表单如下: http://api.btime.com/user/login?protocol=1&mobile=1615457734@qq.com&password=LL8W1uMphsVTIPMClnsHQlAN/dWipeuP/5MHr/%2B/waGTYPRGrWgpvDiTIWcQ2AlXAHyJTwENVw84%0AFRK7xBh07/kdwbHe47L/wPdQoGhHgLaizZLh4QGte6GkYDYk72jhHwtnFsQG4OpusLs1LJ9dd9bq%0AOpkEuaPzXkDQCNw3258%3D%0A&imgcode=&vcode=&os_ver=22&u_time=1499840971&sid=&pro=&src=lx_android&token=4a74dc97f321360008dc126afea827e4&carrier=&push_id=3a466e763b20cf13a5567e962a0d60ae&os_type=Android&net=WIFI&os=android_x86-userdebug%205.1.1%20LMY48Z%20eng.denglibo.20170213.214219%20test-keys&u_salt=d2c032de&u_sign=bc06e9ba4231ff9eeac94f26dbcbe281d2c032de 4、 其实可以明显看出来u_sign和u_salt是有关系的。所以,我们可以先搞定这2个 搜索 u_salt或者u_sign,都可以定位到以下代码,其中2处高亮,分别是salt和sign的算法(先分析salt,再分析sign) private static Map a(aa arg10) { HashMap v4_1; if(arg10 == null) { Map v4 = null; } else { TreeMap v3 = new TreeMap(); if(arg10.d() != null&& ((arg10.d() instanceof q))) { ab v1 = arg10.d(); int v2; for(v2 = 0; v2 <((q)v1).a(); ++v2) { v3.put(URLDecoder.decode(((q)v1).a(v2)), URLDecoder.decode(((q)v1).b(v2))); } } if(arg10.a() != null) { for(v2 = 0; v2 <arg10.a().m(); ++v2) { v3.put(arg10.a().a(v2),arg10.a().b(v2)); } } Stringv6 = f.a(); v3.put("u_salt", v6); StringBuilder v5 = new StringBuilder(); Iterator v8 = v3.entrySet().iterator(); while(v8.hasNext()) { Object v0 = v8.next(); v5.append(((Map$Entry)v0).getKey()); v5.append("="); v5.append(((Map$Entry)v0).getValue()); v5.append("&"); } if(v5.length() > 0) { v5.deleteCharAt(v5.length() -1); } v5.append("&"); v5.append(com.btime.account.a.d); v4_1 = new HashMap(); ((Map)v4_1).put("u_sign",f.a(v5.toString()) + v6); ((Map)v4_1).put("u_salt",v6); } return ((Map)v4_1); } String v6 = f.a(); v3.put("u_salt", v6); u_salt=v6=f.a();进入f.a(下面是整个f的结构。注意看高亮部分) String v1 = f.a(String.valueOf(Math.random())); Math.random() 取随机时间戳,然后调用a,仔细下去看,就发现,a是转换为utf8之后,在调用下一个a也就是MD5。 v4= v1.substring(0, 8);//计算完成之后,取左边8位 综述:u_salt就是随即时间戳的MD5结果的左8位 public class f{ public staticString a(){ int v6= 8; String v1 = f.a(String.valueOf(Math.random())); String v4= v1; try { if(TextUtils.isEmpty(((CharSequence)v4))) { returnv4; } if(v4.length() <= v6){ returnv4; } v4 =v1.substring(0, 8); } catch(Exceptionv0) { v0.printStackTrace(); } return v4; } public staticString a(String arg2) { String v1; try { v1 = f.a(arg2.getBytes("UTF-8")); } catch(UnsupportedEncodingExceptionv0) { v0.printStackTrace(); v1 = ""; } return v1; } public staticString a(byte[] arg3){ String v2; try { MessageDigest v1 = MessageDigest.getInstance("MD5"); v1.update(arg3); v2 = e.a(v1.digest()); } catch(Exceptionv0) { v0.printStackTrace(); v2 = ""; } return v2; } }
继续看u_sign ((Map)v4_1).put("u_sign", f.a(v5.toString()) + v6); f.a是md5算法 sign就是v5的MD5算法之后加上salt 一共就是40位 主要分析v5就可以了。 我们可以在f.a的头部注入代码或者hook得到v5的值,这里我选择jeb动态调试 附加失败多试几次。由于word文档排版会乱,所以我截个图 carrier=&imgcode=&mobile=1615457734@qq.com&net=WIFI&os=android_x86-userdebug5.1.1 LMY48Z eng.denglibo.20170213.214219test-keys&os_type=Android&os_ver=22&password=xNhOyKUkrQEo1R5aMKmtf2hpJ0lADikLcCU1dk8xdIiaATCjuf+F7Omr5F8laSl3iLG71jxbANir aRkxwjlDRdhgiSAK0OZjPhudjT6EB++7fC0C/E1zw3EQb11wATmzBTW08Vl41AhLLQFSII405xbo rUUwB0T9tIxikVsDmJQ= &pro=&protocol=1&push_id=3a466e763b20cf13a5567e962a0d60ae&sid=&src=lx_android&token=4a74dc97f321360008dc126afea827e4&u_salt=deb986f0&u_time=1499842527&vcode=&d1ebg72e85b817353de4e28efdc180d1
需要注意的是,加密的密码是76个字符换个行然后提交数据内,需要替换,否则会提示签名错误(具体各位自己测试吧。这时候,我们就把sign的加密搞出来了)这些里面,除了password,其他可以不用管,固定即可,总之,和提交的数据一样即可 我们继续查找password,开始说了,很可能是RSA加密,搜一下RSA/试试 分别进去看看吧! 下断返回上一层 public static byte[] a(byte[] arg10,String arg11)throws Exception{ byte[] v0_2; int v8= 245; int v0= 0; Class v2= bh.class; __monitor_enter(v2); try { PrivateKey v1= KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(cn.b(arg11))); Cipher v3= Cipher.getInstance("RSA/ECB/PKCS1Padding"); v3.init(1, ((Key)v1)); int v4= arg10.length; ByteArrayOutputStream v5 = new ByteArrayOutputStream(); int v1_1; for(v1_1= 0; v4- v0 > 0;v1_1 = v9) { v0_2= v4 - v0> v8 ? v3.doFinal(arg10, v0, 245) :v3.doFinal(arg10, v0, v4 - v0); v5.write(v0_2, 0, v0_2.length); v0= v1_1 + 1; intv9 = v0; v0*= 245; } v0_2 = v5.toByteArray(); v5.close(); } catch(Throwablev0_1) { goto label_41; } __monitor_exit(v2); return v0_2; label_41: __monitor_exit(v2); throw v0_1; } ConnectionServiceManager 翻译得知 连接服务经理 也许这个是个私钥,能解密(猜测而已),那继续看 RSA/None/PKCS1Padding 这个吧 this.b = cn.a(bh.a(cj.f(arg4).getBytes("UTF-8"),"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDCEYwdO3V2ANrhApjqyk7X8FH5AEaWly58kP9IDAhMqwtIbmcJrUK9oO9Afh3KZnOlDtjiowy733YqpLRO7WBvdbW/c4Dz/d3dy/m+6HMqxaak+GQQRHw/VPdKciaZ3eIZp4MWOyIQwiFSQvPTAo/Na8hV4SgBZHB3lGFw0yu+BmG+h32eIE6p4Y8EDCn+G+yzekX+taMrWTQIysledrygZSGPv1ukbdFDnH/xZEI0dCr9pZT+AZQl3o9a2aMyuRrHM0oupXKKiYl69Y8fKh1Tyd752rF6LrR5uOb9aOfXt18hb+3YL5P9rQ+ZRYbyHYFaxzBPA2jLq0KUQ+Dmg7YhAgMBAAECggEAL9pj0lF3BUHwtssNKdf42QZJMD0BKuDcdZrLV9ifs0f54EJY5enzKw8j76MpdV8N5QVkNX4/BZR0bs9uJogh31oHFs5EXeWbb7V8P7bRrxpNnSAijGBWwscQsyqymf48YlcL28949ujnjoEz3jQjgWOyYnrCgpVhphrQbCGmB5TcZnTFvHfozt/0tzuMj5na5lRnkD0kYXgr0x/SRZcPoCybSpc3t/B/9MAAboGaV/QQkTotr7VOuJfaPRjvg8rzyPzavo3evxsjXj7vDXbN4w0cbk/Uqn2JtvPQ8HoysmF2HdYvILZibvJmWH1hA58b4sn5s6AqFRjMOL7rHdD+gQKBgQD+IzoofmZK5tTxgO9sWsG71IUeshQP9fe159jKCehk1RfuIqqbRP0UcxJiw4eNjHs4zU0HeRL3iF5XfUs0FQanO/pp6YL1xgVdfQlDdTdk6KFHJ0sUJapnJn1S2k7IKfRKE1+rkofSXMYUTsgHF1fDp+gxy4yUMY+h9O+JlKVKOwKBgQDDfaDIblaSm+B0lyG//wFPynAeGd0Q8wcMZbQQ/LWMJZhMZ7fyUZ+A6eL/jB53a2tgnaw2rXBpMe1qu8uSpym2plU0fkgLAnVugS5+KRhOkUHyorcbpVZbs5azf7GlTydR5dI1PHF3Bncemoa6IsEvumHWgQbVyTTz/O9mlFafUwKBgQCvDebms8KUf5JY1F6XfaCLWGVl8nZdVCmQFKbA7Lg2lI5KS3jHQWsupeEZRORffU/3nXsc1apZ9YY+r6CYvI77rRXd1KqPzxos/o7d96TzjkZhc9CEjTlmmh2jb5rqx/Ns/xFcZq/GGH+cx3ODZvHeZQ9NFY+9GLJ+dfB2DX0ZtwKBgQC+9/lZ8telbpqMqpqwqRaJ8LMn5JIdHZu0E6IcuhFLr+ogMW3zTKMpVtGGXEXi2M/TWRPDchiO2tQX4Q5T2/KW19QCbJ5KCwPWiGF3owN4tNOciDGh0xkSidRc0xAh8bnyejSoBry8zlcNUVztdkgMLOGonvCjZWPSOTNQnPYluwKBgCV+WVftpTk3l+OfAJTaXEPNYdh7+WQjzxZKjUaDzx80Ts7hRo2U+EQT7FBjQQNqmmDnWtujo5p1YmJC0FT3n1CVa7g901pb3b0RcOziYWAoJi0/+kLyeo6XBhuLeZ7h90S70GGh1o0V/j/9N1jb5DCL4xKkvdYePPTSTku0BM+n")); } catch(Throwablev0) { ce.a(v0, "ConnectionServiceManager","ConnectionServiceManager"); } } RSA/None/PKCS1Padding 下断返回上一层 public static byte[] a(byte[] arg7, Stringarg8) throws Exception { PublicKey v4 = KeyFactory.getInstance(r.a).generatePublic(newX509EncodedKeySpec(Base64.decode(arg8, 0))); Cipher v0 = Cipher.getInstance("RSA/None/PKCS1Padding","BC"); v0.init(1, ((Key)v4)); return v0.doFinal(arg7); } 上一层得到,这个,明显是计算密码了,看下e.h等于什么 v2就是输入的密码 e.h双击进去得到密钥 public StringgetPassword() { String v2= this.a.getText().toString(); try { v2= new String(Base64.encode(r.a(v2.getBytes(), e.h), 0)); } catch(Exceptionv1) { v1.printStackTrace(); d.d("错误"); } catch(NullPointerExceptionv1_1) { v1_1.printStackTrace(); d.d("私/公钥数据为空"); } catch(InvalidKeySpecExceptionv1_2) { v1_2.printStackTrace(); d.d("私/公钥非法"); } catch(NoSuchAlgorithmExceptionv1_3) { v1_3.printStackTrace(); d.d("无此算法"); } return v2; } static { e.a = u.a("bjtime"); e.b = e.a + "videos"; e.c = "template"; e.d = "common"; e.e = "shield.html"; e.f = "template"; e.g = "index.lua"; e.h ="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDaMWeSroknflaOH2aKc3otA0oe\nl/dI4jPXAGNrC3Ecyp8BmDJLg5zOWU0AVNUuU+xjq95AUKOs5n2cNVordqU53qRg\nLC9pxcvLf893k3Q58emdfmgHfgsCczKn7uvg2hwDsFXttbDXNc6j6QIQatQUFNXL\nHHSrlQMnpVB/sj0dKQIDAQAB\n"; e.i ="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyxsDoVdlw5wCTvqg+RP6dwrKN\nhS0JR7ZgwE89o9IFy2+FIYQws/+681ABL/Vbb5+omhBRt3sIuHRoB5fvGGNFjaKQ\nKa3YvS+ENrdt5h5c52TBTuoTlY0FtY/CQ1Jm0bsC5c1Sjww93+DrPPPPQpZ5pXeV\neVhusY6UyoUQnMksSQIDAQAB"; } 综上所述 1、 密码是 RSA/None/PKCS1Padding 的加密方式实际可以认为是 RSA/ PKCS1Padding的加密 2、 密钥是 "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDaMWeSroknflaOH2aKc3otA0oe\nl/dI4jPXAGNrC3Ecyp8BmDJLg5zOWU0AVNUuU+xjq95AUKOs5n2cNVordqU53qRg\nLC9pxcvLf893k3Q58emdfmgHfgsCczKn7uvg2hwDsFXttbDXNc6j6QIQatQUFNXL\nHHSrlQMnpVB/sj0dKQIDAQAB\n"; 3、 Sign是MD5(参数)+salt 4、 Salt是MD5(随机时间戳)的左8位 5、 写代码看看效果(写代码注意的是密码每隔76位换行一次)
附带word文档
|