前言

在日常分析安卓应用时,通常会有对应用进行hook的需求,用的比较多的hook框架有Xposed,frida,xhook等,正好最近接触Frida接触的较多,所以对Frida的一些常用操作做个记录,方便以后翻阅查询,同时也可以对学习frida的小伙伴有个参考的资料;

一般Frida逆向三阶段:

  1. 阶段一.分析程序执行逻辑,函数参数,函数返回值
  2. 阶段二.在1的基础上对数据进行修改,执行流程的控制,核心方法的调用
  3. 阶段三.在2的基础上实现对核心方法的封装调用,提供对外服务接口

1.FRIDA安装

1.1.安装python3环境

https://www.python.org/ 下载最新的直接安装

1.2.frida安装

C:\Users\Administrator>pip install frida-tools
Collecting frida-tools
/...省略.../Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.Running setup.py install for frida-tools ... done
Successfully installed colorama-0.4.0 frida-12.2.25 frida-tools-1.2.2 prompt-toolkit-1.0.15 pygments-2.2.0 six-1.11.0 wcwidth-0.1.7

1.3.frida-server下载

frida-server下载需要注意的有两点:
1.安装的frida版本:需要下载与安装的frida版本对应的frida-server,否则会出错。

C:\Users\Administrator>frida --version
12.2.25

下载地址:https://github.com/frida/frida/releases

2.手机设备的架构:因为我的手机是arm64平台的所以选择的是android-arm64

frida-server-12.2.25-android-arm64.xz

下载完毕之后,解压后通过adb push到设备的临时目录下

adb push frida-server-12.2.25-android-arm64 /data/local/tmp/

1.4.启动frida服务并连接

adb shell进入设备shell环境,cd到临时目录下,给frida-server文件设置可执行权限使其可以运行

chmod 755 frida-server-12.2.25-android-arm64

之后运行frida服务文件,注意需要通过root权限去运行

./frida-server-12.2.25-android-arm64

然后 进行端口转发

adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043

完毕后就可以在windows上运行简单的frida命令测试我们是否安装成功了

/* -U 连接USB设备 */
/* frida-ps 列出正在运行的进程*/
C:\Users\Administrator>frida-ps -U
PID  Name
----  --------------------------------
310  1:3
714  360sguard
723  360sguard
373  ATFWD-daemon
575  adbd
259  adsprpcd
[....]
3289  zygote

1.5.搭建frida开发环境

$ git clone git://github.com/oleavr/frida-agent-example.git
$ cd frida-agent-example/
$ npm install
$ frida -U -f com.example.android --no-pause -l _agent.js

2.FRIDA 基础

首先来看一下frida的常用功能

2.1.frida查看当前设备进程

λ frida-ps -UPID  Name
-----  -------------------------------------------------------
6241  adbd
897  adsprpcd
749  android.hardware.audio@2.0-service
947  android.hardware.biometrics.fingerprint@2.1-service.fpc
750  android.hardware.bluetooth@1.0-service-qti
600  android.hardware.boot@1.0-service
752  android.hardware.camera.provider@2.4-service
753  android.hardware.cas@1.0-service
612  android.hardware.configstore@1.1-service
[...]

2.2.frida对指定方法进行trace

λ frida-ps -U | grep frida
31521  cn.gemini.k.fridatest
30073  frida-helper-32 λ frida-trace -i "open" -U "cn.gemini.k.fridatest"
Instrumenting...
open: Auto-generated handler at "E:\\Frida\\frida_work\\__handlers__\\libutils.so\\open.js"
Started tracing 1 function. Press Ctrl+C to stop.  /* TID 0x7b3a */
16061 ms  open()/* TID 0x7b3c */
16114 ms  open()
16114 ms  open()
16114 ms  open()

用的比较多的一般就是上面的两个功能,都是frida帮我们生成好了hook代码,直接拿来使用就行,简单方便,但有时候我们想自己定制些功能怎么办?

接下来看下如何通过编写js代码来实现对安卓APP中某些方法的hook。
在hook之前首先要熟悉我们需要hook的目标方法,应用包名,参数等基础信息。这里简单写了个demo,后面我们都通过这个demo来学习。

