精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

qiling框架和AFLplusplus安裝

蛇矛實驗室 ? 來源:蛇矛實驗室 ? 2023-06-17 16:12 ? 次閱讀

背景

Qiling Framework是一個基于Python的二進制分析、模擬和虛擬化框架。它可以用于動態分析和仿真運行不同操作系統、處理器和體系結構下的二進制文件。除此之外,Qiling框架還提供了易于使用的API和插件系統,方便使用者進行二進制分析和漏洞挖掘等工作。其創始人是一名IoT Hacker,創建qiling的初衷便是解決在研究IoT時遇到的種種問題,這也是為什么上一小節說qiling框架比unicorn框架更加適合IoT研究初學者。

qiling使用基礎

qiling框架和AFLplusplus安裝

sudo apt-getupdate
sudo apt-getinstall -ybuild-essential python3-dev automake cmake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools cargo libgtk-3-dev
sudo apt-getinstall -ylld-14llvm-14llvm-14-dev clang-14
sudo apt-getinstall -ygcc-$(gcc --version|head -n1|sed 's/..*//'|sed 's/.* //')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/..*//'|sed 's/.* //')-dev
pip3 install qiling
git clone https://github.com/AFLplusplus/AFLplusplus
make-C AFLplusplus
cdAFLplusplus/unicorn_mode
./build_unicorn_support.sh

程序仿真

首先我們需要克隆qiling倉庫,倉庫中一些實例腳本可供我們學習。

git clone --recurse-submodules https://github.com/qilingframework/qiling.git

一個簡單的示例:

#include
#include
# gcc test.c -o test
# 注意:編譯程序的主機libc需要與rootfs glibc版本(libc-2.7.so)相對應,其他架構同理
intmain(){
printf("hello world!");
return0;
}

使用qiling編寫一個簡單的仿真腳本。

fromqiling import*
fromqiling.const importQL_VERBOSE
# 導入qiling模塊和qiling.const模塊中的QL_VERBOSE常量

if__name__ == "__main__":
#創建Qiling對象,實例中三個參數分別為:path(仿真程序路徑)、rootfs(仿真程序文件系統目錄)和verbose(輸出信息參數),除此外還可以設置env和log_plain參數。
ql = Qiling(["./x8664_linux_symlink/test"], "./x8664_linux_symlink",verbose=QL_VERBOSE.DEBUG)
#運行Qiling對象的run()方法,開始執行仿真程序
ql.run()

這里的verbose(輸出信息參數)有如下級別及其作用:

477b47ba-0cda-11ee-962d-dac502259ad0.png

4790d562-0cda-11ee-962d-dac502259ad0.png

476b1642-0cda-11ee-962d-dac502259ad0.png

VFS劫持

x86_fetch_urandom程序的作用為打開/dev/urandom文件,生成隨機數。當qiling仿真x86_fetch_urandom程序時,環境需要用到仿真文件系統,我們就需要用到VFS劫持,這樣就可以模擬修改文件系統。下面的代碼中為仿真虛擬路徑 "/dev/urandom" 會被映射到宿主系統上的現有"/dev/urandom"文件。當模擬程序將訪問 /dev/random 時,將改為訪問映射文件。

fromqiling importQiling

if__name__ == "__main__":
ql = Qiling(["x86_linux/bin/x86_fetch_urandom"], "x86_linux")

ql.add_fs_mapper(r'/dev/urandom', r'/dev/urandom')
ql.verbose=0
ql.run()

47bebe8c-0cda-11ee-962d-dac502259ad0.png

如果我們想要控制虛擬文件'/dev/urandom'的交互結果,可以繼承QlFsMappedObject類,并可自定義read、write、fstat、ioctl、readline等方法。

fromqiling importQiling
fromqiling.os.mapper importQlFsMappedObject

classFakeUrandom(QlFsMappedObject):
defread(self, size: int)-> bytes:
returnb"x01"#可以修改讀取返回結果

deffstat(self)-> int:
return-1

defclose(self)-> int:
return0

if__name__ == "__main__":
ql = Qiling(["x86_linux/bin/x86_fetch_urandom"], "x86_linux")
ql.add_fs_mapper(r'/dev/urandom', FakeUrandom())
ql.run()

