精华内容
下载资源
问答
  • 1. 该app可以修改模拟手机地理位置(gps、基站、WIFI),拥有全局定位、指定应用定位、模拟扫街等功能,只能在已越狱的IOS系统上使用,安装须要用到Cydia,安装后主要分为插件与主程序两个部分,主程序负责与用户交互...

    工具环境
    ida7.0
    iphone 6
    ios 10.2

    0x00:基本情况

    1. 该app可以修改模拟手机地理位置(gps、基站、WIFI),拥有全局定位、指定应用定位、模拟扫街等功能,只能在已越狱的IOS系统上使用,安装须要用到Cydia,安装后主要分为插件与主程序两个部分,主程序负责与用户交互,插件主要实现了修改地理位置的功能。在Cydia插件安装目录可以找到它释放的xxtweak.dylib文件,拷贝出来后面做详细分析。

    2. 程序运行后如果不是vip用户是不能使用修改定位的功能,如下:

    0x01:保护机制分析

    1. 将手机设置好代理,启动app登录,用抓包工具获取数据包做分析,发送到服务器的数据与服务器返回的数据都是加密的,如下图:

    2. 通过动态调试主app来寻找突破口,主app通过sysctl来做反调试,先编写一个tweak来过掉反调试。这个代码网上很多,如下:

    static int (*orig_sysctl)(int * name, u_int namelen, void * info, size_t * infosize, void * newinfo, size_t newinfosize);
    static int my_sysctl(int * name, u_int namelen, void * info, size_t * infosize, void * newinfo, size_t newinfosize){
        int ret = orig_sysctl(name,namelen,info,infosize,newinfo,newinfosize);
        if(namelen == 4 && name[0] == 1 && name[1] == 14 && name[2] == 1){
            struct kinfo_proc *info_ptr = (struct kinfo_proc *)info;
            if(info_ptr && (info_ptr->kp_proc.p_flag & P_TRACED) != 0){
                NSLog(@"[AntiAntiDebug] - sysctl query trace status.");
                info_ptr->kp_proc.p_flag ^= P_TRACED;
                if((info_ptr->kp_proc.p_flag & P_TRACED) == 0){
                    NSLog(@"[AntiAntiDebug] trace status reomve success!");
                }
            }
        }
        return ret;
    }

    3. 将app放入ida中反编,用登录抓包获取到的信息中关键字来查找字符串定位到组合参数的地方,组合后格式如下:

    {
        "data":"{
    "clientid":"4092f7a187943eff48d1c4b827b5f53c",
    "username":"xxxxx",//用户名
    "password":"xxxxxx"//密码
    }",
        "header":{
            "uptime":"1540607913",
            "sysapi":"10.2",
            "model":"iPhone 6",
            "systype":"1",
            "vercode":"15.13",
            "uuid":"DA148217C2491937"
        }
    }

    然后将组合后的代码用AES加密发送到服务器代码如下:

    void __cdecl -[TXYUserInfoRequset loginUserwith:andPassword:andClientid:](TXYUserInfoRequset *self, SEL a2, id a3, id a4, id a5)
    {
      id v5; // x21
      id v6; // x20
      TXYUserInfoRequset *v7; // x22
      __int64 username; // x19
      __int64 v9; // x1
      __int64 passwrod; // x20
      __int64 v11; // x1
      __int64 v12; // x21
      void *url; // x0
      void *v14; // x0
      void *v15; // x0
      void *v16; // x24
      void *v17; // x0
      __int64 clientId; // x23
      void *v19; // x0
      const __CFString *v20; // x2
      struct objc_object *v21; // x0
      struct objc_object *v22; // ST28_8
      id v23; // x0
      __int64 v24; // ST20_8
      id v25; // x0
      void *v26; // x0
      void *v27; // x28
      void *v28; // x0
      __int64 v29; // x25
      void *v30; // x0
      struct objc_object *v31; // ST18_8
      id v32; // x0
      struct objc_object *info_json; // x0
      struct objc_object *v34; // x28
      id ret; // x0
      __int64 v36; // x21
      void *v37; // x0
      void *v38; // x26
      double v39; // d0
      void *v40; // x0
      __int64 v41; // x25
      void *v42; // x0
      struct objc_object *bodydata; // x24
      __int64 v44; // x1
      NSURLSessionTask *v45; // x0
      __int64 v46; // x0
      struct objc_object *httpurl; // [xsp+38h] [xbp-138h]
      void *v48; // [xsp+40h] [xbp-130h]
      int v49; // [xsp+48h] [xbp-128h]
      int v50; // [xsp+4Ch] [xbp-124h]
      __int64 (__fastcall *v51)(__int64, __int64); // [xsp+50h] [xbp-120h]
      void *v52; // [xsp+58h] [xbp-118h]
      __int64 v53; // [xsp+60h] [xbp-110h]
      const __CFString *body; // [xsp+68h] [xbp-108h]
      const __CFString *v55; // [xsp+70h] [xbp-100h]
      const __CFString *v56; // [xsp+78h] [xbp-F8h]
      const __CFString *v57; // [xsp+80h] [xbp-F0h]
      __int64 encdata; // [xsp+88h] [xbp-E8h]
      const __CFString *v59; // [xsp+90h] [xbp-E0h]
      const __CFString *v60; // [xsp+98h] [xbp-D8h]
      __int64 v61; // [xsp+A0h] [xbp-D0h]
      const __CFString *v62; // [xsp+A8h] [xbp-C8h]
      const __CFString *v63; // [xsp+B0h] [xbp-C0h]
      __int64 v64; // [xsp+B8h] [xbp-B8h]
      __int64 v65; // [xsp+C0h] [xbp-B0h]
      const __CFString *v66; // [xsp+C8h] [xbp-A8h]
      const __CFString *v67; // [xsp+D0h] [xbp-A0h]
      const __CFString *v68; // [xsp+D8h] [xbp-98h]
      __int64 username_1; // [xsp+E0h] [xbp-90h]
      __int64 passwrod_1; // [xsp+E8h] [xbp-88h]
      __int64 clientId_1; // [xsp+F0h] [xbp-80h]
      const __CFString *v72; // [xsp+F8h] [xbp-78h]
      const __CFString *v73; // [xsp+100h] [xbp-70h]
      __int64 v74; // [xsp+108h] [xbp-68h]
      __int64 v75; // [xsp+110h] [xbp-60h]
      __int64 v76; // [xsp+118h] [xbp-58h]
    
      v5 = a5;
      v6 = a4;
      v7 = self;
      v76 = -7228227847426539268LL;
      username = objc_retain(a3, a2);
      passwrod = objc_retain(v6, v9);
      v12 = objc_retain(v5, v11);
      if ( !username )
      {
        v20 = CFSTR("缺少用户名");
    LABEL_7:
        objc_msgSend(v7, (const char *)&MEMORY[0x19AEA7047], v20);
        clientId = v12;
        goto LABEL_10;
      }
      if ( !passwrod )
      {
        v20 = CFSTR("缺少密码");
        goto LABEL_7;
      }
      url = objc_msgSend(
              &MEMORY[0x1B559E398],
              (const char *)&MEMORY[0x1966A229A],
              CFSTR("%@%@"),
              CFSTR("http://ipay.txyapp.com:7658/api/entrance"),
              CFSTR("/111"));
      httpurl = (struct objc_object *)objc_retainAutoreleasedReturnValue(url);
      v14 = objc_msgSend(&MEMORY[0x1B5593F88], (const char *)&MEMORY[0x1966A95FA]);
      v15 = (void *)objc_retainAutoreleasedReturnValue(v14);
      v16 = v15;
      v17 = objc_msgSend(v15, (const char *)&MEMORY[0x1966A8093], CFSTR("clientId"));
      clientId = objc_retainAutoreleasedReturnValue(v17);
      objc_release(v12);
      objc_release(v16);
      if ( clientId )
      {
        username_1 = username;
        v66 = CFSTR("username");
        v67 = CFSTR("password");
        passwrod_1 = passwrod;
        v68 = CFSTR("clientid");
        clientId_1 = clientId;
        v19 = objc_msgSend(&MEMORY[0x1B5593128], (const char *)&MEMORY[0x1966A2036], &username_1, &v66, 3LL);
      }
      else
      {
        v72 = CFSTR("username");
        v73 = CFSTR("password");
        v74 = username;
        v75 = passwrod;
        v19 = objc_msgSend(&MEMORY[0x1B5593128], (const char *)&MEMORY[0x1966A2036], &v74, &v72, 2LL);
      }
      v21 = (struct objc_object *)objc_retainAutoreleasedReturnValue(v19);
      v22 = v21;
      v23 = ((id (__cdecl *)(TXYUserInfoRequset *, SEL, id))objc_msgSend)(v7, "convertToJSONData:", v21);
      v24 = objc_retainAutoreleasedReturnValue(v23);
      v62 = CFSTR("data");
      v63 = CFSTR("header");
      v64 = v24;
      v25 = ((id (__cdecl *)(DeviceAbout_meta *, SEL))objc_msgSend)(
              (DeviceAbout_meta *)&OBJC_CLASS___DeviceAbout,
              "sharedDevice");
      v26 = (void *)objc_retainAutoreleasedReturnValue(v25);
      v27 = v26;
      v28 = objc_msgSend(v26, "getPublicPhone");
      v29 = objc_retainAutoreleasedReturnValue(v28);
      v65 = v29;
      v30 = objc_msgSend(&MEMORY[0x1B5593128], (const char *)&MEMORY[0x1966A2036], &v64, &v62, 2LL);
      v31 = (struct objc_object *)objc_retainAutoreleasedReturnValue(v30);
      objc_release(v29);
      objc_release(v27);
      v32 = ((id (__cdecl *)(TXYUserInfoRequset *, SEL, id))objc_msgSend)(v7, "convertToJSONData:", v31);
      info_json = (struct objc_object *)objc_retainAutoreleasedReturnValue(v32);
      v34 = info_json;
      ret = ((id (__cdecl *)(AES128_meta *, SEL, id, id))objc_msgSend)(// 加密参数
              (AES128_meta *)&OBJC_CLASS___AES128,
              "AES128Encrypt:withKey:",
              info_json,
              (id)CFSTR("Hz2Ywe8UBe@YfZ0*"));
      v36 = objc_retainAutoreleasedReturnValue(ret);
      encdata = v36;
      body = CFSTR("body");
      v55 = CFSTR("type");
      v59 = CFSTR("111");
      v56 = CFSTR("flag");
      v60 = CFSTR("4");
      v57 = CFSTR("t1");
      v37 = objc_msgSend(&MEMORY[0x1B5592FC0], (const char *)&MEMORY[0x1966B0E63]);
      v38 = (void *)objc_retainAutoreleasedReturnValue(v37);
      objc_msgSend(v38, (const char *)&MEMORY[0x19678A07E]);
      v40 = objc_msgSend(&MEMORY[0x1B559E398], (const char *)&MEMORY[0x1966A229A], CFSTR("%lld"), (signed __int64)v39);
      v41 = objc_retainAutoreleasedReturnValue(v40);
      v61 = v41;
      v42 = objc_msgSend(&MEMORY[0x1B5593128], (const char *)&MEMORY[0x1966A2036], &encdata, &body, 4LL);
      bodydata = (struct objc_object *)objc_retainAutoreleasedReturnValue(v42);
      objc_release(v41);
      objc_release(v38);
      v48 = &MEMORY[0x1B558B298];
      v49 = -1040187392;
      v50 = 0;
      v51 = __60__TXYUserInfoRequset_loginUserwith_andPassword_andClientid___block_invoke_2;// 解密返回数并存放配置文件中
      v52 = &__block_descriptor_tmp259;
      v53 = objc_retain(v7, v44);
      v45 = ((NSURLSessionTask *(__cdecl *)(TXYNetManager_meta *, SEL, unsigned __int64, id, id, id, id, id))objc_msgSend)(// 发送网络
              (TXYNetManager_meta *)&OBJC_CLASS___TXYNetManager,
              "txy_requestWithType:urlString:parameters:progress:successBlock:failureBlock:",
              1uLL,
              httpurl,
              bodydata,
              (id)&__block_literal_global240,
              (id)&v48,
              (id)&__block_literal_global262);
      v46 = objc_retainAutoreleasedReturnValue(v45);
      objc_release(v46);
      objc_release(v53);
      objc_release(bodydata);
      objc_release(v36);
      objc_release(v34);
      objc_release(v31);
      objc_release(v24);
      objc_release(v22);
      objc_release(httpurl);
    LABEL_10:
      objc_release(clientId);
      objc_release(passwrod);
      objc_release(username);
    }

    4. 解密与解析服务器返回的数据,解密后数据格式如下:

    {
        "status":0,
        "token":"de9dc56d7d7cc085f4bb1897075e4801",
        "username":"xxxx",//用户名
        "vip":0, //是否为vip用户
        "expiretime":0 //会员过期时间
    }

    解密代码如下:

    / 登录后存放authValue到文件
    __int64 __fastcall __60__TXYUserInfoRequset_loginUserwith_andPassword_andClientid___block_invoke_2(__int64 a1, __int64 a2)
    {
      __int64 v2; // x28
      __int64 v3; // x19
      void *v4; // x0
      void *v5; // x21
      __int64 v6; // x1
      void *v7; // x19
      void *v8; // x0
      __int64 v9; // x22
      void *v10; // x0
      __int64 v11; // x23
      void *v12; // x0
      __int64 v13; // ST10_8
      void *v14; // x0
      void *v15; // x23
      void *v16; // x24
      void *v17; // x0
      struct objc_object *data; // x0
      struct objc_object *v19; // x24
      id v20; // x0
      void *v21; // x0
      void *v22; // x28
      void *v23; // x0
      __int64 v24; // x0
      void *v25; // x0
      void *v26; // x0
      void *v27; // x26
      void *v28; // x0
      __int64 v29; // ST08_8
      void *v30; // x0
      void *v31; // x23
      void *v32; // x22
      void *v33; // x0
      __int64 v34; // x22
      __int64 v35; // x23
      void *v36; // x20
      void *v37; // x0
      __int64 v38; // x21
      __int64 v39; // x0
      void *v40; // x0
      __int64 v41; // x22
      void *v42; // x0
      __int64 v43; // ST00_8
      void *v44; // x0
      void *v45; // x23
      void *v46; // x0
      __int64 v47; // x28
      void *v48; // x0
      __int64 v49; // x22
      void *v50; // x0
      __int64 v51; // ST00_8
      void *v52; // x0
      void *v53; // x23
      void *v54; // x0
      __int64 v55; // x22
      void *v56; // x0
      __int64 v57; // x22
      __int64 v58; // x24
      void *v59; // x0
      void *v60; // x23
      void *v61; // x22
      void *v62; // x0
      void *v63; // x0
      void *v64; // x23
      void *v65; // x0
      __int64 v66; // x24
      void *v67; // x0
      __int64 v68; // x0
      __int64 v69; // x22
      __int64 v70; // x0
      void *v71; // x0
      void *v72; // x22
      int vip; // w24
      id v74; // x0
      void *v75; // x0
      void *v76; // x23
      void *v77; // x0
      void *v78; // x28
      void *v79; // x0
      __int64 v80; // x24
      void *v81; // x0
      __int64 v82; // x23
      void *v83; // x0
      void *v84; // x23
      id v85; // x0
      void *v86; // x0
      __int64 v87; // x24
      void *v88; // x0
      __int64 v89; // x23
      void *v90; // x0
      void *v91; // x22
      void *v92; // x0
      __int64 v93; // x21
      void *v94; // x0
      __int64 v95; // x20
      __int64 v97; // [xsp+0h] [xbp-90h]
      void *v98; // [xsp+20h] [xbp-70h]
      struct objc_object *v99; // [xsp+28h] [xbp-68h]
      __int64 v100; // [xsp+30h] [xbp-60h]
      __int64 v101; // [xsp+38h] [xbp-58h]
      __int64 v102; // [xsp+38h] [xbp-58h]
    
      v2 = a1;
      v3 = objc_retain(a2, a2);
      v4 = objc_msgSend(&MEMORY[0x1B5593F88], (const char *)&MEMORY[0x1966A95FA]);
      v5 = (void *)objc_retainAutoreleasedReturnValue(v4);
      objc_msgSend(v5, (const char *)&MEMORY[0x1966A8EE2], CFSTR("no"), CFSTR("geostates"));
      objc_release(v5);
      v7 = (void *)objc_retain(v3, v6);
      NSLog(CFSTR("response====%@"));
      v8 = objc_msgSend(v7, (const char *)&MEMORY[0x1966A4322], CFSTR("error"), v7);
      v9 = objc_retainAutoreleasedReturnValue(v8);
      v10 = objc_msgSend(v7, (const char *)&MEMORY[0x1966A4322], CFSTR("info"));
      v11 = objc_retainAutoreleasedReturnValue(v10);
      v12 = objc_msgSend(v7, (const char *)&MEMORY[0x1966A4322], CFSTR("status"));
      v13 = objc_retainAutoreleasedReturnValue(v12);
      NSLog(CFSTR("error = %@ info = %@ status = %@"));
      objc_release(v13);
      objc_release(v11);
      objc_release(v9);
      v14 = objc_msgSend(v7, (const char *)&MEMORY[0x1966A4322], CFSTR("status"), v9, v11, v13);
      v15 = (void *)objc_retainAutoreleasedReturnValue(v14);
      v16 = objc_msgSend(v15, (const char *)&MEMORY[0x1966A5F6B]);
      objc_release(v15);
      if ( !v16 )                                   // 判断网络是否返回成功
      {
        v101 = v2;
        v17 = objc_msgSend(v7, (const char *)&MEMORY[0x1966A4322], CFSTR("data"));// 获取返回值中的data数据
        data = (struct objc_object *)objc_retainAutoreleasedReturnValue(v17);
        v19 = data;
        v20 = ((id (__cdecl *)(AES128_meta *, SEL, id, id))objc_msgSend)(// 解密data数据
                (AES128_meta *)&OBJC_CLASS___AES128,
                "AES128Decrypt:withKey:",
                data,
                (id)CFSTR("Hz2Ywe8UBe@YfZ0*"));
        v21 = (void *)objc_retainAutoreleasedReturnValue(v20);
        v22 = v21;
        v23 = objc_msgSend(v21, (const char *)&MEMORY[0x196754444], 4LL);
        v24 = objc_retainAutoreleasedReturnValue(v23);
        v100 = v24;
        v25 = objc_msgSend(&MEMORY[0x1B55A0FF8], (const char *)&MEMORY[0x197C763B3], v24, 1LL, 0LL);
        v26 = (void *)objc_retainAutoreleasedReturnValue(v25);
        v27 = v26;
        v28 = objc_msgSend(v26, (const char *)&MEMORY[0x1966A4322], CFSTR("msg"));
        v29 = objc_retainAutoreleasedReturnValue(v28);
        NSLog(CFSTR("----%@ -------%@"));
        objc_release(v29);
        v30 = objc_msgSend(v27, (const char *)&MEMORY[0x1966A4322], CFSTR("status"), v27, v29);
        v31 = (void *)objc_retainAutoreleasedReturnValue(v30);
        v32 = objc_msgSend(v31, (const char *)&MEMORY[0x1966A5F6B]);
        objc_release(v31);
        if ( v32 )
        {
          v33 = objc_msgSend(v27, (const char *)&MEMORY[0x1966A4322], CFSTR("msg"));
          v34 = objc_retainAutoreleasedReturnValue(v33);
          objc_release(v34);
          v35 = v100;
          if ( !v34 )
          {
    LABEL_21:
            objc_release(v27);
            objc_release(v35);
            objc_release(v22);
            objc_release(v19);
            goto LABEL_22;
          }
          v36 = *(void **)(v101 + 32);
          v37 = objc_msgSend(v27, (const char *)&MEMORY[0x1966A4322], CFSTR("msg"));
          v38 = objc_retainAutoreleasedReturnValue(v37);
          objc_msgSend(v36, (const char *)&MEMORY[0x19AEA7047], v38);
          v39 = v38;
        }
        else
        {
          v98 = v22;
          v40 = objc_msgSend(v27, (const char *)&MEMORY[0x1966A4322], CFSTR("token"));
          v41 = objc_retainAutoreleasedReturnValue(v40);
          objc_release(v41);
          if ( v41 )
          {
            v42 = objc_msgSend(v27, (const char *)&MEMORY[0x1966A4322], CFSTR("token"));
            v43 = objc_retainAutoreleasedReturnValue(v42);
            NSLog(CFSTR("%@"));
            objc_release(v43);
            v44 = objc_msgSend(&MEMORY[0x1B5593F88], (const char *)&MEMORY[0x1966A95FA], v43);
            v45 = (void *)objc_retainAutoreleasedReturnValue(v44);
            v46 = objc_msgSend(v27, (const char *)&MEMORY[0x1966A4322], CFSTR("token"));
            v47 = objc_retainAutoreleasedReturnValue(v46);
            objc_msgSend(v45, (const char *)&MEMORY[0x1966A8EE2], v47, CFSTR("token"));
            objc_release(v47);
            objc_release(v45);
          }
          v99 = v19;
          v48 = objc_msgSend(v27, (const char *)&MEMORY[0x1966A4322], CFSTR("username"));
          v49 = objc_retainAutoreleasedReturnValue(v48);
          objc_release(v49);
          if ( v49 )
          {
            v50 = objc_msgSend(v27, (const char *)&MEMORY[0x1966A4322], CFSTR("username"));
            v51 = objc_retainAutoreleasedReturnValue(v50);
            NSLog(CFSTR("%@"));
            objc_release(v51);
            v52 = objc_msgSend(&MEMORY[0x1B5593F88], (const char *)&MEMORY[0x1966A95FA], v51);
            v53 = (void *)objc_retainAutoreleasedReturnValue(v52);
            v54 = objc_msgSend(v27, (const char *)&MEMORY[0x1966A4322], CFSTR("username"));
            v55 = objc_retainAutoreleasedReturnValue(v54);
            objc_msgSend(v53, (const char *)&MEMORY[0x1966A8EE2], v55, CFSTR("userName"));
            objc_release(v55);
            objc_release(v53);
          }
          v56 = objc_msgSend(v27, (const char *)&MEMORY[0x1966A4322], CFSTR("expiretime"));
          v57 = objc_retainAutoreleasedReturnValue(v56);
          objc_release(v57);
          v58 = v101;
          if ( v57 )
          {
            v59 = objc_msgSend(v27, (const char *)&MEMORY[0x1966A4322], CFSTR("expiretime"));
            v60 = (void *)objc_retainAutoreleasedReturnValue(v59);
            v61 = objc_msgSend(v60, (const char *)&MEMORY[0x1966ABDF3]);
            objc_release(v60);
            v62 = objc_msgSend(&MEMORY[0x1B5593F88], (const char *)&MEMORY[0x1966A95FA]);
            v63 = (void *)objc_retainAutoreleasedReturnValue(v62);
            v64 = v63;
            if ( (_DWORD)v61 )
            {
              v65 = objc_msgSend(*(void **)(v101 + 32), "timeFormatted:", v61);
              v66 = objc_retainAutoreleasedReturnValue(v65);
              objc_msgSend(v64, (const char *)&MEMORY[0x1966A8EE2], v66, CFSTR("expiretime"));
              objc_release(v66);
              objc_release(v64);
              v58 = v101;
              v67 = objc_msgSend(*(void **)(v101 + 32), "timeFormatted:", v61);
              v68 = objc_retainAutoreleasedReturnValue(v67);
              v69 = v68;
              v97 = v68;
              NSLog(CFSTR("expiretime = %@ "));
              v70 = v69;
            }
            else
            {
              objc_msgSend(v63, (const char *)&MEMORY[0x1966A8EE2], CFSTR("0"), CFSTR("expiretime"));
              v70 = (__int64)v64;
            }
            objc_release(v70);
          }
          v102 = v58;
          v71 = objc_msgSend(v27, (const char *)&MEMORY[0x1966A4322], CFSTR("vip"), v97);
          v72 = (void *)objc_retainAutoreleasedReturnValue(v71);
          vip = (unsigned __int64)objc_msgSend(v72, (const char *)&MEMORY[0x1966ABDF3]);
          objc_release(v72);
          v74 = ((id (__cdecl *)(TXYTools_meta *, SEL))objc_msgSend)((TXYTools_meta *)&OBJC_CLASS___TXYTools, "sharedTools");
          v75 = (void *)objc_retainAutoreleasedReturnValue(v74);
          v76 = v75;
          v77 = objc_msgSend(v75, "loadSetDictForPath:", CFSTR("/var/mobile/Library/Preferences/com.txy.TxyNew.plist"));
          v78 = (void *)objc_retainAutoreleasedReturnValue(v77);
          objc_release(v76);
          if ( vip )                                // 如果vip不为0
          {
            v79 = objc_msgSend(&MEMORY[0x1B559E4B0], (const char *)&MEMORY[0x1966A7D6A], 1LL);
            v80 = objc_retainAutoreleasedReturnValue(v79);
            objc_msgSend(v78, (const char *)&MEMORY[0x1966A8EE2], v80, CFSTR("authValue"));
            objc_release(v80);
            v81 = objc_msgSend(&MEMORY[0x1B559E4B0], (const char *)&MEMORY[0x1966A7D6A], 1LL);
            v82 = objc_retainAutoreleasedReturnValue(v81);
            objc_msgSend(v78, (const char *)&MEMORY[0x1966A8EE2], v82, CFSTR("Toggle"));
            objc_release(v82);
            v83 = objc_msgSend(&MEMORY[0x1B5593F88], (const char *)&MEMORY[0x1966A95FA]);
            v84 = (void *)objc_retainAutoreleasedReturnValue(v83);
            objc_msgSend(v84, (const char *)&MEMORY[0x1966A8EE2], CFSTR("1"), CFSTR("isVIP"));
          }
          else                                      // 如果vip为0
          {
            v86 = objc_msgSend(&MEMORY[0x1B559E4B0], (const char *)&MEMORY[0x1966A7D6A], 0LL);
            v87 = objc_retainAutoreleasedReturnValue(v86);
            objc_msgSend(v78, (const char *)&MEMORY[0x1966A8EE2], v87, CFSTR("authValue"));
            objc_release(v87);
            v88 = objc_msgSend(&MEMORY[0x1B559E4B0], (const char *)&MEMORY[0x1966A7D6A], 0LL);
            v89 = objc_retainAutoreleasedReturnValue(v88);
            objc_msgSend(v78, (const char *)&MEMORY[0x1966A8EE2], v89, CFSTR("Toggle"));
            objc_release(v89);
            v90 = objc_msgSend(&MEMORY[0x1B5593F88], (const char *)&MEMORY[0x1966A95FA]);
            v84 = (void *)objc_retainAutoreleasedReturnValue(v90);
            objc_msgSend(v84, (const char *)&MEMORY[0x1966A8EE2], CFSTR("0"), CFSTR("isVIP"));
          }
          objc_release(v84);
          v85 = ((id (__cdecl *)(TXYTools_meta *, SEL))objc_msgSend)((TXYTools_meta *)&OBJC_CLASS___TXYTools, "sharedTools");
          v91 = (void *)objc_retainAutoreleasedReturnValue(v85);
          objc_msgSend(v91, "writeDict:toPath:", v78, CFSTR("/var/mobile/Library/Preferences/com.txy.TxyNew.plist"));// 写入配置文件
          objc_release(v91);
          objc_release(v78);
          v35 = v100;
          v22 = v98;
          v19 = v99;
          v92 = objc_msgSend(v27, (const char *)&MEMORY[0x1966A4322], CFSTR("msg"));
          v93 = objc_retainAutoreleasedReturnValue(v92);
          objc_release(v93);
          if ( v93 )
            objc_msgSend(*(void **)(v102 + 32), (const char *)&MEMORY[0x19AEA7047], CFSTR("登录成功"));
          v94 = objc_msgSend(*(void **)(v102 + 32), "requestResult");
          v95 = objc_retainAutoreleasedReturnValue(v94);
          (*(void (**)(void))(v95 + 16))();
          v39 = v95;
        }
        objc_release(v39);
        goto LABEL_21;
      }
    LABEL_22:
      objc_release(v7);
      return objc_release(v7);
    }

    1. 整个登录过程就分析完成了,主要用到了AES加密算法。

    0x02:破解思路

    1. 通过上面登录过程的分析,根据上面数据格式可以直接改vip与expiretime的值就能成功破解成会员用户,如下图 :

    2. 将服务器返回的值存入.plist文件后,程序会调用-[TXYTools isCanOpen]方法读取文件判断是否为vip,代码如下:

      bool __cdecl -[TXYTools isCanOpen](TXYTools *self, SEL a2)
    {
      void *v2; // x0
      void *v3; // x0
      void *v4; // x20
      void *v5; // x0
      void *v6; // x23
      void *isVIP; // x19
      id v8; // x0
      void *v9; // x0
      void *v10; // x23
      void *v11; // x0
      void *v12; // x20
      void *v13; // x0
      void *v14; // x23
      void *authValue; // x21
    
      v2 = objc_msgSend(&OBJC_CLASS___NSUserDefaults, (const char *)&unk_188A615FA);
      v3 = (void *)objc_retainAutoreleasedReturnValue(v2);
      v4 = v3;
      v5 = objc_msgSend(v3, (const char *)&unk_188A60093, CFSTR("isVIP"));
      v6 = (void *)objc_retainAutoreleasedReturnValue(v5);
      isVIP = objc_msgSend(v6, (const char *)&unk_188A5DF6B);
      objc_release(v6);
      objc_release(v4);
      v8 = ((id (__cdecl *)(TXYTools_meta *, SEL))objc_msgSend)((TXYTools_meta *)&OBJC_CLASS___TXYTools, "sharedTools");
      v9 = (void *)objc_retainAutoreleasedReturnValue(v8);
      v10 = v9;
      v11 = objc_msgSend(v9, "loadSetDictForPath:", CFSTR("/var/mobile/Library/Preferences/com.txy.TxyNew.plist"));
      v12 = (void *)objc_retainAutoreleasedReturnValue(v11);
      objc_release(v10);
      v13 = objc_msgSend(v12, (const char *)&unk_188A60093, CFSTR("authValue"));
      v14 = (void *)objc_retainAutoreleasedReturnValue(v13);
      authValue = objc_msgSend(v14, (const char *)&unk_188A5DF6B);
      objc_release(v14);
      NSLog(CFSTR("user = %ld"));
      NSLog(CFSTR("isVIP = %ld"));
      objc_release(v12);
      return (signed __int64)isVIP > 0 && (signed __int64)authValue > 0;
    }

    3. 第一是可以通过修改服务器返回值做破解,第二是通过hook方法isCanOpen来做破解。代码如下 :

    %hook TXYTools
    - (BOOL)isCanOpen
    {
    return YES;
    }
    %end

    破解成功后可以正常使用功能,如下图所示 :

    0x03:实现原理

    1. 主app获取到要修改的经纬度与地名后DES加密存放在.plist配置文件中,将选择的地图上的经纬度与地址名加密后写入.plist配置文件,代码如下:

    //将选择的app的Build地图上的经纬度与地址名加密后写入.plist配置文件
    __int64 __fastcall -[TXYConfig setLocationWithBundleId:andType:andGPS:withAddress:withIsOn:](void *a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6, double a7, double a8)
    {
      __int64 v8; // x26
      __int64 v9; // x19
      double v10; // d8
      double v11; // d9
      __int64 v12; // x25
      void *v13; // x20
      __int64 v14; // x1
      const __CFString *v15; // x19
      void *v16; // x0
      void *v17; // x0
      void *v18; // x21
      void *v19; // x0
      __int64 v20; // x1
      void *v21; // x22
      void *v22; // x0
      void *v23; // x0
      __int64 v24; // x0
      __int64 v25; // x28
      void *v26; // x0
      __int64 v27; // x0
      __int64 v28; // x24
      void *v29; // x0
      __int64 v30; // x0
      __int64 v31; // x20
      void *v32; // x0
      void *v33; // x26
      void *v34; // x0
      __int64 v35; // x20
      void *v36; // x0
      __int64 v37; // x20
      __int64 v39; // [xsp+10h] [xbp-C0h]
      __int64 v40; // [xsp+18h] [xbp-B8h]
      void *v41; // [xsp+20h] [xbp-B0h]
      const __CFString *v42; // [xsp+28h] [xbp-A8h]
      const __CFString *v43; // [xsp+30h] [xbp-A0h]
      const __CFString *v44; // [xsp+38h] [xbp-98h]
      const __CFString *v45; // [xsp+40h] [xbp-90h]
      __int64 v46; // [xsp+48h] [xbp-88h]
      __int64 v47; // [xsp+50h] [xbp-80h]
      const __CFString *v48; // [xsp+58h] [xbp-78h]
      __int64 v49; // [xsp+60h] [xbp-70h]
      __int64 v50; // [xsp+68h] [xbp-68h]
    
      v8 = a6;
      v9 = a5;
      v10 = a8;
      v11 = a7;
      v12 = a4;
      v13 = a1;
      v40 = a3;
      v41 = a1;
      v50 = -7228227847426539268LL;
      v39 = objc_retain(a3, a2);
      v15 = (const __CFString *)objc_retain(v9, v14);
      v16 = objc_msgSend(v13, "loadSetDict");
      v17 = (void *)objc_retainAutoreleasedReturnValue(v16);
      v18 = v17;
      v19 = objc_msgSend(v17, (const char *)&unk_1966A8093, CFSTR("AppLocation"));
      v21 = (void *)objc_retainAutoreleasedReturnValue(v19);
      if ( !v21 )
      {
        v22 = objc_msgSend(&OBJC_CLASS___NSMutableDictionary, (const char *)&unk_1966A833B);
        v21 = (void *)objc_retainAutoreleasedReturnValue(v22);
      }
      if ( !(_DWORD)v12 )
      {
        objc_retain(CFSTR("跟随系统真实位置"), v20);
        objc_release(v15);
        v15 = CFSTR("跟随系统真实位置");
      }
      v42 = CFSTR("Latitude");
      v23 = objc_msgSend(&OBJC_CLASS___NSNumber, (const char *)&unk_1966A25C1, v11);
      v24 = objc_retainAutoreleasedReturnValue(v23);
      v25 = v24;
      v46 = v24;
      v43 = CFSTR("Longitude");
      v26 = objc_msgSend(&OBJC_CLASS___NSNumber, (const char *)&unk_1966A25C1, v10);
      v27 = objc_retainAutoreleasedReturnValue(v26);
      v28 = v27;
      v47 = v27;
      v44 = CFSTR("address");
      v48 = v15;
      v45 = CFSTR("isOn");
      v29 = objc_msgSend(&OBJC_CLASS___NSNumber, &aNumberwithbool, v8);
      v30 = objc_retainAutoreleasedReturnValue(v29);
      v31 = v30;
      v49 = v30;
      v32 = objc_msgSend(&OBJC_CLASS___NSDictionary, &aDictionarywith, &v46, &v42, 4LL);
      v33 = objc_msgSend(v32, "mutableCopy");
      objc_release(v31);
      objc_release(v28);
      objc_release(v25);
      v34 = objc_msgSend(&OBJC_CLASS___NSNumber, &aNumberwithunsi, v12);
      v35 = objc_retainAutoreleasedReturnValue(v34);
      objc_msgSend(v33, &aSetobjectForke, v35, CFSTR("FakeType"));
      objc_release(v35);
      objc_msgSend(v21, &aSetobjectForke, v33, v40);
      objc_release(v39);
      v36 = objc_msgSend(&OBJC_CLASS___NSNumber, &aNumberwithbool, 1LL);
      v37 = objc_retainAutoreleasedReturnValue(v36);
      objc_msgSend(v18, &aSetobjectForke, v37, CFSTR("Toggle"));
      objc_release(v37);
      objc_msgSend(v18, &aSetobjectForke, v21, CFSTR("AppLocation"));
      NSLog(CFSTR("%@"));
      objc_msgSend(v41, "writeSetConfigWithDict:", v18, v18);// 加密并写入配置文件
      objc_release(v33);
      objc_release(v21);
      objc_release(v18);
      return objc_release(v15);
    }
    
    //加密并写入配置文件
    bool __cdecl -[TXYConfig writeSetConfigWithDict:](TXYConfig *self, SEL a2, id a3)
    {
      void *v3; // x0
      __int64 v4; // x19
      void *v5; // x0
      void *v6; // x0
      void *v7; // x20
      struct objc_object *v8; // x0
      void *v9; // x21
      char v10; // w22
      __int64 v12; // [xsp+8h] [xbp-28h]
    
      v3 = objc_msgSend(&OBJC_CLASS___NSJSONSerialization, &aDatawithjsonob, a3, 1LL, 0LL);
      v4 = objc_retainAutoreleasedReturnValue(v3);
      v5 = objc_msgSend(&OBJC_CLASS___NSString, &aAlloc_0);
      v6 = objc_msgSend(v5, &aInitwithdataEn, v4, 4LL);
      v7 = v6;
      v8 = +[DES encryptString:](&OBJC_CLASS___DES, "encryptString:", v6);// des加密
      v9 = (void *)objc_retainAutoreleasedReturnValue(v8);
      v12 = 0LL;
      v10 = (unsigned __int64)objc_msgSend(
                                v9,
                                &aWritetofileAto,
                                CFSTR("/var/mobile/Library/Preferences/com.txy.TxyNew.plist"),
                                0LL,
                                4LL,
                                &v12);
      objc_release(v9);
      objc_release(v7);
      objc_release(v4);
      return v10;
    }
    id __cdecl +[DES encryptString:](DES_meta *self, SEL a2, id a3)
    {
      return (id)objc_msgSend(self, "TripleDES:encryptOrDecrypt:key:", a3, 0LL, CFSTR("3b38e11ffd65698aedeb5ffc"));
    }

    2. Tweak插件主要是hook了几个函数,当目标程序调用获取地理位置信息函数时就解密主app加密存放好的值做为获取值,代码如下:

    Hook获取位置函数

    char *__fastcall _logosLocalCtor_3415ee8c(int a1, char **a2, char **a3)
    {
      void *v3; // ST30_8
      __int64 v4; // x0
      __int64 v5; // x8
      const char *v7; // [xsp+0h] [xbp-4C0h]
      const char *v8; // [xsp+8h] [xbp-4B8h]
      signed __int64 v9; // [xsp+10h] [xbp-4B0h]
      const char *v10; // [xsp+18h] [xbp-4A8h]
      void *v11; // [xsp+20h] [xbp-4A0h]
      char v12; // [xsp+58h] [xbp-468h]
      __int64 v13; // [xsp+70h] [xbp-450h]
      int v14; // [xsp+7Ch] [xbp-444h]
      __int64 CLLocationManager; // [xsp+80h] [xbp-440h]
      __int64 v16; // [xsp+88h] [xbp-438h]
      char **v17; // [xsp+90h] [xbp-430h]
      char **v18; // [xsp+98h] [xbp-428h]
      int v19; // [xsp+A4h] [xbp-41Ch]
      char v20; // [xsp+A8h] [xbp-418h]
      char v21; // [xsp+A9h] [xbp-417h]
      char v22; // [xsp+AAh] [xbp-416h]
      char v23; // [xsp+ABh] [xbp-415h]
      char v24; // [xsp+ACh] [xbp-414h]
      char v25; // [xsp+ADh] [xbp-413h]
      char v26; // [xsp+AEh] [xbp-412h]
      char v27; // [xsp+AFh] [xbp-411h]
    
      v19 = a1;
      v18 = a2;
      v17 = a3;
      v16 = objc_getClass("AMapLocationManager");
      if ( v16 )
      {
        MSHookMessageEx(
          v16,
          "detectRiskOfFakeLocation",
          _logos_method$_ungrouped$AMapLocationManager$detectRiskOfFakeLocation,// 检测虚拟定位
          &_logos_orig$_ungrouped$AMapLocationManager$detectRiskOfFakeLocation);
      }
      else
      {
        v11 = objc_msgSend(&OBJC_CLASS___NSString, "stringWithFormat:", CFSTR("logos: nil class %s"), "AMapLocationManager");
        v10 = "ERROR";
        v9 = 276LL;
        v8 = "Tweak.xm";
        v7 = "txytweak";
        CFLog(3LL, CFSTR("\x1B[1;31m[%s] \x1B[m\x1B[0;31m%s:%d\x1B[m \x1B[0;30;41m%s:\x1B[m %@"));
      }
      CLLocationManager = objc_getClass("CLLocationManager");
      if ( CLLocationManager )
      {
        MSHookMessageEx(
          CLLocationManager,
          "location",
          _logos_method$_ungrouped$CLLocationManager$location,
          &_logos_orig$_ungrouped$CLLocationManager$location);
      }
      else
      {
        v11 = objc_msgSend(
                &OBJC_CLASS___NSString,
                "stringWithFormat:",
                CFSTR("logos: nil class %s"),
                "CLLocationManager",
                v8,
                v9,
                v10,
                v11);
        v10 = "ERROR";
        v9 = 276LL;
        v8 = "Tweak.xm";
        v7 = "txytweak";
        CFLog(3LL, CFSTR("\x1B[1;31m[%s] \x1B[m\x1B[0;31m%s:%d\x1B[m \x1B[0;30;41m%s:\x1B[m %@"));
      }
      if ( CLLocationManager )
      {
        MSHookMessageEx(
          CLLocationManager,
          "startUpdatingLocation",
          _logos_method$_ungrouped$CLLocationManager$startUpdatingLocation,
          &_logos_orig$_ungrouped$CLLocationManager$startUpdatingLocation);
      }
      else
      {
        v11 = objc_msgSend(
                &OBJC_CLASS___NSString,
                "stringWithFormat:",
                CFSTR("logos: nil class %s"),
                "CLLocationManager",
                v8,
                v9,
                v10,
                v11);
        v10 = "ERROR";
        v9 = 276LL;
        v8 = "Tweak.xm";
        v7 = "txytweak";
        CFLog(3LL, CFSTR("\x1B[1;31m[%s] \x1B[m\x1B[0;31m%s:%d\x1B[m \x1B[0;30;41m%s:\x1B[m %@"));
      }
      v20 = 118;
      v21 = 64;
      v22 = 58;
      v23 = 35;
      v24 = 58;
      v25 = 35;
      v26 = 58;
      v14 = 7;
      v27 = 0;
      class_addMethod();
      v13 = CFNotificationCenterGetDarwinNotifyCenter();
      CFNotificationCenterAddObserver();
      objc_msgSend(&OBJC_CLASS___NSBundle, "mainBundle", v7, v8, v9, v10, v11);
      v3 = (void *)objc_retainAutoreleasedReturnValue();
      objc_msgSend(v3, "bundleIdentifier");
      v4 = objc_retainAutoreleasedReturnValue();
      v5 = curBundle;
      curBundle = v4;
      objc_release(v5);
      objc_release(v3);
      NSLog(CFSTR("txy hook success1111111"));
      return &v12;
    }

    读取 .plist配置文中的数据并解密。

    void __cdecl -[TXYLocation replacedLocationManager:didUpdateToLocation:fromLocation:](TXYLocation *self, SEL a2, id a3, id a4, id a5)
    {
      void *v5; // ST2F8_8
      __int64 v6; // x0
      void *v7; // ST1F0_8
      void *v8; // x0
      void *v9; // ST1E0_8
      void *v10; // ST1D0_8
      char v11; // ST1CC_1
      void *v12; // ST2A8_8
      void *v13; // ST1B8_8
      double v14; // d0
      double v15; // ST2C0_8
      void *v16; // ST1A8_8
      double v17; // d0
      double v18; // ST2C8_8
      __int64 v19; // ST2A0_8
      void *v20; // ST1A0_8
      double v21; // d0
      double v22; // ST198_8
      double v23; // d0
      double v24; // ST190_8
      double v25; // d0
      double v26; // ST188_8
      double v27; // ST180_8
      double v28; // d0
      double v29; // d0
      double v30; // d1
      void *v31; // ST150_8
      void *v32; // ST148_8
      int v33; // ST144_4
      void *v34; // ST258_8
      void *v35; // ST138_8
      double v36; // d0
      void *v37; // ST130_8
      double v38; // d0
      void *v39; // ST128_8
      int v40; // ST124_4
      void *v41; // ST238_8
      void *v42; // ST118_8
      double v43; // d0
      void *v44; // ST110_8
      double v45; // d0
      void *v46; // STF8_8
      double v47; // d0
      double v48; // STE8_8
      double v49; // d0
      double v50; // STD8_8
      double v51; // d0
      double v52; // STC8_8
      double v53; // d0
      double v54; // STB8_8
      double v55; // d0
      double v56; // STA8_8
      __int64 v57; // x0
      __int64 v58; // ST98_8
      void *v59; // x0
      __int64 v60; // x9
      void *v61; // ST88_8
      double v62; // d0
      double v63; // ST80_8
      double v64; // d0
      double v65; // ST78_8
      double v66; // d0
      double v67; // ST70_8
      double v68; // d0
      double v69; // ST68_8
      double v70; // d0
      double v71; // ST60_8
      __int64 v72; // ST58_8
      void *v73; // ST50_8
      double v74; // d0
      double v75; // ST48_8
      double v76; // d0
      double v77; // ST40_8
      double v78; // d0
      double v79; // ST38_8
      double v80; // d0
      double v81; // ST30_8
      double v82; // d0
      double v83; // ST28_8
      __int64 v84; // ST20_8
      double v85; // d0
      double v86; // d1
      double v87; // d2
      double v88; // d3
      char v89; // [xsp+15Ch] [xbp-1E4h]
      char v90; // [xsp+1DCh] [xbp-164h]
      char v91; // [xsp+1ECh] [xbp-154h]
      void *v92; // [xsp+228h] [xbp-118h]
      char v93; // [xsp+267h] [xbp-D9h]
      void *v94; // [xsp+268h] [xbp-D8h]
      unsigned int v95; // [xsp+274h] [xbp-CCh]
      double v96; // [xsp+290h] [xbp-B0h]
      void *v97; // [xsp+298h] [xbp-A8h]
      void *v98; // [xsp+2B0h] [xbp-90h]
      void *v99; // [xsp+2B8h] [xbp-88h]
      double v100; // [xsp+2C0h] [xbp-80h]
      double v101; // [xsp+2C8h] [xbp-78h]
      void *v102; // [xsp+2D0h] [xbp-70h]
      void *jsondata; // [xsp+2E8h] [xbp-58h]
      struct objc_object *v104; // [xsp+300h] [xbp-40h]
      TXYLocation *v105; // [xsp+328h] [xbp-18h]
    
      v105 = self;
      objc_storeStrong();
      objc_storeStrong();
      objc_storeStrong();
      NSLog(CFSTR("第一个方法相应"));
      objc_msgSend(
        &OBJC_CLASS___NSString,
        "stringWithContentsOfFile:encoding:error:",
        CFSTR("/var/mobile/Library/Preferences/com.txy.TxyNew.plist"),
        4LL,
        0LL);
      v104 = (struct objc_object *)objc_retainAutoreleasedReturnValue();
      if ( v104 )
      {
        ((void (__cdecl *)(DES_meta *, SEL, id))objc_msgSend)((DES_meta *)&OBJC_CLASS___DES, "decryptString:", v104);// 解密
        v5 = (void *)objc_retainAutoreleasedReturnValue();
        objc_msgSend(v5, "dataUsingEncoding:", 4LL);
        v6 = objc_retainAutoreleasedReturnValue();
        objc_msgSend(&OBJC_CLASS___NSJSONSerialization, "JSONObjectWithData:options:error:", v6, 1LL, 0LL);
        jsondata = (void *)objc_retainAutoreleasedReturnValue();
        if ( !jsondata )
        {
          objc_msgSend(&OBJC_CLASS___NSMutableDictionary, "dictionary");
          jsondata = (void *)objc_retainAutoreleasedReturnValue();
          objc_release(0LL);
        }
        objc_msgSend(jsondata, "valueForKey:", CFSTR("Toggle"));
        v7 = (void *)objc_retainAutoreleasedReturnValue();
        v91 = (unsigned __int64)objc_msgSend(v7, "boolValue");
        objc_release(v7);
        if ( objc_retain(CFSTR("/var/mobile/Library/Preferences/com.txy.FakeStatus.plist")) )
        {
          v8 = objc_msgSend(&OBJC_CLASS___NSMutableDictionary, "alloc");
          v102 = objc_msgSend(
                   v8,
                   "initWithContentsOfFile:",
                   CFSTR("/var/mobile/Library/Preferences/com.txy.FakeStatus.plist"));
          objc_release(0LL);
          if ( !v102 )
          {
            objc_msgSend(&OBJC_CLASS___NSMutableDictionary, "dictionary");
            v102 = (void *)objc_retainAutoreleasedReturnValue();
            objc_release(0LL);
          }
        }
        else
        {
          objc_msgSend(&OBJC_CLASS___NSMutableDictionary, "dictionary");
          v102 = (void *)objc_retainAutoreleasedReturnValue();
          objc_release(0LL);
        }
        objc_msgSend(v102, "objectForKey:", CFSTR("fakeStatus"));
        v9 = (void *)objc_retainAutoreleasedReturnValue();
        v90 = (unsigned __int64)objc_msgSend(v9, "isEqualToString:", CFSTR("no"));
        objc_release(v9);
        if ( !(v91 & 1) )
        {
          _objc_msgSend(v105, "replacedLocationManager:didUpdateToLocation:fromLocation:", 0LL, 0LL, 0LL);
    LABEL_36:
          v95 = 0;
    LABEL_37:
          objc_storeStrong();
          objc_storeStrong();
          objc_storeStrong();
          objc_storeStrong();
          objc_storeStrong();
          if ( v95 )
            goto LABEL_41;
          goto LABEL_40;
        }
        objc_msgSend(jsondata, "objectForKey:", CFSTR("AppLocation"));
        v99 = (void *)objc_retainAutoreleasedReturnValue();
        objc_msgSend(jsondata, "objectForKey:", CFSTR("AppScan"));
        v98 = (void *)objc_retainAutoreleasedReturnValue();
        objc_msgSend(v98, "allKeys");
        v10 = (void *)objc_retainAutoreleasedReturnValue();
        v11 = (unsigned __int64)objc_msgSend(v10, "containsObject:", curBundle);
        objc_release(v10);
        if ( v11 & 1 )
        {
          objc_msgSend(v98, "objectForKey:", curBundle);
          v12 = (void *)objc_retainAutoreleasedReturnValue();
          objc_msgSend(v12, "objectForKey:", CFSTR("Latitude"));
          v13 = (void *)objc_retainAutoreleasedReturnValue();
          objc_msgSend(v13, "doubleValue");
          v15 = v14;
          objc_release(v13);
          objc_msgSend(v12, "objectForKey:", CFSTR("Longitude"));
          v16 = (void *)objc_retainAutoreleasedReturnValue();
          objc_msgSend(v16, "doubleValue");
          v18 = v17;
          objc_release(v16);
          objc_msgSend(&OBJC_CLASS___NSDate, "date");
          v19 = objc_retainAutoreleasedReturnValue();
          v20 = objc_msgSend(&OBJC_CLASS___CLLocation, "alloc");
          objc_msgSend(0LL, "altitude");
          v22 = v21;
          objc_msgSend(0LL, "horizontalAccuracy");
          v24 = v23;
          objc_msgSend(0LL, "verticalAccuracy");
          v26 = v25;
          v27 = *(double *)&lastCourse;
          objc_msgSend(0LL, "speed");
          v97 = objc_msgSend(
                  v20,
                  "initWithCoordinate:altitude:horizontalAccuracy:verticalAccuracy:course:speed:timestamp:",
                  v19,
                  v15,
                  v18,
                  v22,
                  v24,
                  v26,
                  v27,
                  v28);
          objc_msgSend((void *)lastFakeFromLocation, "coordinate");
          v96 = headingToLocation(v15, v18, v29, v30);
          objc_storeStrong();
          if ( v96 != 0.0 )
            lastCourse = *(_QWORD *)&v96;
          _objc_msgSend(v105, "replacedLocationManager:didUpdateToLocation:fromLocation:", 0LL, v97, v97);
          v95 = 1;
          objc_storeStrong();
          objc_storeStrong();
          objc_storeStrong();
          goto LABEL_33;
        }
        v93 = 0;
        v89 = 0;
        if ( v99 )
        {
          objc_msgSend(v99, "allKeys");
          v94 = (void *)objc_retainAutoreleasedReturnValue();
          v93 = 1;
          v89 = (unsigned __int64)objc_msgSend(v94, "containsObject:", curBundle);
        }
        if ( v93 & 1 )
          objc_release(v94);
        if ( v89 & 1 )
        {
          objc_msgSend(v99, "objectForKeyedSubscript:", curBundle);
          v31 = (void *)objc_retainAutoreleasedReturnValue();
          objc_msgSend(v31, "objectForKeyedSubscript:", CFSTR("isOn"));
          v32 = (void *)objc_retainAutoreleasedReturnValue();
          v33 = (unsigned __int64)objc_msgSend(v32, "intValue");
          objc_release(v32);
          objc_release(v31);
          if ( v33 != 1 )
          {
            _objc_msgSend(v105, "replacedLocationManager:didUpdateToLocation:fromLocation:", 0LL, 0LL, 0LL);
            v95 = 1;
            goto LABEL_33;
          }
          objc_msgSend(v99, "objectForKey:", curBundle);
          v34 = (void *)objc_retainAutoreleasedReturnValue();
          objc_msgSend(v34, "objectForKey:", CFSTR("Latitude"));
          v35 = (void *)objc_retainAutoreleasedReturnValue();
          objc_msgSend(v35, "doubleValue");
          v100 = v36;
          objc_release(v35);
          objc_msgSend(v34, "objectForKey:", CFSTR("Longitude"));
          v37 = (void *)objc_retainAutoreleasedReturnValue();
          objc_msgSend(v37, "doubleValue");
          v101 = v38;
          objc_release(v37);
          objc_msgSend(v34, "objectForKey:", CFSTR("FakeType"));
          v39 = (void *)objc_retainAutoreleasedReturnValue();
          v40 = (unsigned __int64)objc_msgSend(v39, "intValue");
          objc_release(v39);
          if ( v40 )
          {
            v95 = 0;
          }
          else
          {
            _objc_msgSend(v105, "replacedLocationManager:didUpdateToLocation:fromLocation:", 0LL, 0LL, 0LL);
            v95 = 1;
          }
          objc_storeStrong();
          if ( v95 )
          {
    LABEL_33:
            objc_storeStrong();
            objc_storeStrong();
            if ( v95 )
              goto LABEL_37;
            goto LABEL_36;
          }
        }
        else
        {
          if ( (v90 & 1) != 0 )
          {
            _objc_msgSend(v105, "replacedLocationManager:didUpdateToLocation:fromLocation:", 0LL, 0LL, 0LL);
            v95 = 1;
            goto LABEL_33;
          }
          objc_msgSend(jsondata, "objectForKey:", CFSTR("FakeLocation"));
          v41 = (void *)objc_retainAutoreleasedReturnValue();
          objc_msgSend(v41, "objectForKey:", CFSTR("FakeLatitude"));
          v42 = (void *)objc_retainAutoreleasedReturnValue();
          objc_msgSend(v42, "doubleValue");
          v100 = v43;
          objc_release(v42);
          objc_msgSend(v41, "objectForKey:", CFSTR("FakeLongitude"));
          v44 = (void *)objc_retainAutoreleasedReturnValue();
          objc_msgSend(v44, "doubleValue");
          v101 = v45;
          objc_release(v44);
          objc_storeStrong();
        }
        if ( lastFakeFromLocation )
        {
          v73 = objc_msgSend(&OBJC_CLASS___CLLocation, "alloc");
          objc_msgSend(0LL, "altitude");
          v75 = v74;
          objc_msgSend(0LL, "horizontalAccuracy");
          v77 = v76;
          objc_msgSend(0LL, "verticalAccuracy");
          v79 = v78;
          objc_msgSend(0LL, "course");
          v81 = v80;
          objc_msgSend(0LL, "speed");
          v83 = v82;
          objc_msgSend(0LL, "timestamp");
          v84 = objc_retainAutoreleasedReturnValue();
          v92 = objc_msgSend(
                  v73,
                  "initWithCoordinate:altitude:horizontalAccuracy:verticalAccuracy:course:speed:timestamp:",
                  v84,
                  v100,
                  v101,
                  v75,
                  v77,
                  v79,
                  v81,
                  v83);
          objc_release(0LL);
          objc_release(v84);
        }
        else
        {
          v46 = objc_msgSend(&OBJC_CLASS___CLLocation, "alloc");
          objc_msgSend(0LL, "altitude");
          v48 = v47;
          objc_msgSend(0LL, "horizontalAccuracy");
          v50 = v49;
          objc_msgSend(0LL, "verticalAccuracy");
          v52 = v51;
          objc_msgSend(0LL, "course");
          v54 = v53;
          objc_msgSend(0LL, "speed");
          v56 = v55;
          objc_msgSend(0LL, "timestamp");
          v57 = objc_retainAutoreleasedReturnValue();
          v58 = v57;
          v59 = objc_msgSend(
                  v46,
                  "initWithCoordinate:altitude:horizontalAccuracy:verticalAccuracy:course:speed:timestamp:",
                  v57,
                  v100,
                  v101,
                  v48,
                  v50,
                  v52,
                  v54,
                  v56);
          v60 = lastFakeFromLocation;
          lastFakeFromLocation = (__int64)v59;
          objc_release(v60);
          objc_release(v58);
          v61 = objc_msgSend(&OBJC_CLASS___CLLocation, "alloc");
          objc_msgSend(0LL, "altitude");
          v63 = v62;
          objc_msgSend(0LL, "horizontalAccuracy");
          v65 = v64;
          objc_msgSend(0LL, "verticalAccuracy");
          v67 = v66;
          objc_msgSend(0LL, "course");
          v69 = v68;
          objc_msgSend(0LL, "speed");
          v71 = v70;
          objc_msgSend(0LL, "timestamp");
          v72 = objc_retainAutoreleasedReturnValue();
          v92 = objc_msgSend(
                  v61,
                  "initWithCoordinate:altitude:horizontalAccuracy:verticalAccuracy:course:speed:timestamp:",
                  v72,
                  v100,
                  v101,
                  v63,
                  v65,
                  v67,
                  v69,
                  v71);
          objc_release(0LL);
          objc_release(v72);
        }
        _objc_msgSend(v105, "replacedLocationManager:didUpdateToLocation:fromLocation:", 0LL, v92, v92);
        objc_storeStrong();
        objc_storeStrong();
        v95 = 0;
        goto LABEL_33;
      }
      _objc_msgSend(v105, "replacedLocationManager:didUpdateToLocation:fromLocation:", 0LL, 0LL, 0LL);
    LABEL_40:
      v95 = 0;
    LABEL_41:
      objc_storeStrong();
      objc_storeStrong();
      objc_storeStrong();
      objc_storeStrong();
      if ( v95 > 1 )
        headingToLocation(v85, v86, v87, v88);
    }

    0x04:总结

    1. 虽然与服务器交互的数据是加密的,但软件保护逻辑比较简单,实现修改地理位置的功能主要是通过hook获取位置的函数。

    欢迎关注公众号

    转载于:https://www.cnblogs.com/2014asm/p/9866901.html

    展开全文
  • IOS虚拟定位 修改手机位置(Xcode)

    千次阅读 2020-12-19 16:20:32
    打开MAC App store 下载Xcode 先填写自己的 苹果账号登陆然后 点击Create a new Xcode project 选择 iOS 里面的 Augmended Reality App 下一步下一步 然后 在loog 里面创建一波 快捷键command+n 创建Gpx fille...

    一、

    iOS(mac、数据线一根、iphone、xcode):

    打开MAC App store 下载Xcode
    先填写自己的 苹果账号登陆然后
    在这里插入图片描述

    点击Create a new Xcode project
    

    在这里插入图片描述
    选择 iOS 里面的 Augmended Reality App
    在这里插入图片描述
    下一步下一步
    然后
    在这里插入图片描述
    在loog 里面创建一波
    快捷键command+n
    在这里插入图片描述
    创建Gpx fille 文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <gpx version="1.1"
         creator="GMapToGPX 6.4j - http://www.elsewhere.org/GMapToGPX/"
         xmlns="http://www.topografix.com/GPX/1/1"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
          <wpt lat="31.405282727518287" lon="110.47644402008271">
             <name>shenlongjia</name>
             <cmt>神农架</cmt>
             <desc>神农架</desc>
          </wpt>
    </gpx>
    

    吧上面这串复制进去 原来的全部删掉在这里插入图片描述
    这个是获取自己想要地址的网站
    https://tool.lu/coordinate/
    WGS84坐标系 110.47644402008271,31.405282727518287
    使用WGS84坐标
    lat="31.405282727518287"
    lon="110.47644402008271"

    看清楚两个坐标
    然后打开手机设置里面找到开发者Xcode
    在这里插入图片描述
    吧开发者里面的权限打开
    点这里选择手机 往上滑最上面第一个就是自己的手机
    在这里插入图片描述

    在之后点这里选择开发的文件导入手机
    在这里插入图片描述
    然后
    在这里插入图片描述
    选择这个 点击上方的
    Product–Run
    运行打开手机之后位置就修改完成
    会生成一个文件在手机上

    作者:Eamon
    时间:2020/12/19

    展开全文
  • 首先:获取用户手机是否打开了 “允许模拟位置” 选项? 其实很简单,这些设置项,基本都是写在数据库里,所以只要看看setting的源码(或者查看logcat可能也可以得到些有用的信息),就能知道该配置是写了数据库的...

    在应用开发中,如果有签到打卡之类的功能,我们肯定需要在项目中禁止用户开启虚拟定位,导致在***米之外的距离模拟定位然后进行了打卡操作!

    (一)
    首先:获取用户手机是否打开了 “允许模拟位置” 选项?

    其实很简单,这些设置项,基本都是写在数据库里,所以只要看看setting的源码(或者查看logcat可能也可以得到些有用的信息),就能知道该配置是写了数据库的哪个字段。

    boolean isOpen = Settings.Secure.getInt(context.getContentResolver(),Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0; //很明显,Settings.Secure.ALLOW_MOCK_LOCATION 就是存放允许模拟位置的数据库字段了 //当isOpen为ture时,则是用户打开了允许模拟位置的选项,否则则没有开启!
    

    (二)
    其次:虚拟定位可以通过一些第三方应用,然后把我们自己的应用克隆一个。在启动的时候需要在第三方的应用里面来启动,这样的话在私有文件里面生成的包名势必会和直接启动自己的应用有区别,知道了这些,我们将通过以下三个方法来一一检测;

    首先介绍一些那些使用应用分身双开和虚拟定位的应用和自己的应用在私有目录下生成的包名有什么区别:

    我们知道App的私有目录是/data/data/包名/或/data/user/用户号/包名,通过Context.getFilesDir()方法可以拿到私有目录下的files目录。在多开环境下,获取到目录会变为/data/data/多开App的包名/xxxxxxxx或/data/user/用户号/多开App的包名/xxxxxxxx。

    举个例子,在我手机上,正常使用App上面的代码获取到的路径为/data/user/0/top.darkness463.virtualcheck/files。在多开分身的多开环境下,路径为/data/user/0/dkmodel.zom.rxo/virtual/data/user/0/top.darkness463.virtualcheck/files。

    当然,多开软件是可以hook处理让你拿到正常的目录,但截至写这篇文章为止,市面上大部分多开App没有绕过这项检测,仅有360家的分身大师可以绕过。

    1. ps检测
      我们先通过执行对uid进行过滤,得到类似下面的结果
    // 正常情况下
    u0_a148 8162 423 1806036 56368 SyS_epoll+ 0 S top.darkness463.virtualcheck
     
    // 多开环境下
    u0_a155 19752 422 4437612 62752 SyS_epoll+ 0 S top.darkness463.virtualcheck
    u0_a155 19758 422 564234 54356 SyS_epoll+ 0 S com.lbe.parallel
    u0_a155 19747 422 734562 24542 SyS_epoll+ 0 S com.lbe.parallel:mdserver
    

    可以看到在多开环境下,会获取到自己的包名和多开App的包名这2个包名,通过这些包名去/data/data/下找会找到2个目录,而正常情况下只能在/data/data/下找到自己的App的目录。看下具体代码实现;

    public static boolean isRunInVirtual() {
     
        String filter = getUidStrFormat();
     
        String result = exec("ps");
     
        if (result == null || result.isEmpty()) {
     
            return false;
     
        }
     
        String[] lines = result.split("\n");
     
        if (lines == null || lines.length <= 0) {
     
            return false;
     
        }
     
        int exitDirCount = 0;
     
        for (int i = 0; i < lines.length; i++) {
     
            if (lines[i].contains(filter)) {
     
                int pkgStartIndex = lines[i].lastIndexOf(" ");
     
                String processName = lines[i].substring(pkgStartIndex <= 0
     
                        ? 0 : pkgStartIndex + 1, lines[i].length());
     
                File dataFile = new File(String.format("/data/data/%s",
     
                        processName, Locale.CHINA));
     
                if (dataFile.exists()) {
     
                    exitDirCount++;
                }
            }
        }
     
        return exitDirCount > 1;
     
    }
    

    这里的应用列表检测不是指简单的遍历应用列表判断是不是安装了多开App,我们并不阻止用户安装多开App并多开其他App,我们只是不希望用户多开我们自己的App,因此不能检测到用户安装了多开App就把他干掉。
    2.应用列表检测
    多开App都会对context.getPackageName()进行处理,让这个方法返回原始App的包名,因此在被多开的App看来,多开App的包名和原始的那个App的包名一样,因此在多开环境下遍历应用列表时会发现包名等于原始App的包名的应用会有两个。

    private boolean checkPkg(Context context) {
        try {
            if (context == null) {
                return false;
            }
            int count = 0;
            String packageName = context.getPackageName();
            PackageManager pm = context.getPackageManager();
            List<PackageInfo> pkgs = pm.getInstalledPackages(0);
            for (PackageInfo info : pkgs) {
                if (packageName.equals(info.packageName)) {
                    count++;
                }
            }
            return count > 1;
        } catch (Exception ignore) {}
        return false; }
    

    3.maps检测
    读取/proc/self/maps,多开App会加载一些自己的so到内存空间,举个例子,360的分身大师加载了其目录下的某个so,/data/app/com.qihoo.magic-gdEsg8KRAuJy0MuY18BlqQ==/lib/arm/libbreakpad-jni-1.5.so,通过对各种多开App的包名的匹配,如果maps中有多开App的包名的东西,那么当前就是运行在多开环境下。目前没有发现多开App绕过该项检测,但缺点是需要收集所有多开App的包名,一旦多开App改个包名就失效了。

    Set<String> virtualPkgs;  // 多开第三方App包名列表
    private boolean check() {
        BufferedReader bufr = null;
        try {
            bufr = new BufferedReader(new FileReader("/proc/self/maps"));
            String line;
            while ((line = bufr.readLine()) != null) {
                for (String pkg : virtualPkgs) {
                    if (line.contains(pkg)) {
                        return true;
                    }
                }
            }
        } catch (Exception ignore) {
            
        } finally {
            if (bufr != null) {
                try {
                    bufr.close();
                } catch (IOException e) {
                    
                }
            }
        }
        return false;
    }
    

    以上三种检测方法有的会被第三方虚拟定位软件或者多开分身软件躲避掉,有的则不会,所以使用的时候建议三种方法全部用上。

    展开全文
  • 首先:获取用户手机是否打开了 “允许模拟位置” 选项? 其实很简单,这些设置项,基本都是写在数据库里,所以只要看看setting的源码(或者查看logcat可能也可以得到些有用的信息),就能知道该配置是写了数据库的...

    参考:https://blog.csdn.net/mawei7510/article/details/80250416

    在应用开发中,如果有签到打卡之类的功能,我们肯定需要在项目中禁止用户开启虚拟定位,导致在***米之外的距离模拟定位然后进行了打卡操作!

    (一)

    首先:获取用户手机是否打开了  “允许模拟位置”  选项?

    其实很简单,这些设置项,基本都是写在数据库里,所以只要看看setting的源码(或者查看logcat可能也可以得到些有用的信息),就能知道该配置是写了数据库的哪个字段。

    boolean isOpen = Settings.Secure.getInt(context.getContentResolver(),Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0;
    //很明显,Settings.Secure.ALLOW_MOCK_LOCATION 就是存放允许模拟位置的数据库字段了
    //当isOpen为ture时,则是用户打开了允许模拟位置的选项,否则则没有开启!

    (二)

    其次:虚拟定位可以通过一些第三方应用,然后把我们自己的应用克隆一个。在启动的时候需要在第三方的应用里面来启动,这样的话在私有文件里面生成的包名势必会和直接启动自己的应用有区别,知道了这些,我们将通过以下三个方法来一一检测;

     

    首先介绍一些那些使用应用分身双开和虚拟定位的应用和自己的应用在私有目录下生成的包名有什么区别:

    我们知道App的私有目录是/data/data/包名//data/user/用户号/包名,通过Context.getFilesDir()方法可以拿到私有目录下的files目录。在多开环境下,获取到目录会变为/data/data/多开App的包名/xxxxxxxx/data/user/用户号/多开App的包名/xxxxxxxx

    举个例子,在我手机上,正常使用App上面的代码获取到的路径为/data/user/0/top.darkness463.virtualcheck/files。在多开分身的多开环境下,路径为/data/user/0/dkmodel.zom.rxo/virtual/data/user/0/top.darkness463.virtualcheck/files

    当然,多开软件是可以hook处理让你拿到正常的目录,但截至写这篇文章为止,市面上大部分多开App没有绕过这项检测,仅有360家的分身大师可以绕过。

    下面开始正式检测:

    1. ps检测(详见https://www.jianshu.com/p/216d65d9971e

    我们先通过执行对uid进行过滤,得到类似下面的结果

    // 正常情况下
    u0_a148 8162 423 1806036 56368 SyS_epoll+ 0 S top.darkness463.virtualcheck
    
    // 多开环境下
    u0_a155 19752 422 4437612 62752 SyS_epoll+ 0 S top.darkness463.virtualcheck
    u0_a155 19758 422 564234 54356 SyS_epoll+ 0 S com.lbe.parallel
    u0_a155 19747 422 734562 24542 SyS_epoll+ 0 S com.lbe.parallel:mdserver

     

    可以看到在多开环境下,会获取到自己的包名和多开App的包名这2个包名,通过这些包名去/data/data/下找会找到2个目录,而正常情况下只能在/data/data/下找到自己的App的目录。看下具体代码实现;

    public static boolean isRunInVirtual() {
    
        String filter = getUidStrFormat();
    
        String result = exec("ps");
    
        if (result == null || result.isEmpty()) {
    
            return false;
    
        }
    
        String[] lines = result.split("\n");
    
        if (lines == null || lines.length <= 0) {
    
            return false;
    
        }
    
        int exitDirCount = 0;
    
        for (int i = 0; i < lines.length; i++) {
    
            if (lines[i].contains(filter)) {
    
                int pkgStartIndex = lines[i].lastIndexOf(" ");
    
                String processName = lines[i].substring(pkgStartIndex <= 0
    
                        ? 0 : pkgStartIndex + 1, lines[i].length());
    
                File dataFile = new File(String.format("/data/data/%s",
    
                        processName, Locale.CHINA));
    
                if (dataFile.exists()) {
    
                    exitDirCount++;
                }
            }
        }
    
        return exitDirCount > 1;
    
    }

    这里的应用列表检测不是指简单的遍历应用列表判断是不是安装了多开App,我们并不阻止用户安装多开App并多开其他App,我们只是不希望用户多开我们自己的App,因此不能检测到用户安装了多开App就把他干掉。

    2.应用列表检测

    多开App都会对context.getPackageName()进行处理,让这个方法返回原始App的包名,因此在被多开的App看来,多开App的包名和原始的那个App的包名一样,因此在多开环境下遍历应用列表时会发现包名等于原始App的包名的应用会有两个。

    private boolean checkPkg(Context context) {
        try {
            if (context == null) {
                return false;
            }
            int count = 0;
            String packageName = context.getPackageName();
            PackageManager pm = context.getPackageManager();
            List<PackageInfo> pkgs = pm.getInstalledPackages(0);
            for (PackageInfo info : pkgs) {
                if (packageName.equals(info.packageName)) {
                    count++;
                }
            }
            return count > 1;
        } catch (Exception ignore) {}
        return false;
    }

    3.maps检测

    读取/proc/self/maps,多开App会加载一些自己的so到内存空间,举个例子,360的分身大师加载了其目录下的某个so,/data/app/com.qihoo.magic-gdEsg8KRAuJy0MuY18BlqQ==/lib/arm/libbreakpad-jni-1.5.so,通过对各种多开App的包名的匹配,如果maps中有多开App的包名的东西,那么当前就是运行在多开环境下。目前没有发现多开App绕过该项检测,但缺点是需要收集所有多开App的包名,一旦多开App改个包名就失效了。

    复制代码

    Set<String> virtualPkgs;  // 多开第三方App包名列表
    private boolean check() {
        BufferedReader bufr = null;
        try {
            bufr = new BufferedReader(new FileReader("/proc/self/maps"));
            String line;
            while ((line = bufr.readLine()) != null) {
                for (String pkg : virtualPkgs) {
                    if (line.contains(pkg)) {
                        return true;
                    }
                }
            }
        } catch (Exception ignore) {
            
        } finally {
            if (bufr != null) {
                try {
                    bufr.close();
                } catch (IOException e) {
                    
                }
            }
        }
        return false;
    }

     

    以上三种检测方法有的会被第三方虚拟定位软件或者多开分身软件躲避掉,有的则不会,所以使用的时候建议三种方法全部用上。

    展开全文
  • YouDianCMS即友点企业网站管理系统集电脑站 手机站 微信站 APP 小程序五合一,数据自动同步,降低人力维护成本;共用一个管理后台,只要一个虚拟主机,有效节约空间投资。系统采用PHP MYSQL,具有操作简单、轻便快捷...
  • 项目获取的平台APIKEY和APISECRET都放置在手机本地,绝不会上传到其他地方,不放心可以直接看源码。 如果使用过程中发现什么问题可以反馈给我们,我们会第一时间协助处理。 功能 包行两个平台各自的币种排行 可对...
  • 《口袋助理》虚拟签到是指通过给 iOS (免越狱)自带地图增加一个模拟定位 (Simulate Location) 的按钮,任意搜索一个位置,将自己的手机定位到该地点,以欺骗《口袋助理》 App, 从而达到虚拟外勤拜访的目的。
  • 它是应用软件的虚拟框架,对用户具有指示标识以及识别的功能。比如,如同路标,导航能在使用中,定位用户当前在哪儿,为用户突出当前视图重要的功能,告知用户可以去哪儿,在不同的视图和区域迅速地切换信息,记录...
  • BLE蓝牙虚拟车钥匙介绍

    千次阅读 2020-06-01 15:49:35
    携带安装过车厂提供包含蓝牙钥匙功能的APP,激活自己的车辆手机蓝牙钥匙功能,四个车门均设置为无钥匙进入功能,驾驶者靠近车辆达到一定距离后,手机钥匙系统自动识别手机钥匙位置并实现解锁。车主关闭所有车门携带...
  • 它是应用软件的虚拟框架,对用户具有指示标识以及识别的功能。比如,如同路标,导航能在使用中,定位用户当前在哪儿,为用户突出当前视图重要的功能,告知用户可以去哪儿,在不同的视图和区域迅速地切换信息,记录...
  • 该应用程序允许用户使用其Android手机/平板电脑的触摸屏来控制钢琴的虚拟表示。 按下键时,将播放相应的声音文件。 此应用程序旨在供音乐家使用,以便在旅途中快速尝试新的想法,或者供渴望使用1990年代中期电子游戏...
  • 虚拟定位App,强大的应用,无需ROOT,支持一部手机轻松实现本地软件和游戏的虚拟定位、源自内核级技术、不占内存,快速稳定!商务、娱乐、游戏用户必备神器! 破解注册。
  • 小编来给你们讲讲,隐私专家是一个专业保护手机安全隐私工具软件,但是主要功能还有虚拟位置、WIFI模拟、拍照模拟、路线模拟、深度隐藏等,并且隐私专家还是一款免root运行环境下的数据测试软件,提供app多开,旨在...
  • 虚拟天文台是一款模拟星空的软件,它可以通过你所处的时间、地点来计算天空中太阳、月亮和行星等星体的位置,并且将其展现在手机上,这样你就能通过手机来观测到美丽的星空。同时他还可以绘制星座以及各种天文景象,...
  • 什么是刘海屏?...像 华为P20 pro, vivo X21,OPPO R15 华为nova 3e,红米note6等手机厂商也纷纷推出自己的刘海屏手机app也要提前做好适配。 屏幕的正上方居中位置(下图黑色区域)会被挖掉一个孔,...
  • 在一些手机游戏中,玩家可以通过虚拟控制盘来控制游戏角色的行动。 无人机和玩具操控App中也有这一类控制盘的应用。用自定义View的方式来实现类似手柄的控件。相关代码请见 github.com/RustFisher/…JoystickView...
  • Tbox在整车CAN网络的位置与作用

    千次阅读 2019-09-26 23:57:57
    我们讲到了智能车载娱乐系统的5个基本特征: ...基本来说, 当今的智能车机基本有以下几个特点: ...基本都是虚拟按键, 较少用实体按键 ...有配套的车联网手机App, 具有手机钥匙及远程车控等功能 具备控制: ...
  • 一播多无人直播手机,这是一款基于安卓手机开发的直播工具,核心的虚拟摄像头技术由前谷歌工程师参与研发,支持全网直播APP内播放本地视频,可用于录制轮播直播带货、老师讲课、才艺展示等视频,省时、省力、省心。...
  • 有配套的车联网手机App, 具有手机钥匙及远程车控等功能 具备控制: 空调, 数字液晶仪表, 360度环视摄像头, 其他车身设备(天窗, 车窗, 大灯)的应用程序. 以上涉及的第3点, 第4点都必须有Tbox设备才能实.
  • (1)自定义菜单:赋予微信更好的用户体验,一个微网站就是一个APP (2)开放的微信API接口:让微信能够和现有的软件系统能够完美整合在一起,微信API接口将手机网站和微信公众帐号无缝整合就形成了微网站 (3)支持...
  • Google Arcore

    2021-03-30 01:02:02
    ARCore概述: Google 的 ARCore 不是一个你可以下载的 app。它是一个软件开发工具包(SDK),来帮助开发者们创建 AR 应用。 ARCore是Google的构建增强现实体验...运动跟踪使手机能够了解和跟踪其相对于世界的位置

空空如也

空空如也

1 2 3 4
收藏数 70
精华内容 28
关键字:

手机虚拟位置app