frida_hook_libart(简单解释)

news2024/11/28 21:42:24

一:直接取代码


//frida -U -f com.xingin.xhs -l hook_art.js -o xhsart.log
//frida -U -f com.tencent.mobileqq -l hook_art.js -o qqart.log

const STD_STRING_SIZE = 3 * Process.pointerSize;
class StdString {
    constructor() {
        this.handle = Memory.alloc(STD_STRING_SIZE);
    }

    dispose() {
        const [data, isTiny] = this._getData();
        if (!isTiny) {
            Java.api.$delete(data);
        }
    }

    disposeToString() {
        const result = this.toString();
        this.dispose();
        return result;
    }

    toString() {
        const [data] = this._getData();
        return data.readUtf8String();
    }

    _getData() {
        const str = this.handle;
        const isTiny = (str.readU8() & 1) === 0;
        const data = isTiny ? str.add(1) : str.add(2 * Process.pointerSize).readPointer();
        return [data, isTiny];
    }
}

function prettyMethod(method_id, withSignature) {
    const result = new StdString();
    Java.api['art::ArtMethod::PrettyMethod'](result, method_id, withSignature ? 1 : 0);
    return result.disposeToString();
}

/*
GetFieldID is at  0xe39b87c5 _ZN3art3JNI10GetFieldIDEP7_JNIEnvP7_jclassPKcS6_
GetMethodID is at  0xe39a1a19 _ZN3art3JNI11GetMethodIDEP7_JNIEnvP7_jclassPKcS6_
NewStringUTF is at  0xe39cff25 _ZN3art3JNI12NewStringUTFEP7_JNIEnvPKc
RegisterNatives is at  0xe39e08fd _ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi
GetStaticFieldID is at  0xe39c9635 _ZN3art3JNI16GetStaticFieldIDEP7_JNIEnvP7_jclassPKcS6_
GetStaticMethodID is at  0xe39be0ed _ZN3art3JNI17GetStaticMethodIDEP7_JNIEnvP7_jclassPKcS6_
GetStringUTFChars is at  0xe39d06e5 _ZN3art3JNI17GetStringUTFCharsEP7_JNIEnvP8_jstringPh
FindClass is at  0xe399ae5d _ZN3art3JNI9FindClassEP7_JNIEnvPKc
*/

