Free WIFI encryption signature cracking to get others' wireless plaintext passwords (you can also change the traffic of the carrier with a gold coin)

Source: Internet
Author: User

Free WIFI encryption signature cracking to get others' wireless plaintext passwords (you can also change the traffic of the carrier with a gold coin)

Free wifi is a product similar to the wifi universal key. It also exists in the form of plug-ins in other products such as mobile guard and mobile assistant.

Detailed description:

First round: encryption and decryption

Code for feature locating

Based on the features of method = Wifi. password in an http request, the request encapsulation code is located in the qE class.

Next, perform hook analysis on the function.

The encryption method is DESede (DESede is a symmetric encryption algorithm improved by DES symmetric encryption algorithm. Uses a 168-bit key to encrypt data three times.

Next, you need to locate the DESede key to crack the entire process of http request encryption and decryption and implement it using code.

Now we start to trace the generation algorithm of the key. the following code shows that the key in init is obtained by the public static String a (String arg2) method. the Code shows that the key is actually generated by com. qihoo. freewifi. utils. securityUtils completed

Here, getkey is an native METHOD (we can see that the protection work of the app is mainly on the protection key)

public static native String getKey(String paramString1, String paramString2);

Next, we will analyze so.

? strings |grep -i getkeyJava_com_qihoo_freewifi_utils_SecurityUtils_getKey

Then, fix the parameter type in IDA F5 as follows:

In a hash process, this function can be referenced directly without any such information.

GetKey (String paramString1, String paramString2) is the second parameter that leads key generation factor 2 (factor 1 is a fixed method String)

SecurityUtils # arg3 introduction of B (Context arg1, String arg2, String arg3)

Look back at vA # a code

public static String a(String arg2) {        return SecurityUtils.b(Application.a(), arg2, vB.a(Application.a()));    }

VB # a get something similar to UUID pass in SecurityUtils # getKey to get the key. (here we can deny that the string ratio of the preceding inferred UUID class is related to the server. otherwise, the server will not be able to obtain key decryption. The client will pass factor 2 of the generated key to the server, which contains the m2 parameter in the http request. in this way, the server has two factors for generating the key to generate the key for decryption)

Device Association id generation function vB # a analysis. The main logic is as follows: splice imei, deviceid, and android_id to perform md5

v0 = vB.b(arg6);    String v1 = Settings$System.getString(arg6.getContentResolver(), "android_id");    String v2 = vB.a();    vB.a = vP.a("" + v0 + v1 + v2);

V0 is generated by vB # B (Context arg2). In one case, vB is returned. B = arg2.getSystemService ("phone "). getDeviceId (). If it is null, "_DEFAULT_IMEI" is returned"

public static String b(Context arg2) {        try {            vB.b = wa.a(arg2, "APP_STORE_IMEI0", "");            if(!TextUtils.isEmpty(vB.b)) {                String v0_1 = vB.b;                return v0_1;            }            vB.b = arg2.getSystemService("phone").getDeviceId();            if(TextUtils.isEmpty(vB.b)) {                return "_DEFAULT_IMEI";            }            wa.b(arg2, "APP_STORE_IMEI0", vB.b);            return vB.b;        }        catch(Exception v0) {        }        return "_DEFAULT_IMEI";    }

V1 is generated by Settings $ System. getString (arg6.getContentResolver (), "android_id ")

V2 is generated by vB # a (). After the connection, input vP # a (String arg1) to calculate the md5 value. The generated key factor 2 is obtained.

public static String a() {        String v0_3;        String v1 = "";        try {            Class v0_1 = Class.forName("android.os.SystemProperties");            Object v0_2 = v0_1.getMethod("get", String.class).invoke(v0_1, "ro.serialno");        }        catch(Exception v0) {            v0_3 = v1;        }        return v0_3;    }

The encryption method of the response packet is the same as that of the request packet. The response packet is directly decrypted.

The pwd parameter is the Wi-Fi password. It is null if no password is found.

Sort out the entire process:

1. Splice imie/android_id/deviceid and then perform md5 to get m2

2. Pass m2 and method into the native function getkey to obtain the symmetric key.

3. Use the key to encrypt and decrypt the data in the corresponding packet.

Since then, data encryption and decryption have been basically completed. The second step is to sign the data.


Second round: Signature cracking

The sign parameter is composed

v0_1.add(new BasicNameValuePair("sign", tM.b(v0_1, v1)));

Function generation. v0_1 is the list set of parameters in the url, while v1 is a 32-bit string generated by the tM. B () method.

Tm # B is associated with tM. c (); and vA. B (v1, "User. getConfig ");

Private static String B () {String v0 = ""; String v1 = tM. c (); if (! TextUtils. isEmpty (CharSequence) v1) {try {v0 = vA. B (v1, "User. getConfig "); // DESede decryption function} catch (Exception v1_1) {v1_1.printStackTrace () ;}} return v0 ;}

The tM. c () method mainly calls wa. a (Application. a (). getBaseContext (), "qlink_secret_key ","")

private static String c() {        String v0;        if(!TextUtils.isEmpty(tM.a)) {            v0 = tM.a;        }        else {            tM.a = wa.a(Application.a().getBaseContext(), "qlink_secret_key", "");            v0 = tM.a;        }        return v0;    }

Wa # a (Context arg1, String arg2, String arg3) method calls sV # B (Context arg2, String arg3, String arg4)

public static String a(Context arg1, String arg2, String arg3) {        return sV.b(arg1, arg2, arg3);    }

Hook sV # B (Context arg2, String arg3, String arg4) input and output are as follows:

09-15 11:02:27. 749 1293-1293 /? I/QihooWifi: in sV # B (C | s) = last_update_date | para2 = 09-15 11:02:27. 749 1293-1293 /? I/QihooWifi: out sV # B (C | s) = 2015091509-15 11:56:55. 622 2114-3083 /? I/QihooWifi: in sV # B (C | s) = qlink_secret_key | para2 = 09-15 11:56:55. 632 2114-3083 /? I/QihooWifi: out sV # B (C | s) = bytes ==/// DESede the encrypted 32-Bit String 09-15 11:57:00. 032 1244-1244 /? I/QihooWifi: in sV # B (C | s) = APP_STORE_IMEI | para2 = 09-15 11:57:00. 032 1244-1244 /? I/QihooWifi: out sV # B (C | s) = 78856d3517649f2de7ceb7dc3e4a0e9 // m2

The qlink_secret_key String is passed into sV # B (Context arg7, String arg8)

private static Cursor b(Context arg7, String arg8) {        Cursor v0_2;        Cursor v6 = null;        if(arg7 != null && !TextUtils.isEmpty(((CharSequence)arg8))) {            ContentResolver v0 = arg7.getContentResolver();            try {                v0_2 = v0.query(SharedPrefProvider.a, null, "key=\'" + arg8 + "\'", null, null);            }            catch(Throwable v0_1) {                v0_2 = v6;            }            if(v0_2 == null) {                try {                    String v0_3 = "select value from sharedpref where key=?";                    if(sV.b == null || !sV.b.isOpen()) {                        sV.b = new SPDBHelper(arg7).getReadableDatabase();                    }                    v0_2 = sV.b.rawQuery(v0_3, new String[]{arg8});                }                catch(Throwable v0_1) {                    v0_2 = v6;                }            }            else {            }        }        else {            v0_2 = v6;        }        return v0_2;    }

From the code, we can see that the qlink_secret_key value is retrieved from the table sharepref.

How is this value generated and stored in sqlite?

The tM # c () method is used. When the member variable tM. a is null, tM # a (String paramString) is used to assign values.

public static void a(String paramString){a = paramString;wa.b(Application.a().getBaseContext(), "qlink_secret_key", paramString);}

Trace to wa # B (Context paramContext, String paramString1, String paramString2)

public static void b(Context paramContext, String paramString1, String paramString2){sV.a(paramContext, paramString1, paramString2);}

Continue tracking sV # a (Context paramContext, String paramString1, String paramString2)

public static void a(Context paramContext, String paramString1, String paramString2){if (paramContext == null) {}for (;;){  return;  paramContext = paramContext.getContentResolver();  ContentValues localContentValues = new ContentValues();  localContentValues.put("key", paramString1);  localContentValues.put("value", paramString2);  try  {    if (paramContext.update(SharedPrefProvider.a, localContentValues, "key=?", new String[] { paramString1 }) == 0)    {      paramContext.insert(SharedPrefProvider.a, localContentValues);      return;    }  }  catch (Throwable paramContext) {}}}

You can confirm that the key value qlink_secret_key is written through the tM # c () method. Now you have to use xref to check the call of the method. (guess 1: This value is returned by the server, and the returned package is encrypted by DESede, which is also encrypted here. guess 2: The update_key mentioned earlier may be the key to be updated here)

There are two references, one of which is rn # a (rp arg3), where arg3.c gets the url key value under data in json)

Public void a (rp arg3 ){

If (arg3! = Null & arg3.c! = Null ){

Try {

String v0_1 = arg3.c. optString ("url ");

If (TextUtils. isEmpty (CharSequence) v0_1 ))){

Goto label_9;


TM. a (v0_1 );


Catch (Exception v0 ){




Rh. a (this. a, arg3 );


The second application is tk # a (tz arg7), where tk. a = method described as User. getConfig (continue to prove that the returned value is requested through the url here)

Public static void a (tz arg7) {vO. a ("ApiHelper", "getUserConfig"); tL v0 = tK. a (tk. a, "User. getConfig ", null); // request api vO. a ("ApiHelper", "getUserConfig end:" + v0.h); JSONObject v1 = tk. a (v0); // retrieve the data field from the returned value as qlink_secret_key if (v1 = null) {tk. a (v0, arg7, new Object [0]);} else {String v1_1 = v1.optString ("url"); if (! TextUtils. isEmpty (CharSequence) v1_1) {tM. a (v1_1);} tk. a (v0, arg7, new Object [0]);}

Next, clear the app data and capture the packet to capture the request.

After the application is reset, the first request obtains this parameter. The problem arises. Knowing that this is used to calculate the sign, how does the sign of the first request package come from? (Guess: it is related to the other two native functions)

Check the tM # a (String arg9, String arg10, List arg11, String arg12, boolean arg13) method. If the method is User. getConfig, v1 is null to calculate the sign ....

v1 = "User.getConfig".equals(arg10) ? "" : tM.b();        v0_1.add(new BasicNameValuePair("sign", tM.b(v0_1, v1)));

Look back at the tM # a (Context paramContext, String paramString1, String paramString2) method. If para2 is null, The SecurityUtils. B (paramContext, paramString1) function is called.

Private static String a (Context paramContext, String paramString1, String paramString2)


If (TextUtils. isEmpty (paramString1 )){

Return "";


For (;;)


Synchronized (rz.)


If (TextUtils. isEmpty (paramString2 ))


ParamContext = SecurityUtils. B (paramContext, paramString1 );

ParamContext = paramContext. toLowerCase ();

Return paramContext;



ParamContext = SecurityUtils. a (paramContext, paramString1, paramString2 );



TM # B (Context paramContext, String paramString)

public static String b(Context paramContext, String paramString){if ((paramContext == null) || (paramString == null) || (paramString.equals(""))){  if (paramContext == null) {    return "contextisnull";  }  if ((paramString == null) || (paramString.length() == 0)) {    return "signstrisnull";  }}if (!a){  a(paramContext);  if (!a) {    return paramString + "&loadsoerror=true";  }}try{  paramContext = initnew(paramContext.getApplicationContext(), paramString, "", false);  return paramContext;}catch (Throwable paramContext) {}return "";}

It's time to check the so again.

Here we have clearly made a package judgment. If we call it directly like getkey, it will certainly return none. So we have to analyze the isVaild function to try to bypass this restriction.

At this time, there are two choices: 1. path so, change BEQ to BNE in the judgment. 2. Pass the last Boolean variable true ..

Select the second simple method.

09-15 18:29:20.060  11255-11255/org.wooyun.qihoowifi I/Hi﹕ MySign = 5c1c5d32e47638e464bd2ee7f8beea1b

Comparing the previous hook results proves that the guess is correct.

09-15 15:09:14.610    6110-6110/? I/QihooWifi﹕ in tM#a(c|s|s) = channel=100001&devtype=android&inviter_qid=0&m2=78856d3517649f2de7ceb7dc3e4a0e94&manufacturer=LGE&method=User.getConfig&model=Nexus%205&nance=1442300954619&nettype=WIFI&os=4.4.4&qid=0&v=237 | para3 =09-15 15:09:14.610    6110-6110/? I/QihooWifi﹕ out tM#a(c|s|s) = 5c1c5d32e47638e464bd2ee7f8beea1b

Nance is generated as follows:

v3[18] = "nance";v3[19] = String.valueOf(System.currentTimeMillis());

So far, the Code cracking is basically complete.

1. First, splice the parameters in the url into strings in order.

2. If the method is User. getConfig, pass the concatenation string to the native function initnew to calculate the sign value and obtain the encrypted salt value data: url of the sign.

3. If the method is not the same as User. getConfig, the concatenation string is passed into the native function sign, and the encrypted salt obtained in 2 is decrypted and then passed in together. (note the case sensitivity)

Proof of vulnerability:

With the above analysis, you can write your own program to brush off the gold coins. In addition, you have previously made the omnipotent key... now you don't need to install two applications. You can integrate it into an app.


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.