吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 22160|回复: 284
上一主题 下一主题
收起左侧

[Android 原创] Android逆向破解某力定位打卡

    [复制链接]
跳转到指定楼层
楼主
debug_cat 发表于 2023-1-2 12:23 回帖奖励
本帖最后由 莫问刀 于 2023-2-11 15:58 编辑

背景

原来公司使用的是某叮打卡,就是普通的定位打卡,之前已经从系统层做好了位置修改,配合自己写的APP做了注入任意位置,就在周五2022年12月30日突然发公告切换了打卡软件某力e,既然换了那就试试某叮的那一套对它有没有效果,结果很显然无效,如果有效就没有这一篇文章了。

一些猜想

因为之前分析过某叮的定位逻辑,这里大概描述一下。

它的定位逻辑是两个方向的,第一它通过SDK接口,申请了系统的GPS定位,调用了LocationManager这个方法

requestLocationUpdates(@NonNull String provider, long minTime, float minDistance,
        @NonNull LocationListener listener, @nullable Looper looper)

另一个方向就是基于基站和周边WiFi列表的定位+ip,这里叫LBS,这里就取2种逻辑中最快回调回来的,比如GPS快,就拿GPS的经纬度,再使用高德SDK的api获取坐标对应的地点名字,如果GPS不可用,或者无回调,或者LBS定位回调比GPS快,拿到坐标后,也通过SDK获取坐标对应的地点名字。以上就是它的总体逻辑。不要问怎么知道的[doge].

通过分析,某力和某叮都是用了高德SDK,那刚开始我直接修改系统GPS以为能成功,没想到没效果,而且requestLocationUpdates是没有被调用的,那只能说它用的是LBS方式。

打开高德SDKdemo,发现了一个叫H5辅助定位,进入看看,且试用了一下。

原来还能这样啊。

再结合这个大佬的分析https://www.52pojie.cn/thread-1709943-1-1.html,定位重要代码是在SDKWebViewFragment中,大概能确定某力只用LBS方式。改系统的GPS数据是没办法的。

既然不吃系统数据,那就开始分析最新版本的情况吧,上面吾爱大佬的破解方式已经不适用最新版本了,他的文章也没有支出是那个版本,没有给出样本。所以能参考的就只有SDKWebViewFragment中获取定位的方法,也就是web和原生native沟通的方法:

public BaseSDKResult a(LocationGetRequest locationGetRequest, com.delicloud.app.jsbridge.main.c cVar)
开始分析定位重要位置

某力版本android:versionName="2.5.9"

样本地址:https://wwsk.lanzouy.com/iikQR0judryj,MD5:76f852dc4108e05cdb6f105df26c32a5

反编译工具:jadx

我们把样本拖进去jadx中,搜索SDKWebViewFragment,找到之后打开它。

根据上面吾爱大佬的文章,参考是否还存在这个方法,根据开发经验,一般都不会乱改web和原生通信方法。