47d14d90-0cda-11ee-962d-dac502259ad0.png

476b1642-0cda-11ee-962d-dac502259ad0.png

函數hook

下面示例中,我們給str1和str2倆個變量內存中分別復制"abcdef"和"ABCDEF"字符串。正常執行完畢后會打印出"str1 大于 str2"。我們可以使用qiling框架劫持strcmp實現為hook strcmp函數的效果,使其執行到不同分支的結果。

#include
#include

//cd ./x8664_linux/
//gcc demo.c -o test

intmain()
{
charstr1[15];
charstr2[15];
intret;

strcpy(str1, "abcdef");
strcpy(str2, "ABCDEF");
ret = strcmp(str1, str2);

if(ret < 0)
??{
?????printf("str1 小于 str2");
??}
??else?if(ret > 0)
{
printf("str1 大于 str2");
}
else
{
printf("str1 等于 str2");
}
return(0);
}

以下代碼為hook strcmp函數,并通過修改rax寄存器改變執行流程。

fromqiling import*
fromqiling.const import*

# 自定義strcmp hook函數。當程序執行strcmp函數退出時,會調用此函數,并且在比較完畢后,將 rax 寄存器的值修改為 0,表示相等。
defhook_strcmp(ql,*args):
# qiling框架的寄存器取值為ql.arch.reg.xxx
rax = ql.arch.regs.rax
print("hook_addr_rax:",hex(rax))
ql.arch.regs.eax = 0# 0:等于; -1:小于 ;1:大于

# 使用 ql.os.set_api 函數為 strcmp 設置hook函數,第一個參數為要hook的函數名,第二個參數為自定義hook函數,第三個參數為hook類型,這里為退出時觸發hook函數。
defhook_func(ql):
ql.os.set_api('strcmp',hook_strcmp,QL_INTERCEPT.EXIT) # 也可以使用ql.hook_address()函數進行hook,使用方法為ql.hook_address(hook_strcmp,0xXXXXXXXX)

if__name__ == "__main__":
ql = Qiling(["./x8664_linux/test"],"./x8664_linux",verbose=QL_VERBOSE.DEBUG)
hook_func(ql)
#ql.debugger = "gdb12345"
ql.run()

47e98ae0-0cda-11ee-962d-dac502259ad0.png

定義hook函數時hook類型參數有以下三種:

480bf166-0cda-11ee-962d-dac502259ad0.png

qiling使用實例


476b1642-0cda-11ee-962d-dac502259ad0.png

使用qiling解密CTF賽題

當我們掌握了最基礎的三個用法后,我們可以測試一個簡單的例子來加深對qiling框架的理解。以上一小節中unicorn解密ctf題目為例,我們先簡單寫一個運行腳本。這里的ql.debugger="gdb12345"為開啟gdbserver服務,我們可以使用ida或者gdb進行調試。

4828b8a0-0cda-11ee-962d-dac502259ad0.png

簡單運行后發現程序和上一小節中unicorn的運行狀況類似。由于這里我設置了multithead為True,所以這里會比上一小節中unicorn的解密速度快不少。但是還是在有限時間內只輸出4個字符。

484b07e8-0cda-11ee-962d-dac502259ad0.png

當我們將verbose設置為QL_VERBOSE.DISASM便可觀察模擬執行的匯編指令,根據匯編指令我們明顯看到程序在call 0x400670處進行了遞歸調用(或使用調試器調試查看),導致解密時間非常長。所以我們需要進行代碼優化,思路為使用棧空間來保存一個不同輸入參數以及對應計算結果的字典來避免重復計算。

487b222a-0cda-11ee-962d-dac502259ad0.png

這里qiling由于是由unicorn開發而來,所以很多用法和unicorn相似。

fromqiling import*
fromqiling.const import*
frompwn import*