function hook_libart() {
    var symbols = Module.enumerateSymbolsSync("libart.so");
    var addrGetStringUTFChars = null;
    var addrNewStringUTF = null;
    var addrFindClass = null;
    var addrGetMethodID = null;
    var addrGetStaticMethodID = null;
    var addrGetFieldID = null;
    var addrGetStaticFieldID = null;
    var addrRegisterNatives = null;
    var so_name = "lib";      //TODO 这里写需要过滤的so

    for (var i = 0; i < symbols.length; i++) {
        var symbol = symbols[i];
        if (symbol.name.indexOf("art") >= 0 &&
            symbol.name.indexOf("JNI") >= 0 &&
            symbol.name.indexOf("CheckJNI") < 0 &&
            symbol.name.indexOf("_ZN3art3JNIILb0") >= 0
        ) {
            if (symbol.name.indexOf("GetStringUTFChars") >= 0) {
                addrGetStringUTFChars = symbol.address;
                console.log("GetStringUTFChars is at ", symbol.address, symbol.name);
            } else if (symbol.name.indexOf("NewStringUTF") >= 0) {
                addrNewStringUTF = symbol.address;
                console.log("NewStringUTF is at ", symbol.address, symbol.name);
            } else if (symbol.name.indexOf("FindClass") >= 0) {
                addrFindClass = symbol.address;
                console.log("FindClass is at ", symbol.address, symbol.name);
            } else if (symbol.name.indexOf("GetMethodID") >= 0) {
                addrGetMethodID = symbol.address;
                console.log("GetMethodID is at ", symbol.address, symbol.name);
            } else if (symbol.name.indexOf("GetStaticMethodID") >= 0) {
                addrGetStaticMethodID = symbol.address;
                console.log("GetStaticMethodID is at ", symbol.address, symbol.name);
            } else if (symbol.name.indexOf("GetFieldID") >= 0) {
                addrGetFieldID = symbol.address;
                console.log("GetFieldID is at ", symbol.address, symbol.name);
            } else if (symbol.name.indexOf("GetStaticFieldID") >= 0) {
                addrGetStaticFieldID = symbol.address;
                console.log("GetStaticFieldID is at ", symbol.address, symbol.name);
            } else if (symbol.name.indexOf("RegisterNatives") >= 0) {
                addrRegisterNatives = symbol.address;
                console.log("RegisterNatives is at ", symbol.address, symbol.name);
            } else if (symbol.name.indexOf("CallStatic") >= 0) {
                console.log("CallStatic is at ", symbol.address, symbol.name);
                Interceptor.attach(symbol.address, {
                    onEnter: function (args) {
                        var module = Process.findModuleByAddress(this.returnAddress);
                        if (module != null && module.name.indexOf(so_name) == 0) {
                            var java_class = args[1];
                            var mid = args[2];
                            var class_name = Java.vm.tryGetEnv().getClassName(java_class);
                            if (class_name.indexOf("java.") == -1 && class_name.indexOf("android.") == -1) {
                                var method_name = prettyMethod(mid, 1);
                                console.log("<>CallStatic:", DebugSymbol.fromAddress(this.returnAddress), class_name, method_name);
                            }
                        }
                    },
                    onLeave: function (retval) { }
                });
            } else if (symbol.name.indexOf("CallNonvirtual") >= 0) {
                console.log("CallNonvirtual is at ", symbol.address, symbol.name);
                Interceptor.attach(symbol.address, {
                    onEnter: function (args) {
                        var module = Process.findModuleByAddress(this.returnAddress);
                        if (module != null && module.name.indexOf(so_name) == 0) {
                            var jobject = args[1];
                            var jclass = args[2];
                            var jmethodID = args[3];
                            var obj_class_name = Java.vm.tryGetEnv().getObjectClassName(jobject);
                            var class_name = Java.vm.tryGetEnv().getClassName(jclass);
                            if (class_name.indexOf("java.") == -1 && class_name.indexOf("android.") == -1) {
                                var method_name = prettyMethod(jmethodID, 1);
                                console.log("<>CallNonvirtual:", DebugSymbol.fromAddress(this.returnAddress), class_name, obj_class_name, method_name);
                            }
                        }
                    },
                    onLeave: function (retval) { }
                });
            } else if (symbol.name.indexOf("Call") >= 0 && symbol.name.indexOf("Method") >= 0) {
                console.log("Call<>Method is at ", symbol.address, symbol.name);
                Interceptor.attach(symbol.address, {
                    onEnter: function (args) {
                        var module = Process.findModuleByAddress(this.returnAddress);
                        if (module != null && module.name.indexOf(so_name) == 0) {
                            var java_class = args[1];
                            var mid = args[2];
                            var class_name = Java.vm.tryGetEnv().getObjectClassName(java_class);
                            if (class_name.indexOf("java.") == -1 && class_name.indexOf("android.") == -1) {
                                var method_name = prettyMethod(mid, 1);
                                console.log("<>Call<>Method:", DebugSymbol.fromAddress(this.returnAddress), class_name, method_name);
                            }
                        }
                    },
                    onLeave: function (retval) { }
                });
            }
        }
    }

    if (addrGetStringUTFChars != null) {
        Interceptor.attach(addrGetStringUTFChars, {
            onEnter: function (args) {
            },
            onLeave: function (retval) {
                if (retval != null) {
                    var module = Process.findModuleByAddress(this.returnAddress);
                    if (module != null && module.name.indexOf(so_name) == 0) {
                        var bytes = Memory.readCString(retval);
                        console.log("[GetStringUTFChars] result:" + bytes, DebugSymbol.fromAddress(this.returnAddress));
                    }
                }
            }
        });
    }
    if (addrNewStringUTF != null) {
        Interceptor.attach(addrNewStringUTF, {
            onEnter: function (args) {
                if (args[1] != null) {
                    var module = Process.findModuleByAddress(this.returnAddress);
                    if (module != null && module.name.indexOf(so_name) == 0) {
                        var string = Memory.readCString(args[1]);
                        console.log("[NewStringUTF] bytes:" + string, DebugSymbol.fromAddress(this.returnAddress));
                    }

                }
            },
            onLeave: function (retval) { }
        });
    }

    if (addrFindClass != null) {
        Interceptor.attach(addrFindClass, {
            onEnter: function (args) {
                if (args[1] != null) {
                    var module = Process.findModuleByAddress(this.returnAddress);
                    if (module != null && module.name.indexOf(so_name) == 0) {
                        var name = Memory.readCString(args[1]);
                        console.log("[FindClass] name:" + name, DebugSymbol.fromAddress(this.returnAddress));
                    }
                }
            },
            onLeave: function (retval) { }
        });
    }
    if (addrGetMethodID != null) {
        Interceptor.attach(addrGetMethodID, {
            onEnter: function (args) {
                if (args[2] != null) {
                    var clazz = args[1];
                    var class_name = Java.vm.tryGetEnv().getClassName(clazz);
                    var module = Process.findModuleByAddress(this.returnAddress);
                    if (module != null && module.name.indexOf(so_name) == 0) {
                        var name = Memory.readCString(args[2]);
                        if (args[3] != null) {
                            var sig = Memory.readCString(args[3]);
                            console.log("[GetMethodID] class_name:" + class_name + " name:" + name + ", sig:" + sig, DebugSymbol.fromAddress(this.returnAddress));
                        } else {
                            console.log("[GetMethodID] class_name:" + class_name + " name:" + name, DebugSymbol.fromAddress(this.returnAddress));
                        }
                    }
                }
            },
            onLeave: function (retval) { }
        });
    }
    if (addrGetStaticMethodID != null) {
        Interceptor.attach(addrGetStaticMethodID, {
            onEnter: function (args) {
                if (args[2] != null) {
                    var clazz = args[1];
                    var class_name = Java.vm.tryGetEnv().getClassName(clazz);
                    var module = Process.findModuleByAddress(this.returnAddress);
                    if (module != null && module.name.indexOf(so_name) == 0) {
                        var name = Memory.readCString(args[2]);
                        if (args[3] != null) {
                            var sig = Memory.readCString(args[3]);
                            console.log("[GetStaticMethodID] class_name:" + class_name + " name:" + name + ", sig:" + sig, DebugSymbol.fromAddress(this.returnAddress));
                        } else {
                            console.log("[GetStaticMethodID] class_name:" + class_name + " name:" + name, DebugSymbol.fromAddress(this.returnAddress));
                        }
                    }
                }
            },
            onLeave: function (retval) { }
        });
    }
    if (addrGetFieldID != null) {
        Interceptor.attach(addrGetFieldID, {
            onEnter: function (args) {
                if (args[2] != null) {
                    var module = Process.findModuleByAddress(this.returnAddress);
                    if (module != null && module.name.indexOf(so_name) == 0) {
                        var name = Memory.readCString(args[2]);
                        if (args[3] != null) {
                            var sig = Memory.readCString(args[3]);
                            console.log("[GetFieldID] name:" + name + ", sig:" + sig, DebugSymbol.fromAddress(this.returnAddress));
                        } else {
                            console.log("[GetFieldID] name:" + name, DebugSymbol.fromAddress(this.returnAddress));
                        }
                    }
                }
            },
            onLeave: function (retval) { }
        });
    }
    if (addrGetStaticFieldID != null) {
        Interceptor.attach(addrGetStaticFieldID, {
            onEnter: function (args) {
                if (args[2] != null) {
                    var module = Process.findModuleByAddress(this.returnAddress);
                    if (module != null && module.name.indexOf(so_name) == 0) {
                        var name = Memory.readCString(args[2]);
                        if (args[3] != null) {
                            var sig = Memory.readCString(args[3]);
                            console.log("[GetStaticFieldID] name:" + name + ", sig:" + sig, DebugSymbol.fromAddress(this.returnAddress));
                        } else {
                            console.log("[GetStaticFieldID] name:" + name, DebugSymbol.fromAddress(this.returnAddress));
                        }
                    }
                }
            },
            onLeave: function (retval) { }
        });
    }

    if (addrRegisterNatives != null) {
        Interceptor.attach(addrRegisterNatives, {
            onEnter: function (args) {
                console.log("[RegisterNatives] method_count:", args[3], DebugSymbol.fromAddress(this.returnAddress));
                var env = args[0];
                var java_class = args[1];
                var class_name = Java.vm.tryGetEnv().getClassName(java_class);

                var methods_ptr = ptr(args[2]);

                var method_count = parseInt(args[3]);
                for (var i = 0; i < method_count; i++) {
                    var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3));
                    var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize));
                    var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2));

                    var name = Memory.readCString(name_ptr);
                    var sig = Memory.readCString(sig_ptr);
                    var find_module = Process.findModuleByAddress(fnPtr_ptr);
                    console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base));

                }
            },
            onLeave: function (retval) { }
        });
    }
}