继续搜索BaseSDKResult a(LocationGetRequest locationGetRequest找到获取定位的方法。

我们是幸运的,方法还在,而且逻辑也不复杂。我们来大体分析下整个方法的代码吧。

我希望你有Android应用层开发经验,就算混淆的代码,也能看懂大概。不然瞎猜代码是很痛苦的,且会走弯路。

a方法是web和原生通信的方法,返回了一个BaseSDKResult对象。

    @Override // com.delicloud.app.jsbridge.b
    public BaseSDKResult a(LocationGetRequest locationGetRequest, com.delicloud.app.jsbridge.main.c cVar) {
        //第一个判断①gd,通过传入context获取了系统服务LocationManager,
        //然后isProviderEnabled(GeocodeSearch.GPS)翻译:GPS服务能用吗,
        //locationManager.isProviderEnabled("network")网络定位能用吗,
        //如果其中一个可以用,那么就进入里面代码块,如果都不能,就进入②toast一个提示语。
        //显然,这个APP在进入首页的时候会问你要定位权限,你一定要给,有了定位
        //这里的判断就一定会成立。
        if (com.delicloud.app.tools.utils.i.gd(this.mContentActivity)) {
            //③n方法代码判断
            //这里就是再次检查权限,如果没有弹窗提示要权限,
            //如果没有权限,就没有下文了,我们会给APP权限的,这里判断就可以默认为true了
            if (com.delicloud.app.tools.utils.m.n(this)) {
                      //④be方法,从本地sp文件中根据某个key取出数据,然后强转为AddressModel对象。
                AddressModel addressModel = (AddressModel) dl.a.be(this.mContentActivity, com.delicloud.app.commom.b.bBz);
                //请求的时候如果需要缓存,且缓存的addressModel不是空的
                      //且当前时间和缓存的时间小于10000ms(10秒),就使用缓存。
                //经过我的动态调试,addressModel是null,也就是这里的判断不成立。
                      //⑤,看我给出的动态调试代码。
                if (locationGetRequest.isCache() && addressModel != null && System.currentTimeMillis() - addressModel.getCache_time() <= 10000) {
                    LocationGetResult locationGetResult = new LocationGetResult();
                    locationGetResult.setData(new LocationGetResult.LocationGetData(addressModel.getLatitude(), addressModel.getLongitude(), addressModel.getName(), addressModel.getAddress()));
                    Log.e("cache", com.delicloud.app.http.utils.c.aq(locationGetResult));
                    dl.a.a(this.mContentActivity, com.delicloud.app.commom.b.bBz, null);
                          //这里如果存在缓存,就直接返回数据,不需要再去申请定位
                    return locationGetResult;
                }
                //取缓存不成功,就是申请获取位置了。
                final boolean[] zArr = {true};
                //看到这里,做开发的小伙伴就秒懂了。
                      //rxJava,创建订阅,子线程走某事,成功后切换主线下拿到结果。
                //也就是⑥位置是结果,我们关注结果就行了。
                Observable.create(new ObservableOnSubscribe<Long>() { // from class: com.delicloud.app.jsbridge.ui.fragment.SDKWebViewFragment.7
                    @Override // io.reactivex.ObservableOnSubscribe
                    public void subscribe(ObservableEmitter<Long> observableEmitter) throws Exception {
                        if (SDKWebViewFragment.this.bLI == null) {
                            return;
                        }
                        SDKWebViewFragment.this.bLI.a(new a.InterfaceC0166a() { // from class: com.delicloud.app.jsbridge.ui.fragment.SDKWebViewFragment.7.1
                            @Override // com.delicloud.app.tools.utils.a.InterfaceC0166a
                            public void a(double d2, double d3, String str, String str2, String str3, String str4) {
                                zArr[0] = false;
                                LocationGetResult locationGetResult2 = new LocationGetResult();
                                locationGetResult2.setData(new LocationGetResult.LocationGetData(Double.valueOf(d2), Double.valueOf(d3), str, str2));
                                Log.i(SocializeConstants.KEY_LOCATION, com.delicloud.app.http.utils.c.aq(locationGetResult2));
                                SDKWebViewFragment.this.a(com.delicloud.app.jsbridge.b.chq, locationGetResult2);
                            }

                            @Override // com.delicloud.app.tools.utils.a.InterfaceC0166a
                            public void ZP() {
                                zArr[0] = false;
                                SDKWebViewFragment.this.a(com.delicloud.app.jsbridge.b.chq, new BaseSDKResult(JsSDKResultCode.GET_LOCATION_RESULT_FAIL));
                            }
                        });
                    }
                }).timeout(60L, TimeUnit.SECONDS).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Long>() { // from class: com.delicloud.app.jsbridge.ui.fragment.SDKWebViewFragment.6
                    @Override // io.reactivex.Observer
                    public void onComplete() {
                    }

                    @Override // io.reactivex.Observer
                    public void onSubscribe(Disposable disposable) {
                    }

                    @Override // io.reactivex.Observer
                    /* renamed from: e */
                    //⑥这里是定位返回成功后
                    public void onNext(Long l2) {
                               //看到这个判断我觉得很奇怪,为什么要这样呢,到底是否成立呢。
                              //看到申请定位前有那么一句final boolean[] zArr = {true};
                              //这又是何苦,上面标记了true,那判断就一定成立。
                        //最后我们调用了a方法。文章往下拉,看a的实现。⑥
                        if (zArr[0]) {
                            SDKWebViewFragment.this.a(com.delicloud.app.jsbridge.b.chq, new BaseSDKResult(JsSDKResultCode.GET_LOCATION_RESULT_FAIL));
                        }
                    }

                    @Override // io.reactivex.Observer
                    //这里是rxJava流程遇到错误回调
                    public void onError(Throwable th) {
                        if (zArr[0]) {
                            SDKWebViewFragment.this.a(com.delicloud.app.jsbridge.b.chq, new BaseSDKResult(JsSDKResultCode.GET_LOCATION_RESULT_FAIL));
                        }
                    }
                });
            }
            return null;
        }
        //如果GPS和定位不能用,就toast
        es.dmoral.toasty.b.bQ(this.mContentActivity, "当前系统定位开关未开启,无法定位").show();
        return new BaseSDKResult(JsSDKResultCode.IBEACON_NEED_LOCATION_PERMISSION);
    }