defhook_start(ql):
arg0 = ql.arch.regs.rdi
r_rsi = ql.arch.regs.rsi
arg1 = u32(ql.mem.read(r_rsi,4))
if(arg0,arg1) indirect:
(ret_rax,ret_ref) = direct[(arg0,arg1)]
ql.arch.regs.rax = ret_rax
ql.mem.write(r_rsi,p32(ret_ref))
ql.arch.regs.rip = 0x400582
else:
ql.arch.stack_push(r_rsi)
ql.arch.stack_push(arg1)
ql.arch.stack_push(arg0)

defhook_end(ql):
arg0 = ql.arch.stack_pop()
arg1 = ql.arch.stack_pop()
r_rsi = ql.arch.stack_pop()
ret_rax = ql.arch.regs.rax
ret_ref = u32(ql.mem.read(r_rsi,4))
direct[(arg0,arg1)] = (ret_rax,ret_ref)

defsolve(ql):
start_address = 0x400670
end_address = 0x4006f1
end_address2 = 0x400709
ql.hook_address(hook_start,start_address)
ql.hook_address(hook_end,end_address)
ql.hook_address(hook_end,end_address2)

if__name__ == '__main__':
path = ["./x8664_linux_symlink/test"]
rootfs = "./x8664_linux_symlink"
direct = {}
ql = Qiling(path, rootfs,verbose=QL_VERBOSE.DEFAULT)
solve(ql)
ql.run()

運行后便會打印出解密結果。

489af708-0cda-11ee-962d-dac502259ad0.png

除了上一小節中的ctf題目掌握qiling的使用外,我們還可通過qilinglab來加深對qiling框架的使用。qilingLab是由11個小挑戰組成的二進制程序,用來幫助新手快速熟悉和掌握 Qiling 框架的基本用法。官方提供了aarch64程序的解題方法,我們根據這個作為參考解密一下x86_64架構的練習程序。