setImmediate(hook_libart);


二:代码解释

Frida 脚本旨在监控 Android 应用程序中 libart.so 库的 Java Native Interface (JNI) 调用。这个脚本通过拦截特定的 JNI 方法,捕获和记录它们的调用信息,帮助我们分析应用程序的行为。

1. 脚本结构

该脚本主要分为几个部分:

  • 常量和类定义: 定义了一些常量和 StdString 类,用于处理 C++ 字符串。
  • 辅助函数: prettyMethod 函数用于获取方法的可读名称。
  • hook_libart 函数: 该函数负责查找和拦截 libart.so 中的 JNI 方法。
  • 设置和执行: 最后调用 setImmediate 执行 hook_libart 函数。

2. 关键功能

2.1 StdString
class StdString {
    constructor() {
        this.handle = Memory.alloc(STD_STRING_SIZE);
    }

    dispose() {
        const [data, isTiny] = this._getData();
        if (!isTiny) {
            Java.api.$delete(data);
        }
    }

    disposeToString() {
        const result = this.toString();
        this.dispose();
        return result;
    }

    toString() {
        const [data] = this._getData();
        return data.readUtf8String();
    }

    _getData() {
        const str = this.handle;
        const isTiny = (str.readU8() & 1) === 0;
        const data = isTiny ? str.add(1) : str.add(2 * Process.pointerSize).readPointer();
        return [data, isTiny];
    }
}
  • 功能: 这个类用于处理 C++ 中的 std::string 对象,提供了字符串的读取和释放方法。
  • 内存管理: dispose 方法负责释放非小字符串的内存。
