Dji Mimo APP逆向.1(pocket 2直播模式+物体追踪)
大疆的东西,我真的太喜欢了。。。喜欢到想看看是如何做出来的。
逆向菜鸡,安卓羸弱,反正就是个看热闹的主,大佬们轻点拍~
这个就是将应用退壳的东西
具体是一个梆梆企业版的壳子,我是在真机上面把壳砸了的
大概就是在运行前,需要从壳里面把真正的应用解压出来,然后我们从内存里面把这个dump出来,因为是一个完整的调用链条,然后再组装回来~~~~
https://mydown.yesky.com/pcsoft/413552646.html
当然了,这个东西是可以解压的
算了,本来想展示点别的东西,这个电脑不合适,看源码吧
其实java还好一点,反编译的源码还有点难懂,C#完全就是源码。。。
一个是R文件的解析,在安卓的世界里面各种资源都是要被打包到R文件里面的,所以你看到的是一个这样的反编译的资源文件
我推测,对于一些控制类的操作是用json打包发送的
视频流+控制“流”
Google分析的组件,毕竟作为国际大厂,出海是必然的
二维码的组件,作为一些登录时使用
第二个jar文件,我推测是给poctek 2代的直播流写的模块
你看这个是虎牙直播的组件
public UUID getUUId() {
return this.uuid;
}
public boolean isEmpty() {
return this.raw.isEmpty();
}
可以点进去仔细的看,这里的包没有混淆
这里是dji的实现,让我稍微看看是什么东西
package com.dji.socialsdkwx.login.wechat;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.tencent.mm.opensdk.modelbase.BaseReq;
import com.tencent.mm.opensdk.modelbase.BaseResp;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
public class SocialWeChatActivity extends AppCompatActivity implements IWXAPIEventHandler {
private IWXAPI api;
/* access modifiers changed from: protected */
public void onCreate(Bundle bundle) {
}
/* access modifiers changed from: protected */
public void onNewIntent(Intent intent) {
}
public void onReq(BaseReq baseReq) {
}
public void onResp(BaseResp baseResp) {
}
}
import com.tencent.mm.opensdk.modelbase.BaseReq;
import com.tencent.mm.opensdk.modelbase.BaseResp;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
你看这个,其实是微信的登录接口,至于其它的可能要设置语言才可以打开
package com.dji.livestream.livetype;
import android.app.Dialog;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.dji.device.util.c;
import com.dji.livestream.R;
import com.dji.livestream.bean.DJILiveLoadingStage;
import com.dji.livestream.bean.b;
import com.dji.livestream.livetype.-$;
import com.dji.livestream.livetype.a;
import com.dji.livestream.page.LiveStreamBaseActivity;
import dji.base.ui.thirdparty.permission.CommonPermissionHelper;
import org.greenrobot.eventbus.ThreadMode;
import org.greenrobot.eventbus.l;
这里的安卓库分为三类,原生的安卓构件,dji自己的库
以及使用的三方库
EventBus的特性包括:
简化了组件间的通讯
分离了事件的发送者和接受者;
在Activity、Fragment和线程中表现良好;
避免了复杂的和易错的依赖关系和声明周期问题;
使得代码更简洁;
更快;
更小(约50k的jar包)
实际上超过1亿的app证明了它的性能;
有高级属性,例如发布线程、订阅属性等。
可以看到有很多新的软件库在里面,比如全新的储存管理
以及有很多的广播接收器,Facebook,HW
看,HW的推送库
以及全新的安卓库
干哦兄弟!
原生C++
以及FFmpeg的解码库
以及人脸追踪???是用的这种东西吗
private final String[] PERMISSIONS = {"android.permission.BLUETOOTH", "android.permission.BLUETOOTH_ADMIN", "android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION"};
// permissions={blue,blue_admin,access_file_location,access_coarse_location}
private final int REQUEST_CODE_LIVE_SETTINGS = 1011;
// request_code_live_settings
这里的代码做了一点识别,可以看到要动态申请的权限是蓝牙的
private Dialog confirmDialog;
private com.dji.device.common.a connectHelper;
private View facebookLive;
private View facebookSelectedIcon;
private Handler handler = new Handler(Looper.getMainLooper());
private View huyaLive;
private View huyaSelectedIcon;
private View kuaiShouLive;
private View kuaiShouSelectedIcon;
private CommonPermissionHelper permissionHelper;
private CommonPermissionHelper.a permissionRequestData;
private a.b presenter;
private View rtmpLive;
private View rtmpSelectedIcon;
在看这些函数就很有趣了,有连接大疆相机时候的指引,脸书的直播,虎牙的直播,快手的直播,以及支持自定义的RTMP直播
import com.dji.device.connect.bean.c;
package com.dji.network.bean;
import com.google.gson.annotations.SerializedName;
public class BaseBean extends BaseSerializable {
@SerializedName("code")
protected int code;
@SerializedName("msg")
protected String msg;
public int getCode() {
return this.code;
}
public String getMsg() {
return this.msg;
}
public void setCode(int i) {
this.code = i;
}
public void setMsg(String str) {
this.msg = str;
}
}
基本可序列化???
这里是小米的推送包
public String dataPlatform() {
return "XiaoMi Push";
}
OkHttp 是一个高效的 HTTP 客户端,具有非常多的优势:
能够高效的执行 http,数据加载速度更快,更省流量
支持 GZIP 压缩,提升速度,节省流量
缓存响应数据,避免了重复的网络请求
使用简单,支持同步阻塞调用和带回调的异步调用
Retrofit2简单的说就是一个网络请求的适配器,它将一个基本的Java接口通过动态代理的方式翻译成一个HTTP请求,并通过OkHttp去发送请求。
Gson(又称Google Gson)是Google公司发布的一个开放源代码的Java库,主要用途为序列化Java对象为JSON字符串,或反序列化JSON字符串成Java对象。
https://djigo-hk.djiservice.org
不鸟我
Osmo Shield Domain
package com.dji.pano.osmo.mobile;
import com.dji.pano.osmo.mobile.b;
class b$1 implements b.a {
final /* synthetic */ b a;
b$1(b bVar) {
this.a = bVar;
}
public void a() {
}
public void a(int i, String str) {
this.a.a(i, str);
}
public void a(String str) {
}
public void b() {
}
public void c() {
}
}
implements是一个类实现一个接口用的关键字 ,他是用来实现接口中定义的抽象方法。比如:people是一个接口,他里面有say这个方法。public interface people(){ public say();}但是接口没有方法体。只能通过一个具体的类去实现其中的方法体。比如chinese这个类,就实现了people这个接口。public class chinese implements people{ public say() {System.out.println("你好!");}}
https://github.com/square/wire
public RootData(Integer num, EncryptionType encryptionType, PlatformType platformType, String str, Integer num2, Integer num3, ByteString byteString, String str2, Integer num4, String str3, ByteString byteString2) {
super(a, byteString2);
this.version = num;
this.encryption = encryptionType;
this.platform = platformType;
this.app_key = str;
this.app_version = num2;
this.plaintext_len = num3;
this.content = byteString;
this.cmd = str2;
this.timestamp = num4;
this.signature = str3;
}
这个是一个协议的实现我没有细研究,直接贴上来
package com.dji.tracking.jnienum;
public enum CameraFace {
LTSOT_FRONT_CAMERA(1),
// ltsot_front_camera
LTSOT_BEHIND_CAMERA(2);
public final int value;
private CameraFace(int i) {
this.value = i;
}
}
这个是关于脸部追踪的代码
package com.dji.tracking.jnienum;
public enum CheckedObjectType {
ML_OBJ_OTHER(0, "其他"),
ML_OBJ_HEAD_SHOULDER(1, "头肩"),
ML_OBJ_PERSON(2, "人"),
ML_OBJ_FACE(5, "脸"),
ML_OBJ_PET(6, "宠物"),
ML_OBJ_GESTURE_PALM(101, "手掌"),
ML_OBJ_GESTURE_VICTORY(102, "V型手势"),
ML_OBJ_GESTURE_OTHER(103, "其他手势"),
ML_OBJ_NONE(-1, "NONE");
public final String tip;
public final int value;
private CheckedObjectType(int i, String str) {
this.value = i;
this.tip = str;
}
public static CheckedObjectType a(int i) {
CheckedObjectType[] values = values();
for (CheckedObjectType checkedObjectType : values) {
if (checkedObjectType.value == i) {
return checkedObjectType;
}
}
return ML_OBJ_NONE;
}
}
物体识别
package com.dji.tracking.jinout;
import androidx.core.util.Pools;
public class TrackingBox {
private static Pools.SynchronizedPool<TrackingBox> sPool = new Pools.SynchronizedPool<>(10);
public float centerX;
public float centerY;
public float height;
public float width;
public static TrackingBox obtain() {
TrackingBox trackingBox = (TrackingBox) sPool.acquire();
return trackingBox != null ? trackingBox : new TrackingBox();
}
public void recycle() {
sPool.release(this);
}
}
绘制一个追踪的盒子
package com.dji.tracking.jnienum;
public enum RotateAngle {
ML_ROTATE_0(0),
ML_ROTATE_90_CLOCKWISE(1),
ML_ROTATE_180(2),
ML_ROTATE_90_COUNTERCLOCKWISE(3);
public final int value;
private RotateAngle(int i) {
this.value = i;
}
}
旋转的角度
package com.dji.tracking.jnienum;
import androidx.collection.SparseArrayCompat;
public enum TrackingMode {
TK_MODE_UNFUSED(-1),
TK_MODE_LOST(0),
TK_MODE_TRACKED(1),
TK_MODE_NOT_CONFIDENT(2),
TK_MODE_REDETECTED(3),
TK_MODE_GESTURE_STOP(4),
TK_MODE_REAL_LOST(5);
private static final SparseArrayCompat<TrackingMode> h = new SparseArrayCompat<>();
public final int value;
private TrackingMode(int i2) {
this.value = i2;
}
public static TrackingMode a(int i2) {
TrackingMode trackingMode = (TrackingMode) h.get(i2);
if (trackingMode == null) {
TrackingMode[] values = values();
int length = values.length;
int i3 = 0;
while (true) {
if (i3 >= length) {
break;
}
TrackingMode trackingMode2 = values[i3];
if (trackingMode2.value == i2) {
trackingMode = trackingMode2;
break;
}
i3++;
}
if (trackingMode == null) {
trackingMode = TK_MODE_UNFUSED;
}
h.put(i2, trackingMode);
}
return trackingMode;
}
}
追踪模式
package com.dji.tracking;
import android.os.Build;
import com.dji.tracking.a.a;
import com.dji.tracking.a.b;
/* access modifiers changed from: package-private */
public class g {
private static final String[] a = {"HUAWEI ANE", "HONOR STF"};
static f a() {
String upperCase = String.format("%s %s", Build.BRAND, Build.MODEL).toUpperCase();
for (String str : a) {
if (upperCase.contains(str)) {
return new a();
}
}
return new b();
}
}
这个不知道是为什么,单独的说了华为,大写?
最后看看这个,video里面有什么
package com.dji.video.codec;
import android.media.MediaCodec;
import com.dji.video.stream.AACFrameDelegate;
import com.dji.video.stream.AudioConfigs;
import com.dji.video.utils.c;
import java.nio.ByteBuffer;
import java.util.UUID;
import java.util.UUID;
public class AudioStreamDecoder {
private final int a = 0;
private final int b = 1;
private final int c = 3;
private int d = 0;
private MediaCodec e;
private ByteBuffer[] f;
private ByteBuffer[] g;
private boolean h;
private boolean i = false;
private long j = 0;
private final int k = 16384;
private c l = new c(64, 16384);
private byte[] m = new byte[16384];
private a n;
private AudioConfigs o;
private UUID p;
private AACFrameDelegate q;
private boolean r = false;
class a extends Thread {
private boolean b = false;
a() {
}
public void run() {
}
}
音频解码类
package com.dji.video.codec;
import com.dji.video.codec.MediaCodecDecoder;
import com.dji.video.edit.MvModel;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
public class c {
public static List<MediaCodecDecoder.a> a = new ArrayList();
public static Hashtable<String, MediaCodecDecoder.b> b = new Hashtable<>();
public static void a(ArrayList<MvModel> arrayList) {
if (arrayList != null) {
if (a.size() > 0) {
a.clear();
}
if (b.size() > 0) {
b.clear();
}
for (int i = 0; i < arrayList.size(); i++) {
MvModel mvModel = arrayList.get(i);
if (mvModel.d() == 1) {
MediaCodecDecoder.b bVar = new MediaCodecDecoder.b();
String b2 = mvModel.b();
bVar.a = mvModel.e();
bVar.b = mvModel.f();
bVar.d = mvModel.g();
bVar.c = (int) (mvModel.h() + mvModel.c());
if (!b.containsKey(b2)) {
b.put(b2, bVar);
} else if (b.get(b2).c < bVar.c) {
b.put(b2, bVar);
}
}
}
}
}
}
推测这个是视频编辑功能里面的一个子功能
下面的代码有这个库
MediaCodec类可用于访问Android底层的多媒体编解码器,例如,编码器/解码器组件。它是Android底层多媒体支持基础架构的一部分(通常与MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface, 以及AudioTrack一起使用)。
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaExtractor;
import android.media.MediaFormat;
package com.dji.video.codec;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Build;
import java.io.File;
import java.io.IOException;
import java.util.List;
import junit.framework.Assert;
public class e {
public class a {
private c b;
public a(c cVar) {
this.b = cVar;
}
private void a(int i) throws Exception {
}
public void a() throws Exception {
}
}
public class b {
private c b;
public b(c cVar) {
this.b = cVar;
}
private void a(MediaExtractor mediaExtractor, int i, MediaCodec mediaCodec) throws Exception {
}
public void a() throws Exception {
}
}
public class c {
int a = 0;
boolean b = false;
boolean c = false;
public c() {
}
}
public class d extends Thread {
private a b;
public d(a aVar) {
this.b = aVar;
}
public void run() {
try {
this.b.a();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/* renamed from: com.dji.video.codec.e$e reason: collision with other inner class name */
public class C0001e extends Thread {
private b b;
public C0001e(b bVar) {
this.b = bVar;
}
public void run() {
try {
this.b.a();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public e() {
this.b = null;
this.c = null;
this.l = 1280;
this.m = 720;
this.n = this.l;
this.o = this.m;
this.p = this.l;
this.q = this.m;
this.r = 0;
this.s = 0;
this.t = 0;
this.u = 0;
this.v = 0;
this.w = 0;
this.x = 0;
this.y = 0;
this.z = "video/avc";
this.A = 6000000;
this.B = 6000000;
this.C = 25;
this.D = 25;
this.E = -1;
this.I = false;
this.J = false;
this.O = 0;
this.P = false;
this.X = false;
this.b = e();
this.c = f();
}
static /* synthetic */ int J(e eVar) {
int i2 = eVar.O;
eVar.O = i2 + 1;
return i2;
}
static /* synthetic */ int L(e eVar) {
int i2 = eVar.T;
eVar.T = i2 + 1;
return i2;
}
/* access modifiers changed from: private */
public int a(MediaExtractor mediaExtractor) {
return 0;
}
/* access modifiers changed from: private */
public void a(byte[] bArr, byte[] bArr2, int i2, int i3, int i4, int i5) {
}
private static boolean a(int i2) {
if (!(i2 == 39 || i2 == 2130706688)) {
switch (i2) {
case 19:
case 20:
case 21:
break;
default:
return false;
}
}
return true;
}
/* access modifiers changed from: private */
public static int b(MediaCodecInfo mediaCodecInfo, String str) {
MediaCodecInfo.CodecCapabilities capabilitiesForType = mediaCodecInfo.getCapabilitiesForType(str);
for (int i2 = 0; i2 < capabilitiesForType.colorFormats.length; i2++) {
int i3 = capabilitiesForType.colorFormats[i2];
if (a(i3)) {
return i3;
}
}
Assert.fail("找不到适合的颜色格式 " + mediaCodecInfo.getName() + " / " + str);
return 0;
}
/* access modifiers changed from: private */
public static MediaCodecInfo b(String str) {
String[] supportedTypes;
int codecCount = MediaCodecList.getCodecCount();
for (int i2 = 0; i2 < codecCount; i2++) {
MediaCodecInfo codecInfoAt = MediaCodecList.getCodecInfoAt(i2);
if (codecInfoAt.isEncoder()) {
for (String str2 : codecInfoAt.getSupportedTypes()) {
if (str2.equalsIgnoreCase(str)) {
return codecInfoAt;
}
}
continue;
}
}
return null;
}
private static boolean b(int i2) {
if (!(i2 == 39 || i2 == 2130706688)) {
switch (i2) {
case 19:
case 20:
return false;
case 21:
break;
default:
throw new RuntimeException("unknown format " + i2);
}
}
return true;
}
/* access modifiers changed from: private */
public void c(String str) {
}
private void d() throws IOException {
}
private String e() {
return Build.MODEL;
}
private String f() {
return Build.VERSION.RELEASE;
}
static /* synthetic */ int x(e eVar) {
int i2 = eVar.M;
eVar.M = i2 + 1;
return i2;
}
public void a() {
}
public void a(long j2, long j3, int i2) {
}
public void a(long j2, long j3, String str, String str2, String str3, int i2) {
}
public void a(b bVar) {
this.Y = bVar;
}
public void b() {
}
public void c() {
}
}
这段代码,emmm,啥也看不出出来了,只能猜了
https://www.android-doc.com/reference/android/media/MediaCodecInfo.CodecCapabilities.html
public static int b(MediaCodecInfo mediaCodecInfo, String str) {
MediaCodecInfo.CodecCapabilities capabilitiesForType = mediaCodecInfo.getCapabilitiesForType(str);
for (int i2 = 0; i2 < capabilitiesForType.colorFormats.length; i2++) {
int i3 = capabilitiesForType.colorFormats[i2];
if (a(i3)) {
return i3;
}
}
Assert.fail("找不到适合的颜色格式 " + mediaCodecInfo.getName() + " / " + str);
return 0;
}
看这个函数,传入一个编码函数,和一个strings
然后就是一个遍历在匹配一个什么东西?应该是颜色的格式
package com.dji.video.codec;
import android.graphics.BitmapFactory;
public class ImageDecoder {
private int a;
private int b;
public static int[] get_image_paras(String str) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(str, options);
return new int[]{options.outWidth, options.outHeight, 0, 0};
}
public int get_height() {
return this.b;
}
public int[] get_image_data(String str) {
return null;
}
public void get_image_para(String str) {
}
public int get_width() {
return this.a;
}
}
这个函数简单,见名知其义
package com.dji.video.codec;
import java.util.HashMap;
import wseemann.media.FFmpegMediaMetadataRetriever;
public class VideoDescription {
private static final String a = "VideoDescription";
private String b;
private HashMap<String, String> c;
private void a(FFmpegMediaMetadataRetriever.a aVar, String str) {
}
private native void nativeGetDescription(String str, HashMap<String, String> hashMap);
public void a(String str) {
}
public String b(String str) {
return null;
}
}
这个函数是读取一个视频的info
就先到这里~下期再见