dede网站图标/今日重大新闻头条十条
在Android开发中,Hook技术是一种强大的手段,它允许开发者拦截和修改系统或应用的行为。通过Hook,我们可以在事件传递的过程中插入自定义的逻辑,从而实现对应用行为的监控和修改。
Android 系统有自己的事件分发机制,所有的代码调用和回调都遵循一定的顺序执行。Hook 技术的作用就在于,可以在事件传送到终点前截获并监控该事件的传输,并进行自定义的处理。
本文将深入探讨Hook技术的原理、应用场景以及如何实现它,带你一窥Android应用背后的神秘力量。
一、Hook技术的定义
Hook技术,源自计算机编程中的“钩子”概念,是一种在程序执行过程中动态改变程序行为的技术,是一种允许用户或开发者拦截和处理系统事件或方法调用的技术。它通过在程序执行路径中插入自定义的代码片段,从而能够实现对程序行为的拦截和修改。
具体来说,Hook 技术主要包括以下几个特点:
- 动态修改: Hook 技术是在程序运行时进行修改,而不是在编译时。这使得它可以灵活地应用于各种场景,而不需要修改程序源码。
- 透明性: 使用 Hook 技术进行修改是透明的,对于程序的其他部分来说是不可见的。这有利于保持程序的整体一致性和稳定性。
- 可扩展性: Hook 技术可以用于各种程序功能的扩展和增强,例如系统监控、性能分析、安全检测等。
- 多样性: Hook 技术可以应用于不同的编程语言和平台,包括 Windows、Linux、macOS 等。它通常利用操作系统或运行时环境提供的钩子机制来实现。
在 Java 中,常见的 Hook 技术包括:
- 使用反射修改现有类的方法实现
- 利用动态代理创建代理对象
- 通过 Java Instrumentation 接口修改类的字节码
- 利用 Java 的 SecurityManager 进行权限控制
通过这些技术,我们可以在不修改程序源码的情况下,动态地拦截和修改程序的行为,从而实现各种功能扩展和系统监控的需求。
二、Hook技术的应用场景
在Android系统中,Hook技术通常用于以下几个方面:
1、拦截系统事件:如按键事件、触摸事件等
使用 Java AWT/Swing 事件监听器:
在 AWT/Swing 中,可以为KeyListener接口添加实现,并注册到需要监听的组件上。
这样可以监听键盘事件,并在事件发生时进行自定义处理。
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;public class HookKeyEvents extends JFrame {public HookKeyEvents() {setTitle("Hook Key Events");setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setSize(400, 300);JPanel panel = new JPanel();panel.addKeyListener(new CustomKeyListener());panel.setFocusable(true);getContentPane().add(panel);}private class CustomKeyListener implements KeyListener {@Overridepublic void keyPressed(KeyEvent e) {System.out.println("Key pressed: " + KeyEvent.getKeyText(e.getKeyCode()));// 在这里添加自定义的按键处理逻辑}@Overridepublic void keyReleased(KeyEvent e) {System.out.println("Key released: " + KeyEvent.getKeyText(e.getKeyCode()));}@Overridepublic void keyTyped(KeyEvent e) {System.out.println("Key typed: " + e.getKeyChar());}}public static void main(String[] args) {SwingUtilities.invokeLater(() -> new HookKeyEvents().setVisible(true));}
}
2、修改系统行为:如改变系统设置、拦截系统调用等
-
使用 Java 的反射机制可以修改现有类的方法实现。
例如,可以拦截 FileInputStream的read()方法,并在执行原有逻辑前后添加自定义行为。
import java.io.FileInputStream; import java.lang.reflect.Method;public class HookFileInputStream {public static void main(String[] args) throws Exception {// 获取 FileInputStream 的 read() 方法Method readMethod = FileInputStream.class.getDeclaredMethod("read");// 创建一个代理方法,实现自定义逻辑readMethod.invoke(new FileInputStream("example.txt"), new Object[0]);}private static Object proxyRead(Object instance, Method method, Object[] args) throws Throwable {System.out.println("Before reading file");Object result = method.invoke(instance, args);System.out.println("After reading file");return result;} }
3、增强应用功能:如实现应用插件化、动态加载等
插件可以扩展应用程序的功能,比如添加新的菜单项或者修改应用程序的行为。
以下案例演示,如何使用 Java 的反射机制和动态代理来实现插件动态加载。
第一步,定义一个简单的应用程序类:
public class Application {private List<Plugin> plugins = new ArrayList<>();public void addPlugin(Plugin plugin) {plugins.add(plugin);plugin.onPluginLoaded();}public void removePlugin(Plugin plugin) {plugins.remove(plugin);plugin.onPluginUnloaded();}public void run() {System.out.println("Running application...");for (Plugin plugin : plugins) {plugin.doSomething();}}
}
第二步,定义一个插件接口:
public interface Plugin {void onPluginLoaded();void onPluginUnloaded();void doSomething();
}
第三步,创建一个简单的插件实现:
public class ExamplePlugin implements Plugin {@Overridepublic void onPluginLoaded() {System.out.println("ExamplePlugin loaded.");}@Overridepublic void onPluginUnloaded() {System.out.println("ExamplePlugin unloaded.");}@Overridepublic void doSomething() {System.out.println("ExamplePlugin doing something.");}
}
第四步,我们创建一个 PluginLoader
类,它使用 Java 的反射机制和动态代理来动态加载和卸载插件:
public class PluginLoader {public static void loadPlugin(Application app, String pluginClassName) throws Exception {Class<?> pluginClass = Class.forName(pluginClassName);Plugin plugin = (Plugin) pluginClass.getDeclaredConstructor().newInstance();app.addPlugin(plugin);}public static void unloadPlugin(Application app, String pluginClassName) throws Exception {Class<?> pluginClass = Class.forName(pluginClassName);Plugin plugin = (Plugin) Proxy.newProxyInstance(PluginLoader.class.getClassLoader(),new Class<?>[]{Plugin.class},(proxy, method, args) -> {System.out.println("Unloading plugin: " + pluginClassName);app.removePlugin((Plugin) proxy);return null;});plugin.onPluginUnloaded();}
}
第五步,在应用程序中动态加载和卸载插件:
public class Main {public static void main(String[] args) {Application app = new Application();try {PluginLoader.loadPlugin(app, "ExamplePlugin");app.run();PluginLoader.unloadPlugin(app, "ExamplePlugin");app.run();} catch (Exception e) {e.printStackTrace();}}
}
在这个例子中,我们使用 PluginLoader
类来动态加载和卸载 ExamplePlugin
。当插件被加载时,它会被添加到应用程序中,并调用 onPluginLoaded()
方法。当插件被卸载时,它会被从应用程序中移除,并调用 onPluginUnloaded()
方法。
三、Hook技术的工作原理
Android Hook技术的核心在于方法拦截。
它通过以下几个步骤实现:
- 获取目标方法或对象: 首先需要确定需要拦截的目标方法或对象。这可以通过反射或动态代理等技术来实现。
- 创建代理类或方法: 创建一个代理类或方法,用于在目标方法或对象被调用时执行自定义的逻辑。
- 替换或修改目标: 将原有的目标方法或对象替换为代理类或方法,使得后续的调用都会指向代理。
- 执行自定义逻辑: 在代理类或方法中执行自定义的逻辑,例如记录日志、修改参数、改变返回值等。
- 可选:恢复原状: 在某些情况下,可能需要在使用完 Hook 技术后将目标方法或对象恢复到原来的状态。
下面我们来看一个完整的案例,演示如何使用 Java 的 Hook 技术来拦截文件读取操作:
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class HookFileInputStream {public static void main(String[] args) {try {// 1. 获取 FileInputStream 的实例FileInputStream fis = new FileInputStream("example.txt");// 2. 创建代理类FileInputStreamProxy proxy = new FileInputStreamProxy(fis);// 3. 获取 FileInputStream 的 read() 方法Method readMethod = FileInputStream.class.getDeclaredMethod("read");// 4. 创建动态代理对象FileInputStream proxyFis = (FileInputStream) Proxy.newProxyInstance(HookFileInputStream.class.getClassLoader(),new Class<?>[]{FileInputStream.class},proxy);// 5. 执行读取操作int data = proxyFis.read();System.out.println("Read data: " + data);} catch (Exception e) {e.printStackTrace();}}private static class FileInputStreamProxy implements InvocationHandler {private final FileInputStream fileInputStream;public FileInputStreamProxy(FileInputStream fileInputStream) {this.fileInputStream = fileInputStream;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 在目标方法执行前执行自定义逻辑System.out.println("Before reading file");// 执行目标方法Object result = method.invoke(fileInputStream, args);// 在目标方法执行后执行自定义逻辑System.out.println("After reading file");return result;}}
}
在这个案例中,我们使用以下步骤实现了 Hook 技术:
- 首先,我们获取了一个
FileInputStream
的实例,这是我们需要拦截的目标对象。 - 然后,我们创建了一个代理类
FileInputStreamProxy
。这个代理类实现了InvocationHandler
接口,用于在目标方法被调用时执行自定义逻辑。 - 接下来,我们通过反射获取了
FileInputStream
的read()
方法。这是我们需要拦截的目标方法。 - 使用 Java 的动态代理机制,我们创建了一个代理对象
proxyFis
。这个代理对象会在调用任何FileInputStream
方法时,都将调用FileInputStreamProxy
的invoke()
方法。 - 最后,我们调用了代理对象的
read()
方法,在方法执行前后分别输出了一些自定义的日志信息。
四、Hook技术的关键组件
Hook 技术的关键组件主要包括以下几个部分:
- 目标对象/方法: 需要被拦截和修改的系统对象或方法。这是 Hook 技术的核心所在。通常可以通过反射或动态代理等机制获取目标对象或方法。
- 代理对象/方法: 用于替换原有的目标对象或方法,并在目标被调用时执行自定义逻辑的代理实现。代理可以是一个单独的类,也可以是一个动态生成的代理对象。
- 替换机制: 将原有的目标对象或方法替换为代理对象或方法的机制。这可以通过修改对象的成员变量、重写类的方法、使用动态代理等方式实现。
- 自定义逻辑: 在代理对象或方法中执行的自定义逻辑。这是 Hook 技术的核心价值所在,可以包括记录日志、修改参数、改变返回值等各种功能。
- 恢复机制: 有时需要在使用完 Hook 技术后,将系统恢复到原来的状态。这需要提供一种方法来撤销之前的修改,比如保存原有状态并在合适的时候恢复。
下面我们来看一个更加详细的 Java 代码示例,演示这些关键组件的使用:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 1. 目标对象
class TargetObject {public void doSomething() {System.out.println("TargetObject is doing something.");}
}// 2. 代理对象
class ProxyObject implements InvocationHandler {private final Object targetObject;public ProxyObject(Object target) {this.targetObject = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 3. 自定义逻辑System.out.println("Before calling method: " + method.getName());// 4. 执行原始方法Object result = method.invoke(targetObject, args);// 3. 自定义逻辑System.out.println("After calling method: " + method.getName());return result;}
}// 5. 替换机制
public class HookExample {public static void main(String[] args) {// 1. 获取目标对象TargetObject target = new TargetObject();// 2. 创建代理对象ProxyObject proxy = new ProxyObject(target);// 5. 替换机制 - 使用动态代理TargetObject proxyTarget = (TargetObject) Proxy.newProxyInstance(TargetObject.class.getClassLoader(),new Class<?>[]{TargetObject.class},proxy);// 调用目标对象proxyTarget.doSomething();// 5. 恢复机制 (可选)// 在某些情况下,可能需要将目标对象恢复到原来的状态}
}
在这个例子中,我们演示了 Hook 技术的 5 个关键组件:
- 目标对象: 我们定义了一个
TargetObject
类,作为需要被拦截的目标对象。 - 代理对象: 我们创建了一个
ProxyObject
类,它实现了InvocationHandler
接口,用于在目标方法被调用时执行自定义逻辑。 - 自定义逻辑: 在
ProxyObject
的invoke()
方法中,我们添加了在目标方法调用前后执行的自定义逻辑。 - 执行原始方法: 在
invoke()
方法中,我们使用method.invoke()
来调用原始的目标方法。 - 替换机制: 我们使用 Java 的动态代理机制,将原有的
TargetObject
实例替换为代理对象proxyTarget
。这样,所有对TargetObject
的调用都会被ProxyObject
拦截和处理。
此外,我们还提到了恢复机制,即在某些情况下需要将目标对象恢复到原来的状态。这可以通过保存原始对象的引用,并在适当的时候替换回去来实现。
通过这个例子,你应该能够清楚地理解 Hook 技术的关键组件及其在 Java 中的具体实现方式。这种技术可以让你在不修改系统源码的情况下,动态地修改系统的行为,从而实现各种功能扩展和监控需求。
五、实战案例:Activity 插件化实现
在现代 Android 开发中,模块化和插件化设计理念备受青睐。通过插件化技术,我们可以在不修改主 App 的情况下动态加载外部代码,实现功能热插拔。其中,Activity 插件化是一个重要的课题, 这里将详细介绍如何利用 Hook 技术来实现这一目标。
1、Activity 启动流程剖析
在着手实现之前,我们有必要了解一下 Activity 的启动流程。
简单来说,当我们调用 startActivity
方法时,Android 系统会执行以下几个主要步骤:
- (1)、
Instrumentation
的execStartActivity
方法被调用 - (2)、经过层层调用后,最终会执行到
ActivityManagerService(AMS)
的startActivity
方法 - (3)、AMS 进行权限校验,资源校验等一系列检查
- (4)、AMS 通过 Binder 机制调用
ActivityThread
的scheduleLaunchActivity
方法 - (5)、ActivityThread
经过一系列准备步骤后,调用
Instrumentation的
newActivity方法实例化目标 Activity
- (6)、
最后回调目标 Activity 的
onCreate` 等生命周期方法
我们可以发现,想要 Hook 住一个 Activity,最理想的切入点就是在 AMS 的校验之前将目标 Activity 替换成我们想要加载的插件 Activity。
2、自定义 Instrumentation 实现 Hook
Android 为我们提供了 Instrumentation
类,方便在应用程序进程之外监控、修改应用的行为。我们可以通过继承 Instrumentation
并覆写 newActivity
方法,在目标 Activity 实例化前将其替换成我们想要的插件 Activity:
public class MyInstrumentation extends Instrumentation {private ActivityThread origin;@Overridepublic Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {PluginInfo plugin = PluginManager.getInstance().getPlugin(intent);if (plugin != null) {intent.setClassName(plugin.getPackageName(), plugin.getActivityClassName());}if (origin == null) {try {// 反射获取 ActivityThread 实例Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");currentActivityThreadMethod.setAccessible(true);origin = (ActivityThread) currentActivityThreadMethod.invoke(null);// 使用原始 Instrumentation 创建 Activityreturn origin.getInstrumentation().newActivity(cl, intent.getComponent().getClassName(), intent);} catch (Exception e) {e.printStackTrace();}}return super.newActivity(cl, className, intent);}
}
在上面的代码中,我们首先检查目标 Activity 是否需要插件化。如果是,则使用插件包名和 Activity 类名替换原始 Intent 中的值。接着,为了确保非插件化 Activity 的正常启动,我们通过反射获取 ActivityThread
实例,调用其 getInstrumentation
方法获取原始 Instrumentation,并由原始 Instrumentation 创建 Activity 实例。
接下来,我们需要一种方式在 App 启动时注入我们自定义的 Instrumentation。这里我们以 Android 8.0 为例,需要通过 attachBaseContext
来实现。
3、App 启动时注入自定义的 Instrumentation
public class MyApplication extends Application {@Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);try {// 获取原始 InstrumentationInstrumentation instrumentation = (Instrumentation) base.getPackageManager().getInstrumentationInfo(new ComponentName(base.getPackageName(), Instrumentation.class.getName()), 0).classLoader.loadClass("android.app.Instrumentation").newInstance();// 通过反射将 Instrumentation 设置为我们自定义的 MyInstrumentationActivityThread activityThread = (ActivityThread) getActvityThreadMethod.invoke(getStaticFieldValue(activityThreadClass, "sCurrentActivityThread"), null);setFieldValue(activityThread, "mInstrumentation", new MyInstrumentation());} catch (Exception e) {e.printStackTrace();}}// 反射相关工具方法private static Object getStaticFieldValue(Class klass, String fieldName) throws NoSuchFieldException, IllegalAccessException {Field field = klass.getDeclaredField(fieldName);field.setAccessible(true);return field.get(null);}private static void setFieldValue(Object instance, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {Field field = instance.getClass().getDeclaredField(fieldName);field.setAccessible(true);field.set(instance, value);}private static Method getActvityThreadMethod = getMethod(ActivityThread.class, "currentActivityThread");
}
在 attachBaseContext
中,我们首先获取原始 Instrumentation,然后通过反射将 ActivityThread 的 mInstrumentation
字段设置为我们自定义的 MyInstrumentation 实例。这样一来,当 App 启动时,我们的 Instrumentation 就会生效,实现对 Activity 启动过程的拦截和修改。
4、注册与获取插件 Activity
上面我们已经实现了 Hook 逻辑,接下来需要一种方式注册和获取插件 Activity。这里我们可以定义一个 PluginManager 类,维护一个插件列表:
public class PluginManager {private static PluginManager instance;private Map<String, PluginInfo> plugins = new HashMap<>();public static PluginManager getInstance() {if (instance == null) {synchronized (PluginManager.class) {if (instance == null) {instance = new PluginManager();}}}return instance;}public void registerPlugin(PluginInfo plugin) {plugins.put(plugin.getAction(), plugin);}public PluginInfo getPlugin(Intent intent) {return plugins.get(intent.getAction());}public static class PluginInfo {private String packageName;private String activityClassName;private String action;public PluginInfo(String packageName, String activityClassName, String action) {this.packageName = packageName;this.activityClassName = activityClassName;this.action = action;}// Getter & Setter}
}
使用时,我们只需要提前将插件 Activity 的包名、类名和匹配 Action 注册到 PluginManager 即可:
PluginManager.getInstance().registerPlugin(new PluginManager.PluginInfo("com.example.plugin","com.example.plugin.PluginActivity","com.example.plugin.ACTION")
);
通过上面的实现,我们成功地利用Hook技术实现了Activity插件化。用户只需在主APP中注册插件Activity,即可在不修改主APP代码的情况下加载插件Activity,实现功能热插拔。这种设计不仅提高了代码的可维护性和扩展性,而且为实现跨APP调用等高级功能打下基础。
六、结语
本文深入探讨了 Android Hook 技术的原理和实践应用,为读者提供了全面的认知和实践指引。Hook 技术作为一种强大的开发手段,在未来的 Android 应用优化和创新中都将发挥重要作用。在下一篇文章中,我们将进一步探讨如何构建一个完善、安全、高效的插件化框架以及 Hook 技术在项目优化中的应用 ,欢迎继续关注并多提宝贵意见!
相关文章:

深入解读 Android Hook 技术-从原理到实践
在Android开发中,Hook技术是一种强大的手段,它允许开发者拦截和修改系统或应用的行为。通过Hook,我们可以在事件传递的过程中插入自定义的逻辑,从而实现对应用行为的监控和修改。 Android 系统有自己的事件分发机制,…...

架构每日一学 15:想要提升协作效率,必须先统一语义
谭sir与二仙桥大爷的经典对话: 谭sir:你该走哪?(非机动车能走机动车道吗?) 大爷:走二仙桥去成华大道(因为我要去成华大道,当然要走二仙桥) 谭sir:…...

基于树莓派4B设计的智能家居系统(华为云IOT)
基于树莓派的智能家居控制系统(华为云IOT) 文章目录 一、设计需求前言【1】 项目背景【2】需求总结【3】研究的内容【4】 国内外研究状况【5】本课题要解决的问题【6】开发工具的选择 二、硬件选型【1】 树莓派开发板【2】TFT卡-烧写系统使用【3】0.5米网线-远程登录【4】MQ2烟…...

路由懒加载
在 Vue.js 中,路由懒加载(也称为代码分割)是一种优化策略,它允许你将 Vue 组件分割成不同的代码块,并在需要时按需加载它们。这有助于减少初始加载时间,提高用户体验。 Vue Router 支持 Webpack 的动态 im…...

在Spring中实现资源的动态加载和卸载
在Spring框架中,实现资源的动态加载和卸载通常涉及以下几个方面: 1. 使用Bean注解动态注册Bean 通过在配置类中使用Bean注解,可以在运行时动态创建和注册Bean。 Configuration public class DynamicBeanConfig {Beanpublic MyBean myBean(…...

Windows下 CLion中,配置 OpenCV、LibTorch
首先按照win下C部署深度学习模型之clion配置pytorchopencv教程记录 步骤配置。 LibTorch 部分 在测试LibTorch时会出现类似 c10.dll not found 的问题(Debug才有): 参考C部署Pytorch(Libtorch)出现问题、错误汇总和 …...

机器学习知识点总结
简介:随着人工智能(AI)蓬勃发展,也有越来越多的人涌入到这一行业。下面简单介绍一下机器学习的各大领域,机器学习包含深度学习以及强化学习,在本节的机器学习中主要阐述一下机器学习的线性回归逻辑回归&…...

OBproxy基础运维
简介 obproxy 属于OceanBase的代理,生产环境中 OceanBase 数据库的数据 会以 多副本的形式 存放在各个 OBServer 节点上,obproxy 接收用户发出的 SQL 请求,并将 SQL 请求转发至最佳目标 OBServer 节点,最后将执行结果返回给用户&…...

【Python】 探索Pytz库中的时区列表
基本原理 在Python中,处理时区是一个常见但复杂的问题。pytz是一个Python库,它提供了对时区的精确和丰富的支持。pytz库是datetime模块的补充,它允许更准确地处理时区信息。pytz库包括了IANA时区数据库,这个数据库包含了全球的时…...

第六讲:AD、DA的工作原理及实现、运放电路
DA 数模转换器 (DAC) 数模转换器(Digital-to-Analog Converter,简称DAC)是一种将数字信号转换为模拟信号的电子装置。DAC在各种电子设备中广泛应用,如音频设备、通信系统、测量设备和控制系统中。以下是DAC的主要概念和应用。…...

计网ppt标黄知识点整理第(4)章节——谢希仁版本、期末复习自用
路由器:查找转发表,转发分组。 IP网的意义:当互联网上的主机进行通信时,就好像在一个网络上通信一样,看不见互连的各具体的网络异构细节。如果在这种覆盖全球的 IP 网的上层使用 TCP 协议,那么就…...

[数据集][目标检测]RSNA肺炎检测数据集VOC+YOLO格式6012张1类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):6012 标注数量(xml文件个数):6012 标注数量(txt文件个数):6012 标注…...

AndroidStudio中debug.keystore的创建和配置使用
1.如果没有debug.keystore,可以按照下面方法创建 首先在C:\Users\Admin\.android路径下打开cmd窗口 之后输入命令:keytool -genkey -v -keystore debug.keystore -alias androiddebugkey -keyalg RSA -validity 10000 输入两次密码(密码不可见,打码处随便填写没关系) 2.在build…...

什么是最好的手机数据恢复软件?6 款手机数据恢复软件 [2024 年更新]
什么是最好的手机数据恢复软件?在这篇文章中,您将了解 6 款最好的免费手机数据恢复软件,并学习如何恢复数据的完整指南。 最好的手机数据恢复软件是什么? 手机数据恢复软件是恢复智能手机中丢失或删除的文件、消息、照片和其他宝…...

力扣2653.滑动子数组的美丽值
力扣2653.滑动子数组的美丽值 计数排序:数值作为下标 记录个数 求第x小的数 : 暴力枚举负数 直到找到第x个负数 class Solution {public:vector<int> getSubarrayBeauty(vector<int>& nums, int k, int x) {const int BIAS 50;int c…...

2024-06-04 架构-不同层次的抽象的处理-分析
摘要: 2024-06-04 架构-不同层次的抽象的处理-分析. 不同层次的抽象: 将事情做不同的情况的解耦,关于在于理解事情本身的性质,如何解耦,如何处理当将事情解耦成不同的情况后,就可以针对不同的事情,最不同的处理。这么…...

MySQL——C语言连接数据库
MySQL Connection 连接数据库的客户端除了命令行式的还有图形化界面版本,网页版本,当然也包括语言级别的库或者是包,能够帮助我们直接连接数据库; 一、语言连接库下载 方式一:不建议使用,需要自己配置…...

新能源汽车推行精益生产:绿色动力下的效率革命
在新能源汽车行业迅猛发展的当下,推行精益生产已成为提升竞争力的关键所在。精益生产,作为一种以客户需求为导向、追求流程最优化和浪费最小化的管理理念,正逐步在新能源汽车领域展现出其独特的魅力。 新能源汽车的兴起,不仅代表了…...

FCA-九数云 试题及答案
第1题【判断题】仪表板中筛选器只能绑定图表或者图表所在分析表的字段,无法绑定父表中的字段。 A. 正确B. 错误 正确答案:B 第2题【单选题】一张绩效奖金看板,分享给公司所有成员查看。希望输入个人的信息前,只可以查看自己的相关…...

qt dragEnterEvent dragLeaveEvent dragMoveEvent dropEvent都不响应的问题解决方案。
环境:vs2019qt5.14.2 坑哦。让我搞了好久。各种不执行,最后发现,不用vs调制,直接运行exe就能接收拖拽了。 记录一下,感觉是qt的bug。上代码。 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QText…...

LVS精益价值管理系统 DownLoad.aspx 任意文件读取漏洞复现
0x01 产品简介 LVS精益价值管理系统是杭州吉拉科技有限公司研发的一款专注于企业精益化管理和价值流优化的解决方案。该系统通过集成先进的数据分析工具、可视化的价值流映射技术和灵活的流程改善机制,帮助企业实现高效、低耗、高质量的生产和服务。 0x02 漏洞概述…...

【GIC400】——中断使能
文章目录 中断使能全局中断使能RISC-VCortex-MCortex-A中断控制器使能PLICNVICGIC模块中断使能参考系列文章 【ARMv7-A】——异常与中断 【ARMv7-A】——异常中断处理概述 【ARMv7-A】——进入和退出异常中断的过程</...

容器项目之前后端分离
容器化部署ruoyi项目 #需要的镜像nginx、java、mysql、redis、 #导入maven镜像、Java镜像和node镜像 docker load -i java-8u111-jdk.tar docker load -i maven-3.8.8-sapmachine-11.tar docker load -i node-18.20.3-alpine3.20.tar #拉取MySQL和nginx镜像 docker pull mysql…...

游戏心理学Day04
第二章 心理学的生理基础 第三节 游戏中的感觉应用 认知地图 表象是在没有外在知觉信息来源的情况下,对类似知觉信息的加工。 表征是指信息或知识在心理活动中的表现和记载方式,表征是外部事物在心理活动中的内部再现。 我们对世界的表象,…...

文件上传漏洞之upload-labs
前提: 本文中的以xshell命名的均为以密码为admin的一句话木马,而shell命名的则是由冰蝎工具生成的木马。 pass-01:js前端验证 测试性的上传一个一句话木马,发现被拦截了,而且根据推测大概率是前端检测,于…...

解决使用gets(getchar)函数无法输入字符(字符串)和scanf_s函数显示缺少“scanf_s”整型参数的问题
一.函数介绍 gets函数: 该函数就是读取字符串,遇到空格不会停止,直到遇到换行字符,但是也会读取最后的换行字符(这也就是我在写代码的时候遇到的一个问题) getchar函数: 和gets函数类似&#x…...

Excel的VLOOKUP函数的用法
由于工作需要,最近用Excel的时候比较多,遇到一个需求,刚好需要用到VLOOKUP函数,结果由于对参数的理解不清晰,导致折腾了很久,都没达到想要的效果。所以,这里特此就遇到的坑做一个记录࿰…...

【Java面试】十三、ArrayList相关
ArrayList、LinkedList、HashMap等集合,从其前缀可知其对应的数据结构为数组、链表、哈希表。从数据结构,也可反推出集合的结构。 文章目录 1、算法复杂度分析1.1 时间复杂度1.2 常见复杂度1.3 空间复杂度 2、数组2.1 内存寻址2.2 查找元素的时间复杂度2…...

网络简史-基于图论的网络
先看一幅图: 如图,我们对类似 crossbar,banyan tree,b-tree,10-tree,256-tree,甚至 dcn fat-tree 等 “规则拓扑” 网络相当熟悉。规则拓扑网络中,地址信息被编码到拓扑本身&#…...