V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
louislivi
V2EX  ›  Java

结合 AndServer,实现抖音 X-Gorgon 算法,设备 id 生成接口

  •  
  •   louislivi · 2020-04-07 10:14:52 +08:00 · 2890 次点击
    这是一个创建于 1689 天前的主题,其中的信息可能已经有所发展或是发生改变。

    结合 AndServer 实现接口开发

    • Gradle 引入依赖
    implementation 'com.yanzhenjie.andserver:api:2.0.5'
    annotationProcessor 'com.yanzhenjie.andserver:processor:2.0.5'
    implementation 'com.alibaba:fastjson:1.1.71.android'
    
    • 接口类编写
    package com.yf.douyintool.controller;
    
    import android.text.TextUtils;
    import android.util.Log;
    
    import com.alibaba.fastjson.JSONObject;
    import com.bytedance.frameworks.core.encrypt.TTEncryptUtils;
    import com.google.gson.Gson;
    import com.ss.sys.ces.a;
    import com.yanzhenjie.andserver.annotation.GetMapping;
    import com.yanzhenjie.andserver.annotation.PostMapping;
    import com.yanzhenjie.andserver.annotation.RequestBody;
    import com.yanzhenjie.andserver.annotation.RestController;
    import com.yanzhenjie.andserver.util.MediaType;
    import com.yf.douyintool.DeviceUtil;
    import com.yf.douyintool.bean.DeviceBean;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.ArrayList;
    import java.util.Calendar;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.TimeZone;
    import java.util.UUID;
    import java.util.concurrent.TimeUnit;
    import java.util.zip.GZIPOutputStream;
    
    import okhttp3.ConnectionPool;
    import okhttp3.FormBody;
    import okhttp3.Interceptor;
    import okhttp3.OkHttpClient;
    import okhttp3.Protocol;
    import okhttp3.Request;
    import okhttp3.Response;
    
    @RestController
    public class RequestController {
        private static final String NULL_MD5_STRING = "00000000000000000000000000000000";
        public String sessionid = "";
        public String xtttoken = "";
    
        @PostMapping(value = "/request", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
        public String test(@RequestBody String requestBodyString) {
            JSONObject jsonObject = new JSONObject();
            JSONObject requestBody = JSONObject.parseObject(requestBodyString);
            String ck = requestBody.getString("cookie");
            String url = requestBody.getString("url");
            String postData = requestBody.getString("postData");
            if (ck == null || url == null) {
                jsonObject.put("status_code", 0);
                jsonObject.put("status_msg", "缺少参数!");
                return jsonObject.toJSONString();
            }
            if (url.contains("douplus/order/create")) {
                // 如果是投放订单每次生产不同的 DeviceData
                JSONObject deviceData = getNewDeviceData();
                url = replaceUrlParam(url, "device_id", deviceData.getString("device_id"));
                url = replaceUrlParam(url, "iid", deviceData.getString("install_id"));
    //            Log.i("orderCreate", "替换 device_id 成功!");
            }
    //        String _ricket = System.currentTimeMillis() + "";
            long time = System.currentTimeMillis() / 1000;
            String p = url.substring(url.indexOf("?") + 1, url.length());
            boolean isPost = postData != null && !postData.equals("");
            String result;
            if (isPost) {
                FormBody.Builder formBody = new FormBody.Builder();
                String STUB = encryption(postData);
                Map<String, String> map = new HashMap<>();
                String[] ks = postData.split("&");
                for (int i = 0; i < ks.length; i++) {
                    String[] ur = ks[i].split("=");
                    if (ur.length == 1) {
                        map.put(ur[0], "");
                    } else {
                        map.put(ur[0], ur[1]);
    
                    }
                }
                for (Map.Entry<String, String> m : map.entrySet()) {
                    formBody.add(m.getKey(), m.getValue());
                }
                String s = getXGon(p, STUB, ck, sessionid);
                String XGon = ByteToStr(a.leviathan((int) time, StrToByte(s)));
                result = doPostNet(url, formBody.build(), time, XGon, STUB, ck);
            } else {
                String s = getXGon(p, "", ck, sessionid);
                String XGon = ByteToStr(a.leviathan((int) time, StrToByte(s)));
                result = doGetNet(url, time, XGon, ck);
            }
            jsonObject = JSONObject.parseObject(result);
            if (jsonObject == null) {
                jsonObject = new JSONObject();
                jsonObject.put("status_code", -2);
                jsonObject.put("status_msg", "未知错误!");
            }
            return jsonObject.toJSONString();
        }
    
    
        @GetMapping(value = "/getDeviceData", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
        public String getDeviceData() {
            return getNewDeviceData().toJSONString();
        }
    
        @GetMapping(value = "/getQuery", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
        public String getQuery() {
            String uuid = DeviceUtil.getRanInt(15);  //设备 id
            String openudid = DeviceUtil.getRanInt(16);  //android_id
            String _rticket = System.currentTimeMillis() + "";   //获取当前时间
            String url = "https://log.snssdk.com/service/2/device_register/?mcc_mnc=46000&ac=wifi&channel=aweGW&aid=1128&app_name=aweme&version_code=550&version_name=5.5.0&device_platform=android&ssmix=a&device_type=SM-G925F&device_brand=samsung&language=zh&os_api=22&os_version=5.1.1&uuid=" + uuid + "&openudid=" + openudid + "&manifest_version_code=550&resolution=720*1280&dpi=192&update_version_code=5502&_rticket=" + _rticket + "&tt_data=a&config_retry=b";
            String stb = url.substring(url.indexOf("?") + 1, url.length());
            String STUB = encryption(stb).toUpperCase();
            String ck = "odin_tt=9c1e0ebae55f3c2d9f71ab2aadce63126022e8960819bace07d441d977ad60eff6312161f546ebfe747528d03d53a161728250938c4287a588d86aa599c284b3; qh[360]=1; install_id=66715314288; ttreq=1$0b4589453328800ed93e002538883aa52da3e1d5";
            int time = (int) (System.currentTimeMillis() / 1000);
            String s = getXGon(url, STUB, ck, null);
            String XGon = ByteToStr(a.leviathan(time, StrToByte(s)));
            String device = getDevice(openudid, uuid);
            JSONObject deviceJson = JSONObject.parseObject(device);
            final okhttp3.RequestBody formBody = okhttp3.RequestBody.create(okhttp3.MediaType.parse("application/octet-stream;tt-data=a"), this.toGzip(device));
            String result = doPostNet(url, formBody, time, XGon, "", ck);
            JSONObject deviceResult = JSONObject.parseObject(result);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("status_code", 0);
            JSONObject headerJson = deviceJson.getJSONObject("header");
            String data = String.format("os_api=22&device_type=SM-G925F&ssmix=a&manifest_version_code=911&dpi=320&uuid=%s&app_name=aweme&version_name=9.1.1&ts=%d&app_type=normal&ac=wifi&update_version_code=9104&channel=huawei_1&_rticket=%s&device_platform=android&iid=%s&version_code=911&cdid=%s&openudid=%s&device_id=%s&resolution=720*1280&os_version=5.1.1&language=zh&device_brand=OPPO&aid=1128&mcc_mnc=46007",
                    uuid, time, _rticket, deviceResult.getString("install_id_str"), headerJson.getString("clientudid"), openudid, deviceResult.getString("device_id_str"));
            jsonObject.put("data", data);
            Log.i("data", data);
            return jsonObject.toJSONString();
        }
    
        public JSONObject getNewDeviceData() {
            String uuid = DeviceUtil.getRanInt(15);  //设备 id
            String openudid = DeviceUtil.getRanInt(16);  //android_id
            String _rticket = System.currentTimeMillis() + "";   //获取当前时间
            String url = "https://log.snssdk.com/service/2/device_register/?mcc_mnc=46000&ac=wifi&channel=aweGW&aid=1128&app_name=aweme&version_code=550&version_name=5.5.0&device_platform=android&ssmix=a&device_type=SM-G925F&device_brand=samsung&language=zh&os_api=22&os_version=5.1.1&uuid=" + uuid + "&openudid=" + openudid + "&manifest_version_code=550&resolution=720*1280&dpi=192&update_version_code=5502&_rticket=" + _rticket + "&tt_data=a&config_retry=b";
            String stb = url.substring(url.indexOf("?") + 1, url.length());
            String STUB = encryption(stb).toUpperCase();
            String ck = "odin_tt=9c1e0ebae55f3c2d9f71ab2a12ce63c46022e8912819bace07d441d977ad60eff6301161f546ebfe747528d03d53a161728250938c4287a588d86aa599c284b3; qh[360]=1; install_id=66715314288; ttreq=1$0b4589453328800ed93e002538883aa52da3e1d5";
            int time = (int) (System.currentTimeMillis() / 1000);
            String s = getXGon(url, STUB, ck, null);
            String XGon = ByteToStr(a.leviathan(time, StrToByte(s)));
            final okhttp3.RequestBody formBody = okhttp3.RequestBody.create(okhttp3.MediaType.parse("application/octet-stream;tt-data=a"), this.toGzip(this.getDevice(openudid, uuid)));
            String result = doPostNet(url, formBody, time, XGon, "", ck);
            JSONObject jsonObject = JSONObject.parseObject(result);
            if (jsonObject == null) {
                jsonObject = new JSONObject();
                jsonObject.put("status_code", -2);
                jsonObject.put("status_msg", "未知错误!");
            }
            return jsonObject;
        }
    
        public String getDevice(String openudid, String udid) {
            //DeviceBean
            String Serial_number = DeviceUtil.getRanInt(8);
            DeviceBean deviceBean = new DeviceBean();
            deviceBean.set_gen_time(System.currentTimeMillis() + "");
            deviceBean.setMagic_tag("ss_app_log");
            //HeaderBean
            DeviceBean.HeaderBean headerBean = new DeviceBean.HeaderBean();
            headerBean.setDisplay_name("抖音短视频");
            headerBean.setUpdate_version_code(5502);
            headerBean.setManifest_version_code(550);
            headerBean.setAid(1128);
            headerBean.setChannel("aweGW");
            headerBean.setAppkey("59bfa27c67e59e7d920028d9"); //appkey
            headerBean.setPackageX("com.ss.android.ugc.aweme");
            headerBean.setApp_version("5.5.0");
            headerBean.setVersion_code(550);
            headerBean.setSdk_version("2.5.5.8");
            headerBean.setOs("Android");
            headerBean.setOs_version("5.1.1");
            headerBean.setOs_api(22);
            headerBean.setDevice_model("SM-G925F");
            headerBean.setDevice_brand("samsung");
            headerBean.setDevice_manufacturer("samsung");
            headerBean.setCpu_abi("armeabi-v7a");
            headerBean.setBuild_serial(Serial_number);  ////android.os.Build.SERIAL
            headerBean.setRelease_build("2132ca7_20190321");  // release 版本
            headerBean.setDensity_dpi(192);
            headerBean.setDisplay_density("mdpi");
            headerBean.setResolution("1280x720");
            headerBean.setLanguage("zh");
            headerBean.setMc(DeviceUtil.getMac());  //mac 地址
            headerBean.setTimezone(8);
            headerBean.setAccess("wifi");
            headerBean.setNot_request_sender(0);
            headerBean.setCarrier("China Mobile GSM");
            headerBean.setMcc_mnc("46000");
            headerBean.setRom("eng.se.infra.20181117.120021");  //Build.VERSION.INCREMENTAL
            headerBean.setRom_version("samsung-user 5.1.1 20171130.276299 release-keys");  //Build.DISPLAY
            headerBean.setSig_hash("aea615ab910015038f73c47e45d21466");  //app md5 加密  固定
            headerBean.setDevice_id("");   //获取之后的设备 id
            headerBean.setOpenudid(openudid);  //openudid
            headerBean.setUdid(udid);  //真机的 imei
            headerBean.setClientudid(UUID.randomUUID().toString());  //uuid
            headerBean.setSerial_number(Serial_number);  //android.os.Build.SERIAL
            headerBean.setRegion("CN");
            headerBean.setTz_name("Asia\\/Shanghai");  //timeZone.getID();
            headerBean.setTimezone(28800);  //String.valueOf(timeZone.getOffset(System.currentTimeMillis()) / 1000)
            headerBean.setSim_region("cn");
            List<DeviceBean.HeaderBean.SimSerialNumberBean> sim_serial_number = new ArrayList<>();
            DeviceBean.HeaderBean.SimSerialNumberBean bean = new DeviceBean.HeaderBean.SimSerialNumberBean();
            bean.setSim_serial_number(DeviceUtil.getRanInt(20));
            sim_serial_number.add(bean);
            headerBean.setSim_serial_number(sim_serial_number);
    //        Log.i("deviceHeader", headerBean.toString());
            deviceBean.setHeader(headerBean);
            TimeZone timeZone = Calendar.getInstance().getTimeZone();
            timeZone.getID();
            //r
            Gson gson = new Gson();
            return gson.toJson(deviceBean);
        }
    
        public byte[] toGzip(String r) {
            try {
                byte[] bArr2 = r.getBytes("UTF-8");
    
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(8192);
                GZIPOutputStream gZIPOutputStream = new GZIPOutputStream(byteArrayOutputStream);
                gZIPOutputStream.write(bArr2);
                gZIPOutputStream.close();
                bArr2 = byteArrayOutputStream.toByteArray();
                bArr2 = TTEncryptUtils.a(bArr2, bArr2.length);
                return bArr2;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
    
        public class RetryIntercepter implements Interceptor {
    
            public int maxRetry;//最大重试次数
            private int retryNum = 0;//假如设置为 3 次重试的话,则最大可能请求 4 次(默认 1 次+3 次重试)
    
            public RetryIntercepter(int maxRetry) {
                this.maxRetry = maxRetry;
            }
    
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
    //            System.out.println("retryNum=" + retryNum);
                boolean isSuccessful;
                Response response = null;
                try {
                    response = chain.proceed(request);
                    isSuccessful = response.isSuccessful();
                } catch (Exception e) {
                    isSuccessful = false;
                }
                while (!isSuccessful && retryNum < maxRetry) {
                    retryNum++;
                    System.out.println("retryNum=" + retryNum);
                    response = chain.proceed(request);
                }
    
                return response;
            }
        }
    
        public String doGetNet(String url, long time, String XGon, String ck) {
    //        Log.i("XGon", XGon);
    //        Log.i("time", String.valueOf(time));
            Request request = new Request.Builder()
                    .url(url)
                    .get()
                    .addHeader("X-SS-REQ-TICKET", System.currentTimeMillis() + "")
                    .addHeader("X-Khronos", time + "")
                    .addHeader("X-Gorgon", XGon)
                    .addHeader("sdk-version", "1")
                    .addHeader("Cookie", ck)
                    .addHeader("X-Pods", "")
                    .addHeader("Connection", "Keep-Alive")
                    .addHeader("User-Agent", "okhttp/3.10.0.1")
                    .addHeader("x-tt-token", xtttoken)
                    .addHeader("Accept-Encoding", "identity")
                    .addHeader("Connection", "Upgrade, HTTP2-Settings")
                    .addHeader("Upgrade", "h2c")
                    .build();
            List<Protocol> protocols = new ArrayList<>();
    // protocols.add(Protocol.H2_PRIOR_KNOWLEDGE);
            protocols.add(Protocol.HTTP_2);
            protocols.add(Protocol.HTTP_1_1);
            OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .retryOnConnectionFailure(true)
                    .protocols(protocols)
                    .connectionPool(new ConnectionPool(10, 30, TimeUnit.SECONDS))
                    .addInterceptor(new RetryIntercepter(3))
                    .build();
            Response response = null;
            try {
                response = okHttpClient.newCall(request).execute();
            } catch (IOException e) {
                e.printStackTrace();
            }
            //响应成功
            if (response.isSuccessful()) {
                try {
                    return response.body().string();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("status_code", -2);
            jsonObject.put("status_msg", "未知错误!");
            return jsonObject.toJSONString();
        }
    
    
    
        ......主题内容不能超过 20000 个字符
    }
    
    • AndServer 服务类
    package com.yf.douyintool;
    
    import android.content.Context;
    import android.util.Log;
    
    import com.yanzhenjie.andserver.AndServer;
    import com.yanzhenjie.andserver.Server;
    
    import java.net.InetAddress;
    import java.net.UnknownHostException;
    import java.util.concurrent.TimeUnit;
    
    /**
     * Created by Zhenjie Yan on 2018/6/9.
     */
    public class ServerManager {
    
        private static final String TAG = "ServerManager";
    
        private Server mServer;
    
        /**
         * Create server.
         */
        public ServerManager(Context context) {
            InetAddress inetAddress = null;
            try {
                inetAddress = InetAddress.getByName("0.0.0.0");
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
            mServer = AndServer.serverBuilder(context)
                    .inetAddress(inetAddress)
                    .port(8080)
                    .timeout(10, TimeUnit.SECONDS)
                    .listener(new Server.ServerListener() {
                        @Override
                        public void onStarted() {
                            // TODO The server started successfully.
                            Log.d(TAG, "onStarted: ");
                        }
    
                        @Override
                        public void onStopped() {
                            // TODO The server has stopped.
                            Log.d(TAG, "onStarted: ");
                        }
    
                        @Override
                        public void onException(Exception e) {
                            Log.e(TAG, "onException: ",e );
                            // TODO An exception occurred while the server was starting.
                        }
                    })
                    .build();
        }
    
        /**
         * Start server.
         */
        public void startServer() {
            if (mServer.isRunning()) {
                // TODO The server is already up.
            } else {
                mServer.startup();
            }
        }
    
        /**
         * Stop server.
         */
        public void stopServer() {
            if (mServer.isRunning()) {
                mServer.shutdown();
            } else {
                Log.w("AndServer", "The server has not started yet.");
            }
        }
    }
    
    
    • 在 MainActivity.onCreate 方法中加入
    serverManager = new ServerManager(this);
    serverManager.startServer();
    Log.i("address", NetUtils.getLocalIPAddress()+":8080");
    application = this;
    Thread.setDefaultUncaughtExceptionHandler(handler);
    
    • 崩溃重启
    private Thread.UncaughtExceptionHandler handler = (t, e) -> {
            restartApp(); //发生崩溃异常时,重启应用
        };
    

    测试接口

    • getDeviceData getDeviceData
    • getQuery getQuery
    • request request

    Cookie 获取方式

    有爬取登录接口实现的,但其实有一种简单的方式就是二维码登录,但是涉及到一些隐秘性这里就不公开说明了。

    沟通讨论

    有问题可以加 QQ:574747417 共同讨论。

    1 条回复    2020-04-07 15:44:27 +08:00
    SHOWLEE
        1
    SHOWLEE  
       2020-04-07 15:44:27 +08:00
    点赞,加油
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1001 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 20:13 · PVG 04:13 · LAX 12:13 · JFK 15:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.