x86_64程序下載(https://www.shielder.com/attachments/qilinglab-x86_64)

首先運行程序,給我們提示,challenges會造成程序崩潰,只有當我們解出相應challenge后才會顯示信息。

48d36e30-0cda-11ee-962d-dac502259ad0.png

我們可以通過ida逆向以及編寫qiling腳本進行動態調試來完成這些challenge。

48f78a5e-0cda-11ee-962d-dac502259ad0.png

最終的解密腳本如下:

fromqiling import*
frompwn import*
fromqiling.const import*
fromqiling.os.mapper importQlFsMappedObject
importos
importstruct

defhook_cpuid(ql, address, size):
ifql.mem.read(address, size) == b'x0FxA2':
regs = ql.arch.regs
regs.ebx = 0x696C6951
regs.ecx = 0x614C676E
regs.edx = 0x20202062
regs.rip += 2

defchallenge11(ql):
begin, end = 0, 0
forinfo inql.mem.map_info:
#print("=====")
#print(info)
#print("=====")
ifinfo[2] == 5and'qilinglab-x86_64'ininfo[3]:
begin, end = info[:2]
#print("begin_addr",begin)
#print("end_addr",end)
ql.hook_code(hook_cpuid, begin=begin, end=end)

classcmdline(QlFsMappedObject):
defread(self, expected_len):
returnb'qilinglab'

defclose(self):
return0

defchallenge10(ql):
ql.add_fs_mapper('/proc/self/cmdline', cmdline())


defhook_tolower(ql):
return0

defchallenge9(ql):
ql.os.set_api('tolower', hook_tolower)

deffind_and_patch(ql, *args, **kw):
MAGIC = 0x3DFCD6EA00000539
magic_addrs = ql.mem.search(p64(MAGIC))
#print("magic_address:",hex(magic_addrs))

formagic_addr inmagic_addrs:
malloc1_addr = magic_addr - 8
malloc1_data = ql.mem.read(malloc1_addr, 24)
string_addr, _ , check_addr = struct.unpack("QQQ",malloc1_data)

ifql.mem.string(string_addr) == "Random data":
ql.mem.write(check_addr, b"x01")
break

defchallenge8(ql):
base_addr = ql.mem.get_lib_base(os.path.split(ql.path)[-1])
#print("base_addr",hex(base_addr))
ql.hook_address(find_and_patch, base_addr+0xFB5)

defhook_sleep(ql):
return0

defchallenge7(ql):
ql.os.set_api('sleep',hook_sleep)

defhook_rax(ql):
ql.arch.regs.rax = 0

defchallenge6(ql):
base_addr = ql.mem.get_lib_base(os.path.split(ql.path)[-1])
#print("base_addr",hex(base_addr))
hook_addr = base_addr + 0xF16
ql.hook_address(hook_rax, hook_addr)

defhook_rand(ql):
ql.arch.regs.rax = 0

defchallenge5(ql):
ql.os.set_api('rand',hook_rand)

defenter_forbidden_loop_hook(ql):
ql.arch.regs.eax = 1

defchallenge4(ql):
base = ql.mem.get_lib_base(os.path.split(ql.path)[-1])
hook_addr = base + 0xE43
print("qiling binary hookaddr:",hex(hook_addr))
ql.hook_address(enter_forbidden_loop_hook, hook_addr)

classFakeUrandom(QlFsMappedObject):
defread(self, size: int)-> bytes:
ifsize == 1:
returnb"x42"
else:
returnb"x41"* size

defclose(self)-> int:
return0

defhook_getrandom(ql, buf, buflen, flags):
ifbuflen == 32:
data = b'x41'* buflen # b'x41' = A
ql.mem.write(buf, data)
ql.os.set_syscall_return(buflen)
else:
ql.os.set_syscall_return(-1)

defchallenge3(ql):
ql.add_fs_mapper(r'/dev/urandom', FakeUrandom())
ql.os.set_syscall("getrandom", hook_getrandom)

defmy_uname_on_exit_hook(ql, *args):
rdi = ql.arch.regs.rdi
print(f"utsname address: {hex(rdi)}") 
ql.mem.write(rdi, b'QilingOSx00')
ql.mem.write(rdi + 65* 3, b'ChallengeStartx00')

defchallenge2(ql):
ql.os.set_api("uname", my_uname_on_exit_hook, QL_INTERCEPT.EXIT)

defchallenge1(ql):
ql.mem.map(0x1000, 0x1000, info='challenge1')
ql.mem.write(0x1337, p16(1337))

if__name__ == '__main__':
path = ["./x8664_linux/qilinglab-x86_64"]
rootfs = "./x8664_linux"
ql = Qiling(path, rootfs,verbose=QL_VERBOSE.OFF)
challenge1(ql)
challenge2(ql)
challenge3(ql)
challenge4(ql)
challenge5(ql)
challenge6(ql)
challenge7(ql)
challenge8(ql)
challenge9(ql)
challenge10(ql)
challenge11(ql)
#ql.debugger = "gdb12345"
ql.run()

運行后,所有的challenge都會顯示SOLVED。

49143f78-0cda-11ee-962d-dac502259ad0.png

476b1642-0cda-11ee-962d-dac502259ad0.png

qiling設備仿真

qiling提供了路由器仿真案例,該腳本路徑為qiling/example路徑下

#!/usr/bin/env python3
# 1. Download AC15 Firmware from https://down.tenda.com.cn/uploadfile/AC15/US_AC15V1.0BR_V15.03.05.19_multi_TD01.zip
# 2. unzip
# 3. binwalk -e US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin
# 4. locate squashfs-root
# 5. rm -rf webroot && mv webroot_ro webroot
#
# notes: we are using rootfs in this example, so rootfs = squashfs-root
# 
importos, socket, threading
importsys
sys.path.append("../../../")
fromqiling importQiling
# 從qiling.const中導入QL_VERBOSE,指定qiling的日志輸出級別
fromqiling.const importQL_VERBOSE


# 定義patcher函數,用于跳過網卡信息檢測。在前面小節我們仿真tenda路由器時,路由器httpd程序在初始化網絡時會檢查網卡名稱是否為br0。這里腳本直接將代碼執行前內存中的br0字符串替換成了lo,從而跳過檢查。
defpatcher(ql: Qiling):
br0_addr = ql.mem.search("br0".encode() + b'x00')
foraddr inbr0_addr:
ql.mem.write(addr, b'lox00')

# 定義nvram_listener函數,使用該函數監聽Unix套接字,并在收到消息時返回數據。
defnvram_listener():
server_address = 'rootfs/var/cfm_socket'
data = ""
try:
os.unlink(server_address)
exceptOSError:
ifos.path.exists(server_address):
raise

sock = socket.socket(socket.AF_UNIX,socket.SOCK_STREAM)
sock.bind(server_address)
sock.listen(1)
whileTrue:
connection, _ = sock.accept()
try:
whileTrue:
data += str(connection.recv(1024))
if"lan.webiplansslen"indata:
connection.send('192.168.170.169'.encode())
else:
break
data = ""
finally:
connection.close()

# 定義myvfork函數,仿真程序在執行系統調用vfork時被調用,返回值0。
defmyvfork(ql: Qiling):
regreturn = 0
ql.log.info("vfork() = %d"% regreturn)
returnregreturn
# 仿真主函數,生成qiling實例和添加VFS映射。
defmy_sandbox(path, rootfs):
print("path:",path)
print("rootfs",rootfs)
ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG)
print("ql:",ql)
ql.add_fs_mapper("/dev/urandom","/dev/urandom")
ql.hook_address(patcher, ql.loader.elf_entry)
ql.debugger = False
ifql.debugger == True:
ql.os.set_syscall("vfork", myvfork) # vfork函數返回0時,debugger可正常調試。
ql.run()