package cn.gemini.k.fridatest;
import android.util.Log;public class FridaHook1 {static String password = "88888888";int cde = 20;int abc = 10;public int abc(){return abc;}FridaHook1(){Log.e("FridaHook1","一般方法 ret:"+func1_add(cde,abc));   // 一般方法Log.e("FridaHook1","重载方法 ret:"+func2_add_overload(1,2));       // 重载方法Log.e("FridaHook1","重载方法 ret:"+func2_add_overload(1,2,3));  // 重载方法Log.e("FridaHook1","静态方法 ret:"+func3_verify_static("12345678"));  // 静态方法new inner_class().inner_class_func("内部类调用");    // 内部类Log.e("匿名类方法 FridaHook1","ret:"+new anonymous_class(){ //匿名类public String output(){return "匿名类调用";}}.output());}public int func1_add(int a,int b){Log.e("func1_add","arg1:"+a+" arg2:"+b);return a+b;}public int func2_add_overload(int a,int b){Log.e("func2_add_overload","arg1:"+a+" arg2:"+b);return a+b;}public int func2_add_overload(int a,int b,int c){Log.e("func2_add_overload","arg1:"+a+" arg2:"+b+" arg3:"+c);return a+b+c;}public static int func3_verify_static(String str){Log.e("func3_verify_static",str);if(str.equals(password)){Log.e("func3_verify_static","密码正确");return 1;}Log.e("func3_verify_static","密码错误");return 0;}static class inner_class{inner_class(){}public void inner_class_func(String str){Log.e("inner_class_func",str);}}class anonymous_class{ }
}

整理一下hook时需要的信息:
目标应用的包名:“cn.gemini.k.fridatest”(一般在AndroidManifest.xml文件中可以找到)
目标方法所在类的类名:“cn.gemini.k.fridatest.FridaHook1”;
最后是目标方法名和参数:public int func1_add(int a,int b)
没有源码的情况下这些信息都可以通过反编译工具jadx或jeb获取。
接下来开始编写hook代码

2.3.frida hook代码的两种形式

2.3.1.代码内置在python文件中

load.py代码如下:

# -*- coding: UTF-8 -*-
import frida,sysjs_code = '''Java.perform(function(){console.log("Frida Test");var cls = Java.use("cn.gemini.k.fridatest.FridaHook1");cls.func1_add.implementation = function(arg1,arg2){console.log("hook arg1:",arg1," hook arg2:",arg2);return this.func1_add(arg1,arg2);}});
'''# 目标包名
appPacknName = "cn.gemini.k.fridatest"
scriptFile = "hook_script.js"# 输出日志的回调方法
def on_message(message, data):if message['type'] == 'send':print("[*] {0}".format(message['payload']))else:print(message)device = frida.get_usb_device()
# spawn模式,找到目标包名并重启,在启动前注入脚本
pid = device.spawn([appPacknName])
session = device.attach(pid)
# 注意这里需要将device.attach(pid)这句代码写在前面,这样执行才符合预期(启动时程序白屏,等待下面这行代码来恢复执行)
# 其实在https://www.jianshu.com/p/b833fba1bffe这篇文章中有提到
device.resume(pid)# 方式一: 通过js文件创建hook代码
#with open(scriptFile, encoding='UTF-8') as f :
#    script = session.create_script(f.read())
# 方式二: 直接将hook代码写在python文件中
script = session.create_script(js_code)script.on('message', on_message)
script.load()   #把js代码注入到目标应用中
# 避免结束
sys.stdin.read()

解释下上面的python代码,先通过frida.get_usb_device连接到usb设备,接着使用spawn将目标应用重启并挂起,之后再通过attach附加到目标应用,完成附加后开始唤起应用主线程,最后是构造需要注入的js代码,通过session.create_script生成js脚本,script.on用来注册一个回调方法监听目标进程的所有消息,script.load会把前面生成的js代码注入到目标应用中完成hook。这里hook代码只是简单的将函数的参数给输出了下。

运行结果如下:

// frida打印日志
λ python load.py
Frida Test
hook arg1: 1  hook arg2: 2// APP程序执行打印日志
19493-19493/cn.gemini.k.fridatest E/func1_add: arg1:1 arg2:2
19493-19493/cn.gemini.k.fridatest E/FridaHook1: 一般方法 ret:3

2.3.2.代码封装成js文件

hook_script.js代码如下

function main(){Java.perform(function(){console.log("Frida Test");var cls = Java.use("cn.gemini.k.fridatest.FridaHook1");cls.func1_add.implementation = function(arg1,arg2){console.log("hook arg1:",arg1," hook arg2:",arg2);return this.func1_add(arg1,arg2);}});
}
setImmediate(main)

load2.py代码如下

# -*- coding: UTF-8 -*-
import frida,sys# 目标包名
appPacknName = "cn.gemini.k.fridatest"
scriptFile = "hook_script.js"# 输出日志的回调方法
def on_message(message, data):if message['type'] == 'send':print("[*] {0}".format(message['payload']))else:print(message)device = frida.get_usb_device()
# spawn模式,找到目标包名并重启,在启动前注入脚本
pid = device.spawn([appPacknName])
session = device.attach(pid)
# 注意这里需要将device.attach(pid)这句代码写在前面,这样执行才符合预期(启动时程序白屏,等待下面这行代码来恢复执行)
# 其实在https://www.jianshu.com/p/b833fba1bffe这篇文章中有提到
device.resume(pid)# 方式一: 通过js文件创建hook代码
with open(scriptFile, encoding='UTF-8') as f :script = session.create_script(f.read())
# 方式二: 直接将hook代码写在python文件中
# script = session.create_script(js_code)script.on("message", on_message)
script.load()   #把js代码注入到目标应用中
# 避免结束
sys.stdin.read()

运行结果如下:

// frida打印日志
λ python load2.py
Frida Test
hook arg1: 1  hook arg2: 2// APP程序执行打印日志
19493-19493/cn.gemini.k.fridatest E/func1_add: arg1:1 arg2:2
19493-19493/cn.gemini.k.fridatest E/FridaHook1: 一般方法 ret:3

上面这种方式主要是将js代码封装成了一个单独的js文件,不再是之前放在python文件中的形式,封装成一个js文件可以方便我们对代码进行模块化管理。同时这样做还有一个好处就是可以在命令行直接使用frida注入工具来注入js代码到指定进程,省去了用python写load的步骤。

λfrida -U -n "cn.gemini.k.fridatest" -l hook_script.js____/ _  |   Frida 14.2.2 - A world-class dynamic instrumentation toolkit| (_| |> _  |   Commands:/_/ |_|       help      -> Displays the help system. . . .       object?   -> Display information about 'object'. . . .       exit/quit -> Exit. . . .. . . .   More info at https://www.frida.re/docs/home/
Attaching...
Frida Test Hook
[AOSP on walleye::cn.gemini.k.fridatest]->

2.4.frida的两种注入模式

最后简单了解下frida的两种注入模式:
第一种:直接通过attach方法对已启动的目标应用进行注入,这种一般用在hook时机比较晚的场景。
第二种:先使用spawn方法以挂起模式重新启动目标应用后再通过attach方法进行注入,一般用在hook时机比较早的场景。
暂时先大概有所了解,后面会将具体的使用场景。

3.FRIDA HOOK

3.1.hook普通方法、参数/返回值修改

还是上面的hook demo那个例子,我们这次对传入的参数以及返回值尝试修改。
修改js文件中的代码

function Hook1(){Java.perform(function(){console.log("Frida Test Hook1");var cls = Java.use("cn.gemini.k.fridatest.FridaHook1");cls.func1_add.implementation = function(arg1,arg2){console.log("func1_add --> hook arg1:",arg1," hook arg2:",arg2); // 输出原始参数arg1 = arg1 + 1; // 修改参数值加1arg2 = arg2 + 1;    // 修改参数值加1var ret = this.func1_add(arg1,arg2); // 将修改后的参数传入给原始方法return ret + 3;   // 将方法的返回值加3,并返回给调用方法}});
}

上面的js代码就是我们hook要实现的功能,Java.use方法通过类名获取类的类型并返回一个相同类型的对象cls,之后通过cls对象对普通方法func1_add进行hook,成功后当func1_add方法被别的方法调用时就会走到我们设置的hook方法。
我们的hook方法会将传入的两个参数都分别加1,被hook方法的参数获取可以通过在hook方法中定义形参获取也可通过在hook方法内部利用arguments[*]数组获取。修改完参数后再传递给原始方法进行计算,原始方法将返回值返回给hook方法后我们再将返回值加3,最后返回给应用的调用方法。
运行结果如下:

λ python load2.py
Frida Test Hook1
func1_add --> hook arg1: 1  hook arg2: 2// APP程序执行打印日志
6374-6374/cn.gemini.k.fridatest E/func1_add: arg1:2 arg2:3
6374-6374/cn.gemini.k.fridatest E/FridaHook1: 一般方法 ret:8

从上面的结果可以看到,不管是传入的参数,还是最后的返回值都被我们修改掉了。

3.2.hook重载方法/所有重载方法

如果需要hook的方法存在重载方法,那么就需要使用overload关键字来指明参数类型,否则frida会报错。
js代码:

function Hook2(){Java.perform(function(){console.log("Frida Test Hook2");var cls = Java.use("cn.gemini.k.fridatest.FridaHook1");// 重载方法hookcls.func2_add_overload.overload('int', 'int').implementation = function(arg1,arg2){console.log("func2_add_overload --> hook arg1:",arg1," hook arg2:",arg2);arg1 = arg1 + 1;arg2 = arg2 + 1;var ret = this.func2_add_overload(arg1,arg2);return ret + 3;}cls.func2_add_overload.overload('int', 'int', 'int').implementation = function(arg1,arg2,arg3){console.log("func2_add_overload --> hook arg1:",arg1," hook arg2:",arg2," hook arg3:",arg3);arg1 = arg1 + 1;arg2 = arg2 + 1;arg3 = arg3 + 1;var ret = this.func2_add_overload(arg1,arg2,arg3);return ret + 3;}});
}

执行结果

λ python load2.py// APP程序执行打印日志
6374-6374/cn.gemini.k.fridatest E/func2_add_overload: arg1:1 arg2:2
6374-6374/cn.gemini.k.fridatest E/FridaHook1: 重载方法 ret:6
6374-6374/cn.gemini.k.fridatest E/func2_add_overload: arg1:1 arg2:2 arg3:3
6374-6374/cn.gemini.k.fridatest E/FridaHook1: 重载方法 ret:9

如果重载方法比较少上面的方法还行,但是如果重载方法较多的话一个个去写重载就比较无趣了,frida还给我们提供了一种hook所有重载方法的方法。
js代码

console.log(cls.func2_add_overload.overloads.length);
for(var i = 0; i < cls.func2_add_overload.overloads.length; i++){cls.func2_add_overload.overloads[i].implementation = function(){if(arguments.length == 2){var arg1 = arguments[0] + 1;var arg2 = arguments[1] + 1;console.log("func2_add_overload --> hook arg1:",arg1," hook arg2:",arg2);return this.func2_add_overload.apply(this,arguments) + 3;}else if(arguments.length == 3){var arg1 = arguments[0] + 1;var arg2 = arguments[1] + 1;var arg3 = arguments[2] + 1;console.log("func2_add_overload --> hook arg1:",arg1," hook arg2:",arg2," hook arg3:",arg3);return this.func2_add_overload.apply(this,arguments) + 3;}}
}

3.3.hook构造方法

如果我们想hook一个类的构造方法,那么使用固定的$init代替构造方法名:

function Hook3(){Java.perform(function(){console.log("Frida Test Hook3");var cls = Java.use("cn.gemini.k.fridatest.FridaHook1");cls.$init.implementation = function(){console.log("$init --> hook");//this.$init();}});
}

执行结果

λ python load2.py
Frida Test Hook3
$init --> hook// APP程序执行打印日志
因为hook方法只输出了"$init --> hook"并没有调用原始构造方法,所以程序并没有输出。

3.4.hook静态方法

静态方法的hook实际上和一般方法的hook类似。

function Hook4(){Java.perform(function(){console.log("Frida Test Hook4");var cls = Java.use("cn.gemini.k.fridatest.FridaHook1");cls.func3_verify_static.implementation = function(arg1){console.log("func3_verify_static --> hook arg1:",arg1);return this.func3_verify_static(arg1);}});
}

执行结果

λ python load2.py
Frida Test Hook4
func3_verify_static --> hook arg1: 12345678// APP程序执行打印日志
6374-6374/cn.gemini.k.fridatest E/func3_verify_static: 12345678
6374-6374/cn.gemini.k.fridatest E/func3_verify_static: 密码错误
6374-6374/cn.gemini.k.fridatest E/FridaHook1: 静态方法 ret:0

3.5.hook内部类方法/匿名类方法

对于内部类或匿名类方法的hook需要通过$+名字的方式来使用。

function Hook5(){Java.perform(function(){console.log("Frida Test Hook5");// hook内部类方法var innercls = Java.use("cn.gemini.k.fridatest.FridaHook1$inner_class");innercls.inner_class_func.implementation = function(arg1){console.log("inner_class_func arg1:",arg1);return this.inner_class_func(arg1);}// hook匿名类方法var innercls = Java.use("cn.gemini.k.fridatest.FridaHook1$1"); // 匿名类的类名一般使用反编译软件获取innercls.output.implementation = function(){console.log("output 匿名类方法调用");return this.output();}});
}

执行结果

λ python load2.py
Frida Test Hook5
inner_class_func arg1: 内部类调用
output 匿名类方法调用// APP程序执行打印日志
6374-6374/cn.gemini.k.fridatest E/inner_class_func: 内部类调用
6374-6374/cn.gemini.k.fridatest E/匿名类方法 FridaHook1: ret:匿名类调用

3.6.枚举所有类与类的所有方法

Java.enumerateLoadedClasses(callbacks):无返回值,参数是一个回调方法,功能是列出当前已经加载的类,用回调方法处理。
回调方法:
onMath:function(name){}
找到加载的每个类的时候被调用,参数就是类的名字,可以将name传入Java.use()来获得一个js类,还可以通过name对枚举的类进行过滤
onComplete:function(){}
枚举完所有类之后被调用,用来做一些完成后的收尾工作

Java.enumerateLoadedClassesSync():无参数,方法返回所有已经加载的类的数组。

function Hook6(){Java.perform(function(){console.log("Frida Test Hook6");// 枚举所有类console.log("枚举所有类");Java.enumerateLoadedClasses({onMatch: function(name){console.log(name);// 这里可以添加过滤逻辑用来过滤我们关注的类//if(name.indexOf("cn.gemini.k.fridatest") != -1){//  console.log(name);//}},onComplete: function(){}});// 打印类中的所有方法console.log("打印类中的所有方法");var clszz = Java.use("cn.gemini.k.fridatest.FridaHook1");var methods = clszz.class.getDeclaredMethods(); // 获取类中的所有方法可使用反射获得console.log(methods);});
}

运行结果

λ python load2.py
Frida Test Hook6
枚举所有类
[.....] // 一大堆的系统类,可以添加过滤逻辑过滤一下
[Landroid.content.pm.ProviderInfo;
androidx.core.app.CoreComponentFactory$CompatWrapped
androidx.core.app.CoreComponentFactory
打印类中的所有方法
public static int cn.gemini.k.fridatest.FridaHook1.func3_verify_static(java.lang.String),public int cn.gemini.k.fridatest.FridaHook1.abc(),public int cn.gemini.k.fridatest.FridaHook1.func1_add(int,int),public int cn.gemini.k.fridatest.FridaHook1.func2_add_overload(int,int),public int cn.gemini.k.fridatest.FridaHook1.func2_add_overload(int,int,int)

3.7.hook类中所有成员方法

如果类中有静态方法,又有重载方法的话下面代码会报错,没有静态方法则可以hook类中的成员方法。

function Hook7(){Java.perform(function(){console.log("Frida Test Hook7");var clszz = Java.use("cn.gemini.k.fridatest.FridaHook1");// 先枚举类的所有方法var methods = clszz.class.getDeclaredMethods();for(var i = 0; i < methods.length; i++){var methodName = methods[i].getName(); // 获取到每个方法的名字console.log(methodName);console.log(clszz[methodName].overloads.length);// 重载方法的处理for(var j = 0; j < clszz[methodName].overloads.length; j++){clszz[methodName].overloads[j].implementation = function(){for(var k = 0;k < arguments.length; k++){console.log(this + " arg"+ k + ":" + arguments[k]);}return this[methodName].apply(this, arguments);}}}});
}

3.8.实例化类对象/修改类字段

一个类的字段包括静态字段和非静态字段。
静态字段的访问可以直接通过 类.字段名.value = “XXX” 的方式进行修改。

非静态字段则需要先拿到对象实例才能修改,获取对象实例可使用Java.choose()。同时还需要注意非静态字段又分为有同名方法的字段和无同名方法的字段。

  • 对于有同名方法的字段,在访问时需要在字段名前面加一个下划线"_"才能访问。
  • 对于无同名方法的字段,则直接可以访问。
    对象实例化则通过$new方法即可创建一个新的对象。
    js代码
function Hook8(){Java.perform(function(){console.log("Frida Test Hook8");var clazz = Java.use("cn.gemini.k.fridatest.FridaHook1");// 修改类中的静态字段console.log("修改前静态字段的值:" + clazz.password.value);clazz.password.value = "9"; // 静态字段的修改console.log("修改后静态字段的值:" + clazz.password.value);// 实例化类对象var newcls = clazz.$new();  // 通过$new方法对类进行实例化console.log("实例化一个类对象"+newcls)console.log("修改前的字段值: abc=="+newcls._abc.value+" cde=="+newcls.cde.value);// 修改类中的非静态字段Java.choose("cn.gemini.k.fridatest.FridaHook1",{onMatch: function(obj){obj.cde.value = 100;    // 非静态字段修改方式obj._abc.value = 200;   // 非静态字段修改:这里需要注意因为类中存在一个同名的方法,所以访问该字段时需要加个下划线"_"console.log("修改后的字段值: abc=="+obj._abc.value+" cde=="+obj.cde.value);},onComplete: function(){}});});
}

执行结果

λ python load2.py
Frida Test Hook8
修改前静态字段的值:88888888
修改后静态字段的值:9
实例化一个类对象cn.gemini.k.fridatest.FridaHook1@a675c28
修改前的字段值: abc==10 cde==20
修改后的字段值: abc==200 cde==100

3.9.frida方法主动调用

frida的方法主动调用,主要分以下几种情况
1.frida主动调用Java类中的静态方法,也就是使用static关键字声明的。
2.frida主动调用对象的Java成员方法,通过对象才能调用的方法,非static方法。

  • 方法一:创建一个新对象完成主动调用
  • 方法二:搜索内存中已有对象完成主动调用(推荐使用内存中原有的对象,因为内存中的对象才是应用真实的应用使用的对象,自己创建对象的数据可能与应用当时实际使用的数据不一致,一般协议分析会存在上面的情况(那么问题来了,如果内存中有多个对象该如何区分哪个是我们要的对象呢?),如果只是单纯使用对象方法的功能那么一般问题不大)

3.frida主动调用so中的方法。
对so方法的直接调用需要用到frida的NativeFunction方法,方法原型如下:
NativeFunction(address, returnType, argTypes[, abi])
1)address:要hook的方法地址
2)returnType:返回值类型
3)argTypes[, abi]: 参数类型 这里参数可以是多个
js代码

function Hook9(){Java.perform(function(){console.log("Frida Test Hook9");// 主动调用类静态方法var clszz = Java.use("cn.gemini.k.fridatest.FridaHook1");clszz.func3_verify_static(">>>pwd<<<");// 主动调用类成员方法// 第一种方式:创建一个新对象完成主动调用var obj = clszz.$new();var ret = obj.func2_add_overload(11,22);console.log("返回值: " + ret);// 第二种方式:搜索内存中已有对象完成主动调用Java.choose("cn.gemini.k.fridatest.FridaHook1",{onMatch: function(instance){console.log("found instance :"+ instance);console.log("返回值: "+ instance.func2_add_overload(33,44));},onComplete: function(){console.log("Search Completed!");}})// 主动调用so的native方法var str_name_so = "libnative-lib.so";    //要hook的so名var str_name_func = "JNI_Frida_Test";    //要hook的方法名// 获取方法地址var addr_func = Module.findExportByName(str_name_so , str_name_func);console.log("func addr is ---" + addr_func);//定义NativeFunction 等下要调用var func_JNI_Frida_Test = new NativeFunction(addr_func,"void",[]);func_JNI_Frida_Test();});
}

文章转载于: https://blog.csdn.net/weixin_46734340/article/details/117401345

Frida之安装和使用教程相关推荐

  1. mysql数据库解压安装教程_MySQL数据库之windows 10下解压版MySql安装配置方法教程...

    本文主要向大家介绍了MySQL数据库之windows 10下解压版MySql安装配置方法教程 ,通过具体的内容向大家展现,希望对大家学习MySQL数据库有所帮助. windows 10 下安装解压版的 ...

  2. codeblocks安装及使用教程(手把手傻瓜式教学)

    1.首先去codeblocks官网下载最新版codeblocks安装包. 2.双击下载的包,看到如下界面. 3.点击next,进行下一步. 4.点击I agree. 5.点击next,进行下一步. 6 ...

  3. linux_OEL5.4_安装Oracle11g中文教程图解

    一.安装ORACLE10g 软件(11.2.0.0) 参考pdf:链接:http://pan.baidu.com/s/1pLHU94J 密码:keo8 (一)安装前的包支持 1. 虚拟机yum 环境搭 ...

  4. 安装phpMyAdmin图文教程

    phpmyadmin的安装配置已经是老生常谈的话题了,网络上到处都可以找到相关的配置教程.但是,那些大多都是手动配置的,稍不留神,容易出错.因此站长今天在这里介绍的是,被很多phpmyadmin用户所 ...

  5. mysql超详细教程_MySQL8.0.23安装超详细教程

    前言 最近在做一个人脸识别的项目,需要用数据库保存学生信息与前段交互. MySQL的优点 1.mysql性能卓越,服务稳定,很少出现异常宕机. 2.mysql开放源代码且无版权制约,自主性及使用成本低 ...

  6. TensorRT安装及使用教程

    常见网络tensorrt c++版实现: https://github.com/wang-xinyu/tensorrtx 参考:TensorRT安装及使用教程_ZONGXP的博客-CSDN博客_ten ...

  7. 六丶青龙的依赖库+拉库+互助安装及使用教程

    六丶青龙的依赖库+互助安装及使用教程 没有服务器的先自行购买,这里推荐腾讯云2H4G8M首年70–点击购买 青龙面板安装教程 QQ交流:1014549449 --------------点击跳转 青龙 ...

  8. python详细安装教程3.7.4-python 3.7.4 安装 opencv的教程

    明确一下,我们需要使用python来调用opencv中的库函数,所以需要安装opencv-python. 主要需要安装: 1. opencv-python 2. numpy 第一步先来安装opencv ...

  9. winpython使用教程-Win系统PyQt5安装和使用教程

    安装PyQt5: 安装流程如下: 1.PyQt5下载界面中提供了win32,win64,linux,macos等系统的下载版本,这里我选择的是PyQt5-5.10.1-5.10.1-cp35.cp36 ...

最新文章

  1. Ubuntu 中Eclipse 默认的OpenJDK 和 SUNJDK问题总结
  2. 多变量逻辑回归python_Python实现逻辑回归(Logistic Regression in Python)
  3. netty实现简单时事通讯_简单的Java实现Netty进行通信
  4. c/c++ 实参、形参、值传递、地址传递的区别与关系 ---- 授人以鱼不如授人以渔
  5. 触摸工业光掩模行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)
  6. Android Studio 中 gradle 下载失败的解决办法
  7. 使用协同过滤推荐算法进行电影推荐
  8. Windows 控制台注册表工具 Reg
  9. 第三方直播美颜SDK的美颜功能是怎么实现的?
  10. java 几分钟前几小时前几天前后转化为时间
  11. 微信小程序(四) 节点查询 | wx.createSelectorQuery
  12. 超级简单!编译的C++生成的exe文件发给别人,直接在其他电脑运行超级简单!亲测有效!
  13. 安卓软件远程连接ConnectBot v1.8.6
  14. 多元线性回归预测房价
  15. 解压压缩包报错:tar: invalid tar magic
  16. 通俗学AI(3):VGG套路得人心
  17. Python游戏开发,pygame模块,Python实现超级玛丽100%真实版
  18. Tanner Pro集成电路设计与布局实战指导
  19. 以排印为本,从内容出发
  20. 远程时间同步在分布式测控与实时仿真系统应用

热门文章

  1. 自定义注解事务课程回顾
  2. mongodb java连接 集群_java连接mongodb集群
  3. permission denied和linux赋值权限chmod命令
  4. MQTT.fx连接aliyun阿里云的方法
  5. jar包不统一也会报错:Exception in thread main java.lang.NoClassDefFoundError
  6. 国际主流云厂商生存画像:三大赛道愈发清晰
  7. 【转载】我心目中最好的框架组合是
  8. 2016网络安全***赛记录
  9. android120 zhihuibeijing 开机页面
  10. HDU 4332 Constructing Chimney [状态压缩+矩阵]