①:gd这个判断,做了什么呢,进去看看。

public class i {
    //获取定位服务,判断是否可用,如果我们授权了肯定可以用
    public static boolean gd(Context context) {
        LocationManager locationManager = (LocationManager) context.getSystemService(SocializeConstants.KEY_LOCATION);
        return locationManager.isProviderEnabled(GeocodeSearch.GPS) || locationManager.isProviderEnabled("network");
    }
}

③再次检查定位权限

    public static boolean n(final Fragment fragment) {
        if (c(fragment.getContext(), cxo)) {
            return true;
        }
        if (com.delicloud.app.commom.b.bAc) {
            return false;
        }
        com.delicloud.app.commom.b.bAc = true;
        com.delicloud.app.deiui.feedback.dialog.b.bVs.d(fragment.getActivity(), "得力e+申请访问精准定位权限", "用于极速打卡、考勤签到打卡、天气服务等功能。拒绝或取消授权不影响其他服务", "去开启", "取消", true, new b.a() { // from class: com.delicloud.app.tools.utils.m.6
            @Override // com.delicloud.app.deiui.feedback.dialog.b.a
            public void Za() {
                es.dmoral.toasty.b.bQ(Fragment.this.getActivity(), "权限拒绝后,将无法使用该功能").show();
            }

            @Override // com.delicloud.app.deiui.feedback.dialog.b.a
            public void Zb() {
                m.a(Fragment.this, m.cxo, 12);
            }
        }).show(fragment.getChildFragmentManager(), "权限申请");
        return false;
    }

④根据一个key字符串,获取本地储存的数据,然后转Java bean对象。

    public static <T extends Serializable> T be(Context context, String str) {
        try {
            return (T) bh(context, str);
        } catch (Exception e2) {
            e2.printStackTrace();
            return null;
        }
    }
        private static Object bh(Context context, String str) throws IOException, ClassNotFoundException {
        //从sp中取出数据
        String string = getString(context, str);
        if (TextUtils.isEmpty(string)) {
            return null;
        }
        //这里经过base64解码,也就是我们可以根据str这个key去sp中找到里面的数据
        //然后base64解码就可以看到存储内容了,有兴趣可以hook得到str,看看sp的数据哦
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.decode(string.getBytes(), 0));
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        Object readObject = objectInputStream.readObject();
        byteArrayInputStream.close();
        objectInputStream.close();
        return readObject;
    }

⑤AddressModel addressModel = (AddressModel) dl.a.be(this.mContentActivity, com.delicloud.app.commom.b.bBz);中be方法frida hook代码,这里也可以用Xposed的hook,看你自己会那个。

let a = Java.use("dl.a");
a["be"].implementation = function (context, str) {
    console.log('be is called' + ', ' + 'context: ' + context + ', ' + 'str: ' + str);
    let ret = this.be(context, str);
    console.log('be ret value is ' + ret);
    return ret;
};

经过我的调试,返回值是null,也就是没缓存,取缓存条件不成立,继续往下走。

