Frida-Hook-Java层操作大全

news2024/11/25 14:35:46

附件下载

https://github.com/DERE-ad2001/Frida-Labs

前期准备

  1. 使用 jadx 进行逆向工程的基础知识。
  2. 应具备理解 Java 代码的能力。
  3. 具备编写小型 JavaScript 代码片段的能力。
  4. 熟悉 adb。
  5. 设备已 root。
  6. Frida环境配置

Hook(Hooking)简介

让我们从非常基础的知识开始。

什么是钩子?

Hook是指拦截和修改应用程序或Android系统中函数或方法行为的过程。例如,我们可以钩取我们应用程序中的一个方法,并通过插入我们自己的实现来改变其功能。

现在,让我们尝试在一个应用程序中钩取一个方法。我们将使用JavaScript API 来完成这个任务,但值得注意的是,Frida也支持Python。

1、使用Hook修改被调用的方法的逻辑,返回值,传入参数

基本模板

首先让我提供给你一个模板,然后我们一步步来解释。

Java.perform(function() {

  var <class_reference> = Java.use("<package_name>.<class>");
  <class_reference>.<method_to_hook>.implementation = function(<args>) {

    /*
      我们自己的方法实现
    */

  }

})
  • Java.perform 是 Frida 中用于创建一个特殊上下文的函数,让你的脚本能够与 Android 应用程序中的 Java 代码进行交互。它就像是打开了一扇门,让你能够访问并操纵应用程序内部运行的 Java 代码。一旦进入这个上下文,你就可以执行诸如钩取方法或访问 Java 类等操作来控制或观察应用程序的行为。

  • var <class_reference> = Java.use("<package_name>.<class>");
    在这里,你声明一个变量 <class_reference> 来表示目标 Android 应用程序中的一个 Java 类。你使用 Java.use 函数指定要使用的类,该函数接受类名作为参数。<package_name> 表示 Android 应用程序的包名,<class> 表示你想要与之交互的类。
    <package_name>
    file

  • <class_reference>.<method_to_hook>.implementation = function(<args>) {}
    在所选的类内部,通过 <class_reference>.<method_to_hook> 符号访问你想要钩取的方法。这是你可以定义自己的逻辑以在钩取的方法被调用时执行的地方。<args> 表示传递给函数的参数。

例题Frida-Labs 0x1

通过Jadx分析Frida-labs 0x1

onCreate方法
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(C0570R.layout.activity_main);
        final EditText editText = (EditText) findViewById(C0570R.C0573id.editTextTextPassword);
        this.f103t1 = (TextView) findViewById(C0570R.C0573id.textview1);
        final int i = get_random();
        ((Button) findViewById(C0570R.C0573id.button)).setOnClickListener(new View.OnClickListener() { // from class: com.ad2001.frida0x1.MainActivity.1
            @Override // android.view.View.OnClickListener
            public void onClick(View view) {
                String obj = editText.getText().toString();
                if (TextUtils.isDigitsOnly(obj)) {
                    MainActivity.this.check(i, Integer.parseInt(obj));
                } else {
                    Toast.makeText(MainActivity.this.getApplicationContext(), "Enter a valid number !!", 1).show();
                }
            }
        });
    }

可以发现,在onCreate方法中,有一个监听事件,监听了button的点击,当按钮点击下去之后,程序首先判断输入是不是数字,是数字的话,就将其从string转化为int,再进入check中与i比较,因此我们需要检查check方法。

check方法
 void check(int i, int i2) {
        if ((i * 2) + 4 == i2) {
            Toast.makeText(getApplicationContext(), "Yey you guessed it right", 1).show();
            StringBuilder sb = new StringBuilder();
            for (int i3 = 0; i3 < 20; i3++) {
                char charAt = "AMDYV{WVWT_CJJF_0s1}".charAt(i3);
                if (charAt < 'a' || charAt > 'z') {
                    if (charAt >= 'A') {
                        if (charAt <= 'Z') {
                            charAt = (char) (charAt - 21);
                            if (charAt >= 'A') {
                            }
                            charAt = (char) (charAt + 26);
                        }
                    }
                    sb.append(charAt);
                } else {
                    charAt = (char) (charAt - 21);
                    if (charAt >= 'a') {
                        sb.append(charAt);
                    }
                    charAt = (char) (charAt + 26);
                    sb.append(charAt);
                }
            }
            this.f103t1.setText(sb.toString());
            return;
        }
        Toast.makeText(getApplicationContext(), "Try again", 1).show();
    }