2.2 prettyMethod 函数
function prettyMethod(method_id, withSignature) {
    const result = new StdString();
    Java.api['art::ArtMethod::PrettyMethod'](result, method_id, withSignature ? 1 : 0);
    return result.disposeToString();
}
  • 功能: 该函数调用 art::ArtMethod::PrettyMethod 获取方法的可读名称,便于后续的日志记录。
2.3 hook_libart 函数

这是脚本的核心部分,负责查找和拦截多个 JNI 方法:

  • 符号查找: 使用 Module.enumerateSymbolsSync 获取 libart.so 中的符号,并根据名称过滤出相关的 JNI 方法。

  • 拦截 JNI 方法: 对于每个找到的 JNI 方法,通过 Interceptor.attach 进行拦截,记录方法调用的详细信息。

例如,拦截 GetStringUTFChars 方法:

if (addrGetStringUTFChars != null) {
    Interceptor.attach(addrGetStringUTFChars, {
        onEnter: function (args) {
        },
        onLeave: function (retval) {
            if (retval != null) {
                var module = Process.findModuleByAddress(this.returnAddress);
                if (module != null && module.name.indexOf(so_name) == 0) {
                    var bytes = Memory.readCString(retval);
                    console.log("[GetStringUTFChars] result:" + bytes, DebugSymbol.fromAddress(this.returnAddress));
                }
            }
        }
    });
}
  • 参数和返回值处理: 在 onEnteronLeave 中处理方法的参数和返回值,记录相关信息。

3. 监控的 JNI 方法

该脚本监控了多个 JNI 方法,包括:

  • GetFieldID
  • GetMethodID
  • GetStaticFieldID
  • GetStaticMethodID
  • RegisterNatives
  • NewStringUTF
  • GetStringUTFChars
  • FindClass
  • CallStatic
  • CallNonvirtual

这些方法的调用通常涉及 Java 和 C/C++ 之间的交互,监控这些方法可以帮助分析应用程序的行为,尤其是在涉及 JNI 的部分。

4. 使用方法

要使用这个脚本,需要:

  1. 安装 Frida: 确保您已经在设备上安装了 Frida Server,并且在我们的计算机上安装了 Frida 工具。

  2. 运行命令: 使用如下命令运行脚本:

    frida -U -f com.xingin.xhs -l hook_art.js -o xhsart.log
    frida -U -f com.tencent.mobileqq -l hook_art.js -o qqart.log
    

    这里 -U 表示连接到 USB 设备,-f 指定要启动的应用程序,-l 指定要加载的脚本,-o 指定输出日志文件。