⑥onNext流程调用了a

    public void a(String str, BaseSDKResult baseSDKResult) {
        //这里可以忽略,这东西不是null,原因,fragment创建的时候,获取了webview组件
              //ckD就是BridgeWebView实例,不可能,
              //这里判断是防止fragment退出之后清理了BridgeWebView,rxJava还继续回调引发
              //空指针异常,要判断下,正常情况下可以理解往下走
              //以下就是ckD赋值代码
              //public View onCreateView(LayoutInflater layoutInflater, \
        //@Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
        //View onCreateView = super.onCreateView(layoutInflater, viewGroup, bundle);
        //this.ckD = (BridgeWebView) onCreateView.findViewById(R.id.fragment_web_view);
        //return onCreateView;
        //}
        if (this.ckD == null) {
            return;
        }
        //这个开发真是个大聪明,居然直接把重要数据log打印出来,我真的谢谢你。
              //在开发的时候调试版本可以打log,辅助开发,到了生产环境一定要把log关掉
              //通常我们都是使用一个log工具做代{过}{滤}理,统一打印log处理
              //在发布最终版本的时候关闭log工具开关,同时通过混淆把Log.i等代码移除
              //大家可以过滤看看,这里打印了什么,破解的方案就在这里了
        Log.i("SDKWebViewFragment", "call back registerMethod=" + str + ",result=" + com.delicloud.app.http.utils.c.aq(baseSDKResult));
              //ckF是private HashMap<String, c> ckF = new HashMap<>();一个Map
              //这里的判断是:Map中是否包含了str这个(变量里面的值)键值对,如果有,就取出str对应的
              //value,这个value就是c对象,c对象是:
              //public interface c {
                    //    void nm(String str);
        //}
              //用人话说就是:取出str对应的c接口的具体实现对象然后调用nm方法
        if (this.ckF.containsKey(str)) {
                  //这里调用nm之前,aq方法处理了baseSDKResult(里面就是定位结果)⑦
            this.ckF.get(str).nm(com.delicloud.app.http.utils.c.aq(baseSDKResult));
        }
    }

⑦进去aq看看,里面做了什么呢。

    public static String aq(Object obj2) {
        gson = afV();
        Gson gson2 = gson;
              //gson是一个处理json的工具,这里使用了gson的api,把java bean转成json字符串
        if (gson2 != null) {
                  //把传递进来的java bean转json字符串
            return gson2.toJson(obj2);
        }
        return null;
    }

做开发的人基本上秒懂了,就是java bean转json吗,那么我们可以看看aq方法到底返回了什么。

上frida hook就行。

let c = Java.use("com.delicloud.app.http.utils.c");
c["aq"].implementation = function (obj2) {
    console.log('aq is called' + ', ' + 'obj2: ' + obj2);
    let ret = this.aq(obj2);
    console.log('aq ret value is ' + ret);
    return ret;
};
{
    "code":0,
    "data":{
        "address":"广东省广州市",
        "latitude":23.xxxx,
        "longitude":113.xxxx,
        "name":"xx大厦"
    },
    "method":"",
    "msg":"成功"
}

打印的数据居然是这样的。

聪明的你应该知道怎么做了吧?

frida hook改位置

经过动态调试之后,我发现了aq的数据居然包含了位置信息,我当时就想到了破解方案。

替换大法!

我去替换里面的数据,看看效果如何。

说干就干。

//com.delicloud.app.http.utils.c
Java.perform(function() {
    var com_delicloud_app_http_utils_c_clz = Java.use('com.delicloud.app.http.utils.c');

    var com_delicloud_app_http_utils_c_clz_method_aq_4105 = com_delicloud_app_http_utils_c_clz.aq.overload('java.lang.Object');
    com_delicloud_app_http_utils_c_clz_method_aq_4105.implementation = function(v0) {

        var ret = com_delicloud_app_http_utils_c_clz_method_aq_4105.call(com_delicloud_app_http_utils_c_clz, v0);

        console.log("json:", ret);
        //data":{"address
        console.log("判断地址::" + ret.indexOf("data\":\{\"address") != -1 );
              //代码中必须把json转义,不转义语法错误。
        var addr = "{\n" +
            "    \"code\":0,\n" +
            "    \"data\":{\n" +
            "        \"address\":\"目标地址名称,自己替换经纬度,后面给工具获取\",\n" +
            "        \"latitude\":23.2222,\n" +
            "        \"longitude\":113.22222,\n" +
            "        \"name\":\"某大厦\"\n" +
            "    },\n" +
            "    \"method\":\"\",\n" +
            "    \"msg\":\"成功\"\n" +
            "}";
        //如果返回的数据包含了这样的字符串,就直接替换我们目的地,其他
        //java bean转json的就正常返回就行。
        if (ret.indexOf("data\":\{\"address") != -1) {
            return addr;
        }
        return ret;
    };

});

执行脚本,下拉刷新看看效果。

我反手就点了,打卡成功。

看到这里,像做持久化hook的应该秒懂了。

