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 个字符
}
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.");
}
}
}
serverManager = new ServerManager(this);
serverManager.startServer();
Log.i("address", NetUtils.getLocalIPAddress()+":8080");
application = this;
Thread.setDefaultUncaughtExceptionHandler(handler);
private Thread.UncaughtExceptionHandler handler = (t, e) -> {
restartApp(); //发生崩溃异常时,重启应用
};
有爬取登录接口实现的,但其实有一种简单的方式就是二维码登录,但是涉及到一些隐秘性这里就不公开说明了。
有问题可以加 QQ:574747417 共同讨论。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.