5. 总结

这个 Frida 脚本是一个强大的工具,用于监控 Android 应用程序中的 JNI 调用。通过拦截 libart.so 中的关键方法,可以深入了解 Java 和本地代码之间的交互,帮助进行调试、分析和安全研究。脚本的灵活性和可扩展性使其能够适应多种分析需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2249327.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣 二叉树的层序遍历-102

二叉树的层序遍历-102 class Solution { public:vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> res; // 二维数组用来存储每层节点if (root nullptr)return res;queue<TreeNode*> q; // 队列用来进行层序遍历q.push(r…

鸿蒙学习使用本地真机运行应用/元服务 (开发篇)

文章目录 1、前提条件2、使用USB连接方式3、使用无线调试连接方式4、运行 1、前提条件 在Phone和Tablet中运行HarmonyOS应用/元服务的操作方法一致&#xff0c;可以采用USB连接方式或者无线调试的连接方式。两种连接方式是互斥的&#xff0c;只能使用一种&#xff0c;无法同时…

数据库导论

data 数据是数据库中存储的基本数据&#xff0c;描述事物的符号称为数据。 DB 数据库是长期存储在计算机内&#xff0c;有组织&#xff0c;可共享的大量数据的集合。数据库中的数据按照一定的数据模型组织&#xff0c;描述和存储&#xff0c;具有较小的冗余度&#xff0c;较…

数据结构 ——— 归并排序算法的实现

目录 归并排序的思想 归并排序算法的实现 归并排序的思想 将已经有序的子序列合并&#xff0c;得到完全有序的序列&#xff0c;即先使每个子序列有序后&#xff0c;再使子序列段间有序 若将两个有序表合并成一个有序表&#xff0c;称为二路归并 归并排序步骤示意图&#x…

【数据结构】【线性表】一文讲完队列(附C语言源码)

队列 队列的基本概念基本术语基本操作 队列的顺序实现顺序队列结构体的创建顺序队列的初始化顺序队列入队顺序队列出队顺序队列存在的问题分析循环队列代码汇总 队列的链式实现链式队列的创建链式队列初始化-不带头结点链式队列入队-不带头节点链式队列出队-不带头结点带头结点…

chrome允许http网站打开摄像头和麦克风

第一步 chrome://flags/#unsafely-treat-insecure-origin-as-secure 第二步 填入网址&#xff0c;点击启用 第三步 重启 Chrome&#xff1a;设置完成后&#xff0c;点击页面底部的 “Relaunch” 按钮&#xff0c;重新启动 Chrome 浏览器&#xff0c;使更改生效。

Spring Boot教程之十: 使用 Spring Boot 实现从数据库动态下拉列表

使用 Spring Boot 实现从数据库动态下拉列表 动态下拉列表&#xff08;或依赖下拉列表&#xff09;的概念令人兴奋&#xff0c;但编写起来却颇具挑战性。动态下拉列表意味着一个下拉列表中的值依赖于前一个下拉列表中选择的值。一个简单的例子是三个下拉框&#xff0c;分别显示…

DRM(数字权限管理技术)防截屏录屏----视频转hls流加密、web解密播放

提示&#xff1a;视频转hls流加密、web解密播放 需求&#xff1a;研究视频截屏时&#xff0c;播放器变黑&#xff0c;所以先研究的视频转hls流加密 文章目录 [TOC](文章目录) 前言一、工具ffmpeg、openssl二、后端nodeexpress三、web播放四、文档总结 前言 ‌HLS流媒体协议‌&a…

视觉经典神经网络与复现:深入解析与实践指南

目录 引言 经典视觉神经网络模型详解 1. LeNet-5&#xff1a;卷积神经网络的先驱 LeNet-5的关键特点&#xff1a; 2. AlexNet&#xff1a;深度学习的突破 AlexNet的关键特点&#xff1a; 3. VGGNet&#xff1a;深度与简洁的平衡 VGGNet的关键特点&#xff1a; 4. ResNe…

【算法day1】数组:双指针算法

题目引用 这里以 1、LeetCode704.二分查找 2、LeetCode27.移除元素 3、LeetCode977.有序数组的平方 这三道题举例来说明数组中双指针的妙用。 1、二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜…

华为云云连接+squid进行正向代理上网冲浪

1 概述 ‌Squid‌是一个高性能的代理缓存服务器&#xff0c;主要用于缓冲Internet数据。它支持多种协议&#xff0c;包括FTP、gopher、HTTPS和HTTP。Squid通过一个单独的、非模块化的、I/O驱动的进程来处理所有的客户端请求&#xff0c;这使得它在处理请求时具有较高的效率‌。…

2024年11月24日Github流行趋势

项目名称&#xff1a;FreeCAD 项目维护者&#xff1a;wwmayer, yorikvanhavre, berndhahnebach, chennes, WandererFan等项目介绍&#xff1a;FreeCAD是一个免费且开源的多平台3D参数化建模工具。项目star数&#xff1a;20,875项目fork数&#xff1a;4,117 项目名称&#xff1…

二叉树:堆的建立和应用

在建立堆之前&#xff0c;我们要知道什么是树和二叉树 树 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个结点组成的一个具有层次关系的集合&#xff0c;之所以把它叫做树&#xff0c;是因为它长得像一棵倒挂的树&#xff0c;也就是根在上面&…

使用 pycharm 新建不使用 python 虚拟环境( venv、conda )的工程

有时候我们发现一个好玩的 demo&#xff0c;想赶快在电脑上 pip install 一下跑起来&#xff0c;发现因为 python 的 venv、conda 环境还挺费劲的&#xff0c;因为随着时间的发展&#xff0c;之前记得很清楚的 venv、conda 的用法&#xff0c;不经常使用&#xff0c;半天跑不起…

(详细文档!)java swing学生信息管理系统 +mysql

第一章&#xff1a;系统功能分析 1.1、系统简介与开发背景 学生信息管理系统是在信息化时代&#xff0c;特别是在教育领域中产生的。随着学校规模的不断扩大和信息化技术的不断发展&#xff0c;传统的纸质档案管理方式已经无法满足学校对学生信息管理的需求&#xff0c;因此需…

租辆酷车小程序开发(二)—— 接入微服务GRPC

vscode中golang的配置 设置依赖管理 go env -w GO111MODULEon go env -w GOPROXYhttps://goproxy.cn,direct GO111MODULEauto 在$GOPATH/src 外面且根目录有go.mod 文件时&#xff0c;开启模块支持 GO111MODULEoff 无模块支持&#xff0c;go会从GOPATH 和 vendor 文件夹寻找包…

集合卡尔曼滤波(EnKF)的三维滤波(模拟平面定位)例程,带逐行注释

这段 MATLAB 代码实现了一个三维动态系统的集合卡尔曼滤波&#xff08;Ensemble Kalman Filter, EnKF&#xff09;示例。代码的主要目的是通过模拟真实状态和测量值&#xff0c;使用 EnKF 方法对动态系统状态进行估计。 文章目录 参数设置初始化真实状态定义状态转移和测量矩阵…

鸿蒙千帆启新程,共绘数字生态蓝图

华为的鸿蒙千帆起计划&#xff1a;共筑数字未来&#xff0c;学习华为创新之路 在当今全球科技竞争日益激烈的背景下&#xff0c;华为作为中国科技企业的代表&#xff0c;正通过其自主创新的鸿蒙系统&#xff0c;引领一场移动应用生态的变革。鸿蒙千帆起计划&#xff0c;作为华…

python控制鼠标,键盘,adb

python控制鼠标&#xff0c;键盘&#xff0c;adb 听说某系因为奖学金互相举报&#xff0c;好像拿不到要命一样。不禁想到几天前老墨偷走丁胖子的狗&#xff0c;被丁胖子逮到。他面对警察的问询面不改色坚持自我&#xff0c;反而是怒气冲冲的丁胖子被警察认为是偷狗贼。我觉得这…

yum安装升级指定版本软件

与apt相同&#xff0c;yum也可以指定升级软件版本。 这里以升级gitlab-ce为例。 获得当前可用版本命令如下&#xff1a; yum list gitlab-ce --showduplicates 查看当前gitlab版本。在/var/opt/gitlab/gitlab-rails/查看VERSION文件内容&#xff1a; cat VERSION 当前版本…