我就不提供相关的成品了。

如何获取正确坐标

刚开始的时候我是去https://lbs.amap.com/tools/picker取坐标。

当我从这个网站取回来坐标后,并没有效果,显示的位置是目标坐标的4点钟方向再过一段距离。

后来问了其他有坐标处理经验的朋友阿肥,他告诉我地图坐标可能需要做标准换算。

public class JXMapUtil {

    private static final String PN_GAODE_MAP = "com.autonavi.minimap";   // 高德地图包名
    private static final String PN_BAIDU_MAP = "com.baidu.BaiduMap";     // 百度地图包名
    private static final String PN_TENCENT_MAP = "com.tencent.map";      // 腾讯地图包名

    private static final double a = 6378245.0;
    private static final double pi = 3.1415926535897932384626;
    private static final double ee = 0.00669342162296594323;

    //____________________________坐标转化_____________________________________________________

    /**
     * 转化为火星坐标系
     * @Param latitude
     * @param longitude
     * @return
     */
    public static double[] toGCJ02Point(double latitude, double longitude) {
        double[] dev = calDev(latitude, longitude);
        double retLat = latitude + dev[0];
        double retLon = longitude + dev[1];
        return new double[] { retLat, retLon };
    }

    /**
     * 火星坐标系 转化为 WGS84(国际坐标系
     * @param latitude
     * @param longitude
     * @return
     */
    public static double[] toWGS84Point(double latitude, double longitude) {
        double[] dev = calDev(latitude, longitude);
        double retLat = latitude - dev[0];
        double retLon = longitude - dev[1];
        dev = calDev(retLat, retLon);
        retLat = latitude - dev[0];
        retLon = longitude - dev[1];
        return new double[] { retLat, retLon };
    }

    private static double[] calDev(double wgLat, double wgLon) {
        if (isOutOfChina(wgLat, wgLon)) {
            return new double[] { 0, 0 };
        }
        double dLat = calLat(wgLon - 105.0, wgLat - 35.0);
        double dLon = calLon(wgLon - 105.0, wgLat - 35.0);
        double radLat = wgLat / 180.0 * pi;
        double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
        return new double[] { dLat, dLon };
    }