本方法显而易见就是检查输入是否能够满足i*2 + 4 == i2,如果满足则将flag输出到f103t1所绑定的textView控件上,其中用于判断的i则来自get_random。

get_random
    int get_random() {
        return new Random().nextInt(100);
    }

显而易见,本方法就只是普通的返回一个随机数。

Hook begin!

对于本样例程序,我们有两种方法去解决,首先我们可以直接hook程序逻辑。更改随机产生的值为一个固定值。或者hook check方法更改check方法传入的参数

Hook get_random方法
实现代码
function hook(){
    var MainActivity = Java.use("com.ad2001.frida0x1.MainActivity");
    MainActivity.get_random.implementation = function (){
        return 0;
    }
}

function main(){
    Java.perform(function (){
        hook();
    })
}

setImmediate(main);

代码解释如下:

  1. 首先定义了一个名为hook的JavaScript函数,其中包含了对目标应用特定方法的hook逻辑。

    • hook函数通过Frida的Java API来获取目标应用中的MainActivity类。
    • 然后,它通过Java.use()方法获取了MainActivity类的引用,使得我们可以访问该类的方法。
    • 最后,hook函数将MainActivity类中的get_random方法进行了修改。它用自定义的实现替换了原有方法的实现,使得每次调用get_random方法时都返回固定值0。
  2. 接着定义了一个名为main的JavaScript函数,其中包含了Frida的Java.perform()方法,用于执行指定的hook逻辑。

  3. 最后,通过setImmediate()函数调用main函数,确保在Frida脚本启动后立即执行。

hook check方法

如果我们检查check函数的参数,第一个参数i表示随机数,而第二个参数i2对应于用户输入的数字。让我们使用Frida来捕获并转储这两个参数。

在处理具有参数的方法时,重要的是使用overload(arg_type)关键字指定预期的参数类型。此外,在钩入方法时确保包括这些指定的参数在你的实现中。在这里,我们的check()函数接受两个整数参数,所以我们可以这样指定:

a.check.overload(int, int).implementation = function(a, b) {

  ...

}

function hook2(){
    var MainActivity = Java.use("com.ad2001.frida0x1.MainActivity");
    MainActivity.check.overload('int','int').implementation = function (a,b){
        console.log("Origin i and i2 = ",a,b);
        return this.check(a,b);
    }
}

function main(){
    Java.perform(function (){
        hook2();
    })
}

setImmediate(main);

我们可以使用console.log查看传入的a与b是什么
file

this.check(a,b);中的a,b改为自己设定的值就可以了。
file

2、Hook调用静态的未被调用的方法

在之前讲到的Java.use Api中,如果我们指定的类中包含了静态的方法,则我们可以直接调用该方法。模板如下:

Java.perform(function (){
    var <class_reference> = Java.use("<package_name>.<class>");
    a.function(val);
})

例题Frida-labs 0x2

MainActivity类
package com.ad2001.frida0x2;

import android.os.Bundle;
import android.util.Base64;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/* loaded from: classes3.dex */
public class MainActivity extends AppCompatActivity {

    /* renamed from: t1 */
    static TextView f103t1;