if__name__ == "__main__":
# 創建后臺運行的線程并執行,以便收到Unix套接字的消息時進行響應。
nvram_listener_therad = threading.Thread(target=nvram_listener, daemon=True)
nvram_listener_therad.start()
# 運行仿真實例
my_sandbox(["rootfs/bin/httpd"], "rootfs")

當我們運行腳本后,會顯示路由器的ip和端口,當我們發現本地的8080正在監聽時,說明設備已經仿真成功。

4946e194-0cda-11ee-962d-dac502259ad0.png

仿真成功后可訪問http://localhost:8080查看效果:

49677b0c-0cda-11ee-962d-dac502259ad0.png

在后面的小節中,我們會學習對仿真路由器設備進行fuzz。其中最為重要的一步便是編寫仿真腳本,后續在我們分析好固件程序中要fuzz地址范圍后,只有仿真設備可以順利觸發保存快照的功能,才可保證fuzz的正確性。

qiling fuzz

qiling框架可以使用AFLplusplus對arm架構程序進行fuzz測試,測試代碼如下:

#include
#include
#include
// Program that will crash easily.
#defineSIZE (10)

intfun(inti)
{
char*buf = malloc(SIZE);
charbuf2[SIZE];

while((*buf = getc(stdin)) == 'A')
{
buf[i++] = *buf;
}
strncpy(buf2, buf, i);
puts(buf2);
return0;
}

intmain(intargc, char**argv)
{
returnfun(argc);
}

qiling提供的fuzz腳本如下:

#!/usr/bin/env python3
"""
Simple example of how to use Qiling together with AFLplusplus. 
This is tested with the recent Qiling framework (the one you cloned),
afl++ from https://github.com/AFLplusplus/AFLplusplus

After building afl++, make sure you install `unicorn_mode/setup_unicorn.sh`

Then, run this file using afl++ unicorn mode with
afl-fuzz -i ./afl_inputs -o ./afl_outputs -m none -U -- python3 ./fuzz_x8664_linux.py @@
"""

# No more need for importing unicornafl, try ql.afl_fuzz instead!

importsys, os
frombinascii importhexlify
sys.path.append("../../..")
fromqiling import*
fromqiling.extensions importpipe
fromqiling.extensions.afl importql_afl_fuzz

defmain(input_file, enable_trace=False):
ql = Qiling(["./arm_fuzz"], "../../rootfs/arm_qnx", console=enable_trace)
# 設置ql的標準輸入為進程的標準輸入
ql.os.stdin = pipe.SimpleInStream(sys.stdin.fileno())
# 如果沒有啟用控制臺追蹤,則將標準輸出和標準錯誤流設置為Null
ifnotenable_trace:
ql.os.stdout = pipe.NullOutStream(sys.stdout.fileno())
ql.os.stderr = pipe.NullOutStream(sys.stderr.fileno())