    private static boolean isOutOfChina(double lat, double lon) {
        if (lon < 72.004 || lon > 137.8347)
            return true;
        if (lat < 0.8293 || lat > 55.8271)
            return true;
        return false;
    }
    private static double calLat(double x, double y) {
        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    private static double calLon(double x, double y) {
        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
        return ret;
    }

}

也就是你从网站拾取的坐标需要转wgs84坐标

double[] wgs84Point = JXMapUtil.toWGS84Point(latitude, longitude);

这样的坐标喂给高德腾讯百度相关定位就OK了。

我这里提供一个apk方便坐标拾取。

如果使用,打卡拾取,点击你需要的位置,然后右下角点击√,返回的坐标就是wgs84标准的坐标了。

工具下载地址:

https://wwsk.lanzouy.com/iAFqH0jut6yj

MD5:d3c87fdd3d1982d29d485dc7baaab176

这样的坐标就是冇问题的啦!

总结

这个案例允许动态调试,没有root检查等等阻拦,是一个很好的实战例子。

在做这个逆向的事情的前提,我个人认为,你应该具备以下知识。

0:先学会开发!达到入门就OK。

1:Java基础扎实,有Android应用层开发的经验,能看懂SDK代码。

2:熟悉开发中使用到的第三方库,比如这里用到的gson,rxJava,高德定位SDK。

3:熟练使用jadx,apktool,AndroidKiller,Frida,Xposed等工具。

题外话,准备好AOSP系统代码,能修改系统且刷入手机,方便定位某些系统api,甚至定制接口。

夸张一点说十行代码搞定某定打卡(已实现了https://www.bilibili.com/video/BV1aK411Z7oQ)(这只是其中一个案例)

这一篇是通过hook不修改apk做的打卡,下一篇是改apk,直接插入需要的经纬度直接打卡。

个人博客地址:http://www.debuglive.cn/article/1059234217549889536

我现在工作是搞Android TV launcher开发的,偶尔也会做点盒子,手机的业务,算是有一点点开发经验。

在熟悉开发的前提下,去逆向会顺利很多 。

--来自业余逆向菜鸡的总结。

免费评分

参与人数 36威望 +2 吾爱币 +133 热心值 +30 收起 理由
刘小志 + 1 + 1 写的很详细,很赞
Lonerwcq + 1 + 1 谢谢@Thanks!
ttiatdi + 1 用心讨论,共获提升!
chen2008 + 1 我很赞同!
TheKillerv + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Golive180 + 1 谢谢@Thanks!
bravin + 1 + 1 热心回复!
18611429192 + 1 谢谢@Thanks!
zlnxlzht + 1 + 1 我很赞同!
luozchao + 1 用心讨论,共获提升!
z450041747 + 1 + 1 我很赞同!
sob13600 + 1 + 1 谢谢@Thanks!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
hbzjhg + 1 谢谢@Thanks!
AbCherry90 + 1 + 1 我很赞同!
TonyHo + 1 用心讨论,共获提升!
Quincy379 + 1 + 1 用心讨论,共获提升!
shamemist + 1 + 1 谢谢@Thanks!
cjw666 + 1 + 1 用心讨论,共获提升!
qtfreet00 + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
SVIP9大会员 + 1 + 1 牛的!膜拜!
shalj + 1 + 1 热心回复!
crizquan + 1 + 1 谢谢@Thanks!
luxingyu329 + 1 + 1 虽然我不懂,但给你点个赞,相信有一天我会懂的,正在看java
1MajorTom1 + 1 我很赞同!
kuiur0810 + 1 + 1 我很赞同!
allspark + 1 + 1 用心讨论,共获提升!
杨辣子 + 1 + 1 我很赞同!
Sionis + 1 + 1 谢谢@Thanks!
Moical + 1 + 1 谢谢@Thanks!
Joydawn + 1 + 1 我很赞同!
Tonyha7 + 1 + 1 谢谢@Thanks!
52菜鸟 + 1 + 1 虽然看不懂 还是要支持一下
yue52ei + 1 谢谢@Thanks!
左良渊上的骏马 + 1 + 1 我很赞同!
笙若 + 1 + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
luxingyu329 发表于 2023-1-3 15:42
我觉得研究这个一定要有一段路要走,支持楼主
推荐
 楼主| debug_cat 发表于 2023-1-5 15:15 |楼主
林伊轩 发表于 2023-1-5 15:08
这...太牛了.
修改aosp代码刷入到自己手机就很有难度吧

整体来说是琐碎的事情很多,繁琐,其中一步错误,后面就卡主。
总体来说,就是创建Ubuntu300gb空间,下载手机对应有驱动的版本的源码分支,导入ubt中,配置各种需要的编译lib,然后开始编译,过程中遇到错误就解决,最后手机解开bl锁,Google亲儿子的手机和AOSP有对应关系,只要你驱动下对,刷机是不会出问题的。这样刷的系统才是真的原生系统,原生到代码都是你可以一行一行修改的。这是我的公众号地址https://mp.weixin.qq.com/mp/appm ... d=1#wechat_redirect里面有我编译系统刷机改机的笔记

免费评分

参与人数 2吾爱币 +4 热心值 +2 收起 理由
Quincy379 + 1 + 1 谢谢@Thanks!
林伊轩 + 3 + 1 谢谢@Thanks!

查看全部评分

推荐
 楼主| debug_cat 发表于 2023-1-2 12:40 |楼主
推荐
 楼主| debug_cat 发表于 2023-1-2 12:34 |楼主

捕获一枚大佬~
4#
影子恋人 发表于 2023-1-2 12:29
向大佬低头
5#
a2415868 发表于 2023-1-2 12:36
认真看完了,感觉学到了很多!
6#
WLK970606 发表于 2023-1-2 13:33
卧槽楼主,你确定不是和我一个公司的?我们已经也是钉钉,上周五换了得力e+..............
7#
影子恋人 发表于 2023-1-2 13:47

谁是大佬
8#
 楼主| debug_cat 发表于 2023-1-2 13:52 |楼主
WLK970606 发表于 2023-1-2 13:33
卧槽楼主,你确定不是和我一个公司的?我们已经也是钉钉,上周五换了得力e+..............

不会吧不会吧,这么巧吗,我在越秀区上班哦
9#
matocool 发表于 2023-1-2 13:57
向大佬低头
10#
52pojie44521 发表于 2023-1-2 13:58
给力啊,大神
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 10:09

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表