Reverse analysis of sig parameters of Kuaishou 7.5


Sent a few days ago the same title of the article, there was in a hurry, because is the first time in my love writing and Posting, just want to try my love’s feeling, don’t want to point to send, the “full” is the image of the article to send out, also let many readers feel puzzling, here, give you apologized, this time to give you a formal analysis of the text.

Main analysis steps

  • Jadx1.1.0 with Jeb1.0 static analysis
  • Ida pro7.0 static analysis + restore

1. Jadx part

It is the SIG parameters that we need to analyze, so we search jADX directly for relevant references

Of course, as can be seen from the figure, there are many search results for sig, so let’s change our thinking, because SIG is probably in the format of “sig” with quotation marks (other forms are not excluded), so let’s search for “SIG” again.

Yes, there are a lot less results, as you can see from the search results, two related to the View, three related to the push service, obviously not, so we just need to analyze the first and last, now let’s go into the last code to see

Due to jADX itself, some code was not compiled well, but it was still possible to see the general logic. A method was called to calculate R6 and assign r6 to R8, which is the SIG parameter. To clarify the point, we use JEB to look at it

2. Jeb part

We already know the exact code location in JADX, so go directly to the relevant code and the TAB key translates into more intuitive Java code As you can see, the SIG parameter is called by the cpu.getclock method. Let’s look at the specific method codeLibcore. so is a native method of getClock

Part 3. Ida

The name of the CPU_getClock method is Java_ class name. The name of the CPU_getClock method is Java_ class name. The name of the CPU_getClock method is Java_ class name. So it’s this method right here If we look inside the method, we can see the actual method assembly code in the curly braces for __unwind H header, F5 refresh, jni Env parameter update, etc. The logic is much clearer nowCpu_inited, cpu_cnT, cpu_united, cpu_united, cpu_cnT, cpu_united, cpu_united, cpu_united, cpu_united, cpu_united, cpu_united, cpu_united, cpu_united, cpu_united, cpu_united, cpu_united Cpu_cnt), after a few byte array related conversions to the first custom function value v12, which is obtained by the sDecryptedText method. Let’s look inside the methodAs you can see, the address corresponding to v12 is 6074. The value of V12 is a fixed value, and you can look inside the sDecryptedText method. Here we hook v12 directly using FRIda, on the code and the result

The logic of Frida Hook goes something like this:

Get the base address of so -> add the offset of 6074 we got earlier -> read the pointer to the address using frida memory and get the value of V12

J_cpu_clock_start ->j_cpu_clock_x->j_cpu_clock_end ->j_cpu_clock_end Then look atGo to j_CPU_clock_start firstFrom the yellow section into the specific code to viewThis method initializes some variables. Let’s look at the j_CPU_clock_X method, which is where most of the encryption logic is written. Let’s look at its call diagram firstIt’s pretty clear. Let’s look at the codeAfter defining some variables, you notice that the sub_1EF4 method is called and look at the code Combined with the previous code, it has a bit of an MD5 feel. After verification of the above code, it can be found that it is indeed MD5 after adding salt. The code is as follows

public class Sign { public static final String FANS_SALT = "382700b563f4"; public static String SHA512(final String strText) { return SHA(strText, "SHA-512"); } private static String SHA(final String strText, final String strType) {// Return String strResult = null; If (strText! = null && strtext.length () > 0) {try {// SHA encryption starts // Create an encryption object and pass in the encryption type MessageDigest MessageDigest = MessageDigest.getInstance(strType); MessageDigest. Update (strtext.getBytes ())); Byte byteBuffer[] = messageDigest. Digest (); byte byteBuffer[] = messageDigest. StrHexString = new StringBuffer(); Byte buffer for (int I = 0; i < byteBuffer.length; i++) { String hex = Integer.toHexString(0xff & byteBuffer[i]); if (hex.length() == 1) { strHexString.append('0'); } strHexString.append(hex); StrResult = strhexstring.tostring (); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } return strResult; } public static String genSigSignature(Map<String,String> params, String salt) {// sig's algorithm if(params == null){return null; } String sign = ""; StringBuffer sb = new StringBuffer(); SortedMap<String,String> SortedMap = new TreeMap<>(params); // 2. Set<String> keySet = sortedmap.keyset (); For (String key: keySet) {/ / sign if not participate in algorithm (key) equals (" sig ") | | key. Equals (" __NStokensig ")) {continue; } String value = sortedMap.get(key); sb.append(key + "=" + URLDecoder.decode(value,"UTF-8")); } String uriString = sb.toString(); uriString = uriString + salt; System.out.println("My String: \n" + uriString); Sign = MD5 (uriString); System.out.println("My Sign:\n" +sign); } catch (Exception e) { e.printStackTrace(); } return sign.toLowerCase(); } public final static String md5(String s) { char hexDigits[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; try { byte[] btInput = s.getBytes(); / / get the MD5 digest algorithms MessageDigest object MessageDigest mdInst = MessageDigest. GetInstance (" MD5 "); // Update the digest mdinst.update (btInput) with the specified byte; Byte [] md = mdinst.digest (); byte[] md = mdinst.digest (); // Convert the ciphertext to a hexadecimal string form int j = md.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } catch (Exception e) { e.printStackTrace(); return null; } } public static Map<String,String> getMapFromStr(String str){ String[] arr = str.split("\\&"); Map<String,String> map = new HashMap<>(); for(String item : arr){ String[] itemArr = item.split("=",2); map.put(itemArr[0],itemArr[1]); } return map; } private static char[] b(byte[] bArr) { char[] b = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; char[] cArr = b; int length = bArr.length; char[] cArr2 = new char[(length << 1)]; int i = 0; for (int i2 = 0; i2 < length; i2++) { int i3 = i + 1; cArr2[i] = cArr[(bArr[i2] & 240) >>> 4]; i = i3 + 1; cArr2[i3] = cArr[bArr[i2] & 15]; } return cArr2; }}Copy the code