defplace_input_callback(ql: Qiling, input: bytes, _: int):
# 設置fuzz輸入點 
ql.os.stdin.write(input)
returnTrue

defstart_afl(_ql: Qiling):
# 設置fuzz實例
ql_afl_fuzz(_ql, input_file=input_file, place_input_callback=place_input_callback, exits=[ql.os.exit_point])

# 獲取libc的基地址
LIBC_BASE = int(ql.profile.get("OS32", "interp_address"), 16)
# 設置hook函數,用于處理SignalKill信號
ql.hook_address(callback=lambdax: os.abort(), address=LIBC_BASE + 0x38170)
# main函數地址
main_addr = 0x08048aa0
# 設置hook函數,在main函數運行時調用start_afl函數
ql.hook_address(callback=start_afl, address=main_addr)

# 若啟用控制臺追蹤,則將設置相關信息輸出
ifenable_trace:
# The following lines are only for `-t` debug output
md = ql.arch.disassembler
count = [0]
defspaced_hex(data):
returnb' '.join(hexlify(data)[i:i+2] fori inrange(0, len(hexlify(data)), 2)).decode('utf-8')

defdisasm(count, ql, address, size):
buf = ql.mem.read(address, size)
try:
fori inmd.disasm(buf, address):
return"{:08X}	{:08X}: {:24s} {:10s} {:16s}".format(count[0], i.address, spaced_hex(buf), i.mnemonic,
i.op_str)
except:
importtraceback
print(traceback.format_exc())

deftrace_cb(ql, address, size, count):
rtn = '{:100s}'.format(disasm(count, ql, address, size))
print(rtn)
count[0] += 1

ql.hook_code(trace_cb, count)

# okay, ready to roll.
# try:
ql.run()
# except Exception as ex:
# # Probable unicorn memory error. Treat as crash.
# print(ex)
# os.abort()
os._exit(0) # that's a looot faster than tidying up.


if__name__ == "__main__":
iflen(sys.argv) == 1:
raiseValueError("No input file provided.")
iflen(sys.argv) > 2andsys.argv[1] == "-t":
main(sys.argv[2], enable_trace=True)
else:
main(sys.argv[1])

AFLplusplus執行腳本如下:

#!/usr/bin/sh
AFL_AUTORESUME=1 AFL_PATH="$(realpath ../../../AFLplusplus)"PATH="$AFL_PATH:$PATH"afl-fuzz -i afl_inputs -o afl_outputs -U -- python3 ./fuzz_arm_qnx.py @@

運行后fuzz.sh后,便會出現afl++ 運行界面,等待幾秒后便出現crash。

498c501c-0cda-11ee-962d-dac502259ad0.png

crash的變異數據存放在afl_outputs目錄下,我們可以使用xxd id:000000,xxxxxx命令查看變異數據。

#xxdid:000000,sig:06,src:000000,time:4112,execs:1077,op:havoc,rep:8
00000000: 4141 4141 4141 4141 4141 4141 ff7f4241 AAAAAAAAAAAA..BA
00000010: 4141 4145 4141 be414dff0000 0041 4141 AAAEAA.AM....AAA
00000020: 41

責任編輯:彭菁

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 框架
    +關注

    關注

    0

    文章

    396

    瀏覽量

    17269
  • 虛擬化
    +關注

    關注

    1

    文章

    355

    瀏覽量

    29671
  • IOT
    IOT
    +關注

    關注

    186

    文章

    4097

    瀏覽量

    195081

原文標題:物聯網安全之qiling框架初探