    /* JADX INFO: Access modifiers changed from: protected */
    @Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(C0569R.layout.activity_main);
        f103t1 = (TextView) findViewById(C0569R.C0572id.textview);
    }

    public static void get_flag(int a) {
        if (a == 4919) {
            try {
                SecretKeySpec secretKeySpec = new SecretKeySpec("HILLBILLWILLBINN".getBytes(), "AES");
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                IvParameterSpec iv = new IvParameterSpec(new byte[16]);
                cipher.init(2, secretKeySpec, iv);
                byte[] decryptedBytes = cipher.doFinal(Base64.decode("q7mBQegjhpfIAr0OgfLvH0t/D0Xi0ieG0vd+8ZVW+b4=", 0));
                String decryptedText = new String(decryptedBytes);
                f103t1.setText(decryptedText);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

本用例程序就一个MainActivity类,类中存在一个未被使用的静态方法get_flag,在get_flag中比较了传入的参数,如果传入的参数为4919则解密flag,设置给txtView控件,那么根据之前给出的调用模板,我们hook代码如下:

Hook代码:
function hook(){
    var MainActivity = Java.use("com.ad2001.frida0x2.MainActivity");
    MainActivity.get_flag(4919);
}

function main(){
    Java.perform(function (){
        hook();
    })
}

setImmediate(main);

但是我们发现如果使用的是setIMMediate(main)的话我们使用
frida -U -f com.ad2001.frida0x2 -l .\Hook.js
可能会导致hook不上的情况。
file

解决方法1

我们事先启动Frida 0x2应用程序。然后使用如下命令注入我们的脚本
frida -U 'Frida 0x2' -l .\Hook.js
file
本方法与之前的方法不同之处是该方法是直接hook入我们后台正在启动的程序,而之前的方法是根据包名再启动一个程序。

解决方法2

当我们发现使用解决方法1能够成功hook的时候,就可以推断出,是由于我们启动main函数使用的是setImmediate(main),是立即启动可能会导致脚本注入的速度比程序启动的速度快。因此我们可以改用setTimeout(main,1000),也就是延迟1秒钟启动程序。
详情可见https://www.cnblogs.com/fsjohnhuang/p/4151595.html
file

3、更改类中的静态变量

类似于如下写法static int code = 0;
使用static 修饰的变量则为静态变量。我们可以用如下方法更改静态变量。

Java.perform(function (){

    var <class_reference> = Java.use("<package_name>.<class>");
    <class_reference>.<variable>.value = <value>;

})

例题 Frida-labs 0x3

MainActivity类
file
标记处我们可以发现,当Checker.code为512的时候点击按钮,程序则会解密并且将textView控件设置为Flag。

Hook代码

function hook(){
    var a = Java.use("com.ad2001.frida0x3.Checker");
    a.code.value = 512;
}

function main(){
    Java.perform(function (){
        hook();
    })
}
setImmediate(main);



4、调用非MainActivity,非静态方法

在JAVA代码中,如果创建了一个非静态的类,当我们需要使用这个类的时候需要new一个类的对象出来我们才能使用这个类的功能。类似代码如下:


Check ch = new Check();
String flag = ch.get_flag(1337);

那么在Java源码中需要new出来的实例,我们怎么使用Frida来实现呢?
模板如下:

Java.perform(function() {

  var <class_reference> = Java.use("<package_name>.<class>");
  var <class_instance> = <class_reference>.$new(); // Class Object
  <class_instance>.<method>(); // 调用方法

})

例题Frida-labs 0x4

MainActivity:

file
MainActivity中没有任何东西。

Checker

file
Checker中出现了get_flag方法,返回了flag。则我们使用之前的模板来Hook

Hook代码:
function hook(){
    console.log("Hook Success!");
    var Check = Java.use("com.ad2001.frida0x4.Check");
    var Check_obj = Check.$new();
    var String = Check_obj.get_flag(1337);
    console.log(String);
}

function main(){
    Java.perform(function (){
        hook();

    })
}

setImmediate(main);

file

5、调用MainActivity中的非静态方法

前面有提到过如果不是MainActivity中的方法我们使用.$new()可以创建一个实例。那么如果我们将这个使用到MainActivity会发生什么呢?

function hook(){
    var MainActivity = Java.use("com.ad2001.frida0x5");
    var MainActivity_obj = MainActivity.$new();
}

file
好吧,它崩溃了。那么这是什么原因呢?

直接使用Frida创建MainActivity或任何Android组件可能会很棘手,因为Android的生命周期和线程规则。Android组件,如Activity子类,依赖于应用程序上下文进行正确运行。在Frida中,您可能缺少必要的上下文。Android UI组件通常需要具有关联Looper的特定线程。如果涉及UI任务,请确保在具有活动Looper的主线程上执行。活动是较大的Android应用程序生命周期的一部分。创建MainActivity的实例可能需要应用处于特定状态,并且通过Frida管理整个生命周期可能并不直接。总之,为MainActivity创建实例并不是一个好主意。

那么这里的解决方案是什么呢?

当Android应用程序启动时,系统会创建MainActivity的一个实例(或AndroidManifest.xml文件中指定的启动器活动)。创建MainActivity实例是Android应用程序生命周期的一部分。因此,我们可以使用frida获取MainActivity的实例,然后调用flag()方法来获取我们的标志。

在现有实例上调用方法

在现有实例上调用方法可以很容易地通过Frida完成。为此,我们将使用两个API。

  • Java.performNow:用于在Java运行时环境中执行代码的函数。

  • Java.choose:在运行时枚举指定Java类(作为第一个参数提供)的实例。

让我展示一个模板给你。

Java.performNow(function() {
  Java.choose('<包名>.<类名>', {
    onMatch: function(instance) {
      // 待办事项
    },
    onComplete: function() {}
  });
});

这里有两个回调函数:

  • onMatch
    • onMatch回调函数在Java.choose操作期间找到指定类的每个实例时执行。
    • 这个回调函数接收当前实例作为它的参数。
    • 您可以在onMatch回调中定义自定义操作,以在每个实例上执行。
    • function(instance) {}instance参数表示目标类的每个匹配实例。您可以使用任何其他名称。
  • onComplete
    • onComplete回调在Java.choose操作完成后执行操作或清理任务。此块是可选的,如果您在搜索完成后不需要执行任何特定操作,则可以选择将其留空。

例题Frida-labs 0x5

MainActivity

file
可以发现其中flag方法是未被调用的方法,并且是解密密文将Flag输出到TextView控件上。

BeginHook!

现在我们知道如何使用Java.choose API,让我们开始编写我们的frida脚本。

  • 包名:com.ad2001.frida0x5
  • 类名:MainActivity
  • 函数名:flag
Java.performNow(function() {
  Java.choose('com.ad2001.frida0x5.MainActivity', {
    onMatch: function(instance) {
      // 待办事项
    },
    onComplete: function() {}
  });
});

让我们在成功找到MainActivity实例时包含一个console.log语句以打印一条消息。由于在枚举完成后我们没有任何特定的操作要执行,我们可以将onComplete块留空。

Java.performNow(function() {
  Java.choose('com.ad2001.frida0x5.MainActivity', {
    onMatch: function(instance) {
      console.log("找到实例");
    },
    onComplete: function() {}
  });
});

让我们启动Frida并注入我们的脚本。
file

Hook代码
function hook(){
    Java.choose('com.ad2001.frida0x5.MainActivity',{
        onMatch:function (MainActivity){
            MainActivity.flag(1337);
            console.log("Hook Success!");
        },onComplete:function (){

        }
    })
}

function main(){

    Java.perform(function (){
        hook();
    })
}

setImmediate(main);

file

6、MainActivity中非静态并且参数为非静态变量方法调用

例题Frida-labs 0x6

file
我们之前已经解决过类似的问题了。在这种情况下,我们有一个get_flag()方法,在应用程序中没有被调用。如果调用此方法,它将使用AES解密标志,并将标志设置在Textview中。如果我们检查get_flag方法,它只接受一个参数,这个参数是Checker类的一个实例。参数被命名为A,其类型是Checker

public void get_flag(Checker A) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
    // 方法体
}

在方法内部,它检查A.num1是否等于1234,以及A.num2是否等于4321。如果条件成立,该方法将继续使用AES解密加密字符串,并将解密后的结果设置在TextView中。因此,让我们检查一下Checker类。

file

在Checker类中,我们有两个变量。

  • num1
  • num2

num1应该等于1234num2应该等于4321,以满足if条件执行解密并设置标志的代码块。请记住,这个类也没有实例。

解决方案

这个问题很容易解决,因为我们之前已经在上一篇帖子中做过了,唯一的区别是get_flag方法的参数是Checker类的一个对象。我将总结解决这个问题的步骤如下:

  • 创建一个Checker类的实例。
  • num1设置为1234,num2设置为4321。
  • 获取MainActivity的实例。
  • 使用实例作为参数调用get_flag方法。

让我们开始编写我们的frida脚本。

首先让我们创建Checker类的实例。

var checker = Java.use("com.ad2001.frida0x6.Checker");
var checker_obj = checker.$new(); // 类对象

设置num1num2的值。

checker_obj.num1.value = 1234;
checker_obj.num2.value = 4321;

现在让我们获取MainActivity的实例。我们可以使用Java.performNowJava.chooseAPI。我们在之前的挑战中已经做过了。

Java.performNow(function() {
  Java.choose('com.ad2001.frida0x6.MainActivity', {
    onMatch: function(instance) {
      console.log("找到实例");

    },
    onComplete: function() {}
  });
})

让我们更新脚本,加入Checker类的实例。

Java.performNow(function() {
  Java.choose('com.ad2001.frida0x6.MainActivity', {
    onMatch: function(instance) {
      console.log("找到实例");

      var checker = Java.use("com.ad2001.frida0x6.Checker");
      var checker_obj  = checker.$new();  // 类对象
      checker_obj.num1.value = 1234;
      checker_obj.num2.value = 4321;

    },
    onComplete: function() {}
  });
});

现在唯一要做的是通过传递Checker类的实例来调用get_flag方法。

Java.performNow(function() {
  Java.choose('com.ad2001.frida0x6.MainActivity', {
    onMatch: function(instance) {
      console.log("找到实例");

      var checker = Java.use("com.ad2001.frida0x6.Checker");
      var checker_obj  = checker.$new();  // 类对象
      checker_obj.num1.value = 1234; // num1
      checker_obj.num2.value = 4321; // num2
      instance.get_flag(checker_obj); // 调用get_flag方法

    },
    onComplete: function() {}
  });
});

让我们启动frida并运行我们的脚本。

PS C:\Users\ajind> frida -U -f com.ad2001.frida0x6

file

当我们检查我们的手机时,TextView将显示标志。

7、Hook构造函数

如果在ARM64 设备上不工作请看issue:https://github.com/frida/frida/issues/1575

挂钩构造函数十分简单,与挂钩方法类似。让我为您提供一个模板。

Java.perform(function() {
  var <class_reference> = Java.use("<package_name>.<class>");
  <class_reference>.$init.implementation = function(<args>){

    /*

    */

  }
});

我们可以看到,为了挂钩构造函数,我们可以使用$init关键字。

例题Frida-labs 0x7

MainActivity

file
可以看到程序在使用flag方法判断之前,首先使用 Checker ch = new Checker(123, 321); 创建了一个Checker实例,则123 , 321 分别对应A.num1与 A.num2。
那么我们只需要钩住构造函数即可。

Hook代码
function hook(){
    var Checker = Java.use("com.ad2001.frida0x7.Checker");
    Checker.$init.implementation = function (a,b){
        console.log("Origin num",a,b);
        this.$init(600,600);
        console.log("Hook Success");
    }
}

function main(){
    Java.perform(function (){
        hook();
    })
}
setImmediate(main);

file

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

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

相关文章

IPsec VPN协议框架

IPsec是IETF&#xff08;Internet Engineering Task Force&#xff09;制定的一组开放的网络安全协议。它并不是一个单独的协议&#xff0c;而是一系列为IP网络提供安全性的协议和服务的集合&#xff0c;包括认证头AH&#xff08;Authentication Header&#xff09;和封装安全载…

dolphinscheduler海豚调度(五)seatunnel案例

seatunnel作为新一代流行的数据集成工具&#xff0c;其功能非常强大且简单易用&#xff0c;今天演示一下如何通过dolphinscheduler创建并运行seatunnel任务 本次dolphinscheduler和seatunnel均部署在同一机器上的单机版本 1、环境配置 打开dolphinscheduler安装目录&#xf…

【Python】新手入门(6):变量与数据类型

【Python】新手入门&#xff08;6&#xff09;&#xff1a;变量与数据类型 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448…

Java多线程——synchronized、volatile 保障可见性

目录 引出synchronized、volatile 保障可见性Redis冲冲冲——缓存三兄弟&#xff1a;缓存击穿、穿透、雪崩缓存击穿缓存穿透缓存雪崩 总结 引出 Java多线程——synchronized、volatile 保障可见性 synchronized、volatile 保障可见性 原子性&#xff1a;在一次或者多次操作时…

嵌入式学习-FreeRTOS-Day1

一、重点 1、VCC和GND VCC&#xff1a; 1、电路中为电源&#xff0c;供应电压 2、3.3v-5v 3、数字信号中用1表示GND&#xff1a; 1、表示地线 2、一般为0v 3、数字信号中用0表示2、电容和电阻 电容 存储电荷 存储能量&#xff1a; 电容器可以在其两个导体板&#xff08;极…

03.axios数据提交和错误处理

一.axios常用请求方法和数据提交 1. 想要提交数据&#xff0c;先来了解什么是请求方法 请求方法是一些固定单词的英文&#xff0c;例如&#xff1a;GET&#xff0c;POST&#xff0c;PUT&#xff0c;DELETE&#xff0c;PATCH&#xff08;这些都是http协议规定的&#xff09;&am…

【Web】浅聊JDBC的SPI机制是怎么实现的——DriverManager

目录 前言 分析 前言 【Web】浅浅地聊JDBC java.sql.Driver的SPI后门-CSDN博客 上篇文章我们做到了知其然&#xff0c;知道了JDBC有SPI机制&#xff0c;并且可以利用其Driver后门 这篇文章希望可以做到知其所以然&#xff0c;对JDBC的SPI机制的来源做到心里有数 分析 先是…

开源玩具总动员-本博客的知识关系图

作为一个非全职编程爱好者&#xff0c;基本是把计算机周边当做高档大玩具来玩的&#xff0c;顺便带着有兴趣的学生搞一搞学习。这篇文章作为全站的一个导航篇&#xff0c;把本博客的主干要点汇聚一下。从小学开始一直与计算机结缘。通过各种业余时间&#xff0c;慢慢地把感兴趣…

一篇了解电容的使用

目录 一、电容理论基础 1.电容的本质 2.电容量的大小 &#xff08;1&#xff09;电容的单位 &#xff08;2&#xff09;电容量的决定式 3.电容的特点 4.电容的串并联 5.电容器的类型 6.电容实际的电路模型 二、电容器的选型 1.安装方式 2.电容值 3.电容的类型 4…

备战蓝桥杯————二分搜索(一)

引言 一、二分查找 基本概念 代码框架 二、二分查找 题目描述 解题思路及代码 结果展示 三、寻找左侧边界的二分搜索 使用背景 基本代码 引言 在计算机科学的世界里&#xff0c;二分查找算法无疑是一种经典且强大的工具。它以其高效的性能&#xff0c;在有序数据集中…

Java毕业设计 基于SpringBoot 众筹网

Java毕业设计 基于SpringBoot 众筹网 SpringBoot 众筹网 功能介绍 注册 邮箱验证码 登录 忘记密码 首页 图片轮播 关于我们 项目列表 发布项目 我的添加项目 提交审核 已在募捐 项目详情 项目介绍 项目进展 捐赠列表 评论 新闻列表 发布新闻 新闻详情 评论新闻 联系我们 提交…

Android开发工程师面试题,2024年Android开发陷入饱和

前言 马上快到金三银四都春招阶段了&#xff0c;在这本就是跳槽、找工作的年后黄金时间&#xff0c;大多数求职者都早早做好年后求职的准备&#xff0c;其中不乏有年前早早辞了工作准备年后跳槽的有经验的职场老人们&#xff0c;也有一批即将毕业的应届毕业生的职场新人们。 …

redis使用笔记

redis使用笔记 1、Redis简介1.1 含义1.2 功能1.3 特点 2. 常用的数据结构2.1 HASH 3 redis接口定义3.1 redisReply3.2 redisContext3.3 redisCommand 4 实践操作4.1 遇到问题4.1.1 Get哈希的时候返回error4.1.2 长度一直为0&#xff0c;str没法打印&#xff08;未解决&#xff…

合泰HT66F2390----定时器中断学习笔记

前言 无需多言 直接开始定时器中断 的学习 通过上次的PWM学习&#xff0c;上次用的是周期型TM定时器模块 这次使用标准型TM定时器模块&#xff08;STM&#xff09; 代码 #include <HT66F2390.h>void Timer0_Init(void){_stm0c0 0b00001000;_stm0c1 0b11000001;_stm…

Android岗面试,android内存优化面试题

前言 曾听过很多人说Android学习很简单&#xff0c;做个App就上手了&#xff0c;工作机会多&#xff0c;毕业后也比较容易找工作。这种观点可能是很多Android开发者最开始入行的原因之一。 在工作初期&#xff0c;工作主要是按照业务需求实现App页面的功能&#xff0c;按照设…

22万字大模型面经整理+答案

槽位对齐&#xff08;slot alignment&#xff09; 在text2sql任务中&#xff0c;槽位对齐&#xff08;slot alignment&#xff09;通常指的是将自然语言问题中的关键信息&#xff08;槽位&#xff09;与数据库中的列名或API调用中的参数进行匹配的过程。这个过程中&#xff0c…

PTA L2-001 紧急救援

作为一个城市的应急救援队伍的负责人&#xff0c;你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候&#xff0c;你的任务是带领你的…

leetcode 热题 100_最小覆盖子串

题解一&#xff1a; 双指针滑动窗口&#xff1a;暴力解法——用双指针来表示字符串s中的子串首尾&#xff0c;遍历所有子串并与字符串t判断是否符合条件。我们可以对遍历和判断的过程进行优化&#xff0c;首先是遍历&#xff0c;右指针先移动直到涵盖所以需要的字母&#xff0c…

大数据技术学习笔记(五)—— MapReduce(2)

目录 1 MapReduce 的数据流1.1 数据流走向1.2 InputFormat 数据输入1.2.1 FileInputFormat 切片源码、机制1.2.2 TextInputFormat 读数据源码、机制1.2.3 CombineTextInputFormat 切片机制 1.3 OutputFormat 数据输出1.3.1 OutputFormat 实现类1.3.2 自定义 OutputFormat 2 Map…