文章出處:【微信號:蛇矛實驗室,微信公眾號:蛇矛實驗室】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    labview視覺通用平臺框架源代碼

    本帖最后由 1281788316 于 2024-4-19 13:52 編輯 labview通用視覺軟件框架,機器視覺通用框架 通用視覺框架源代碼。可以參考用于開發常規案例。里面有部分函數,用戶
    發表于 08-03 16:57

    [資料分享]+Android框架揭秘

    `[資料分享]+Android框架揭秘一、看威武霸氣的封面作者:(韓)金泰延等 二、讀讀簡介,看看適合你嗎? 《android框架揭秘》通過對android系統源代碼的分析,主要介紹android
    發表于 09-26 09:47

    Py之TFCudaCudnn:Win10下安裝深度學習框架Tensorflow+Cuda+Cudnn最簡單最快捷最詳細攻略

    Py之TFCudaCudnn:Win10下安裝深度學習框架Tensorflow+Cuda+Cudnn最簡單最快捷最詳細攻略
    發表于 12-20 10:35

    Yaf框架安裝和HelloWorld

    Yaf框架:從安裝到HelloWorld
    發表于 06-28 09:29

    什么是框架?為什么要有框架

    前言什么是框架?程序框架其實就類似一個文件大綱或者模板。因為寫程序就和類似于寫文章,如果沒有大綱或者模板那么你寫起來就會比較費勁。而。為什么要有框架?節約時間,減少錯誤。因為對于一種類型的程序它們
    發表于 11-09 07:38

    在 ubuntu 上安裝騰訊推理框架 ncnn 的方法記錄

    本教程詳細記錄了在 ubuntu 上安裝騰訊推理框架 ncnn 的方法。
    發表于 12-14 07:49

    在arduino ide中安裝esp32的arduino框架時的問題求解

    在arduino ide中安裝esp32的arduino框架時的問題在arduino ide中安裝esp32的arduino框架: arduino ide彈出錯誤:
    發表于 03-06 08:22

    堡盟新型框架適配器——通過兩次卡入安裝傳感器

    堡盟新型框架適配器——通過兩次卡入安裝傳感
    發表于 01-08 09:34 ?726次閱讀

    SHARC音頻模塊:如何安裝和配置裸機框架以便使用

    詳解介紹如何安裝和配置裸機框架,以獲得成功的使用體驗。
    的頭像 發表于 06-27 06:07 ?2831次閱讀
    SHARC音頻模塊:如何<b class='flag-5'>安裝</b>和配置裸機<b class='flag-5'>框架</b>以便使用

    電流互感器安裝圖解

    電流互感器一般安裝在成套配電柜、金屬構架上,也可安裝在母線穿過墻壁或樓板處。電流互感器可直接用基礎螺栓固定在墻壁或樓板上,或者用角鋼做成矩形框架埋入墻壁或樓板中,將與框架同樣大小的鐵板
    發表于 06-25 17:32 ?1.9w次閱讀

    cartography框架安裝與建圖測試

    cartgrapher這個框架是google在2016年開源出來的框架,該框架可以接入2D激光、3D激光、里程計、IMU傳感器的數據,輸出2D地圖或者是3D地圖。同時該框架還有一個比較
    的頭像 發表于 08-10 11:43 ?1344次閱讀

    qiling fuzz iot設備測試及實例分析

    qiling和AFL++環境的搭建在前面的小節中已經說過,這里就不再演示。
    的頭像 發表于 07-22 09:05 ?1568次閱讀
    <b class='flag-5'>qiling</b> fuzz iot設備測試及實例分析

    labview通用視覺框架參考

    labview通用視覺軟件框架,機器視覺通用框架 通用視覺框架源代碼。可以參考用于開發常規案例。里面有部分函數,用戶也可隨意編輯函數,開發速度超級快。打開前需要先安裝labview、V
    發表于 08-04 15:58 ?76次下載

    基于Python 輕量級ORM框架

    重量級,僅僅為了使用Django的ORM框架的功能,而安裝Django有點導致系統臃腫。而peewee這個框架語法幾乎與Django的ORM框架一致,而又非常輕量。 它的
    的頭像 發表于 11-01 11:17 ?504次閱讀
    基于Python 輕量級ORM<b class='flag-5'>框架</b>

    搭建ssm框架的詳細流程

    有效地集成和利用這些開源框架,提高開發效率,并且具有良好的可擴展性和可維護性。 本文將詳細介紹搭建SSM框架的流程,包括環境搭建、創建項目、配置框架和測試等步驟。 一、環境搭建 首先,我們需要確保電腦已經
    的頭像 發表于 12-03 14:52 ?2925次閱讀