Windows下x64反汇编参数传递约定

一句话,调用顺序为从左到右,  Function( rcx, rdx, r8,r9, [rsp+0x20], [rsp+0x28], [rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], [rsp+0x50], [rsp+0x58], [rsp+0x60] ...)

注:本文资料收集于互联网。

x64 体系结构

x64 体系结构是 x86 的向后兼容扩展。 它提供与 x86 相同的旧 32 位模式,以及新的 64 位模式。

术语"x64"包括 AMD 64 和 Intel64。 指令集接近相同。

寄存 器

x64 将 x86 的 8 个常规用途寄存器扩展为 64 位,并添加了 8 个新的 64 位寄存器。 64 位寄存器的名称以"r"开头,因此例如, eax 的 64 位扩展名为 rax。 新寄存器的名称为 r8 到 r15

每个寄存器的低 32 位、16 位和 8 位可直接在操作数中处理。 这包括寄存器,如 esi,其低 8 位以前不可处理。 下表为 64 位寄存器的下半部分指定汇编语言名称。

64 位寄存器 低 32 位 低 16 位 低 8 位

rax

eax

ax

rbx

ebx

bx

bl

rcx

ecx

残雪

Cl

rdx

edx

Dx

Dl

rsi

Esi

Sil

rdi

Edi

di

dil

rbp

Ebp

Bp

bpl

粒子

Esp

sp

Spl

r8

r8d

r8w

r8b

r9

r9d

r9w

r9b

r10

r10d

r10w

r10b

r11

r11d

r11w

r11b

r12

r12d

r12w

r12b

r13

r13d

r13w

r13b

r14

r14d

r14w

r14b

r15

r15d

r15w

r15b

输出到 32 位子注册的操作会自动零扩展为整个 64 位寄存器。 输出到 8 位或 16 位子注册的操作不是零扩展 (这是兼容的 x86 行为) 。

axbxcx 和 dx 的高 8 位仍可作为 ahbhchdh 进行地址处理,但不能用于所有类型的操作数。

指令指针、eip 和标志寄存器已分别扩展到 (和 rflags) 64 位。

x64 处理器还提供多组浮点寄存器:

  • 八个 80 位 x87 寄存器。

  • 八个 64 位 MMX 寄存器。 (与 x87 registers.)

  • 8 个 128 位 SSE 寄存器的原始集增加到 16 个。

调用约定

与 x86 不同,C/C++ 编译器仅支持 x64 上的一个调用约定。 此调用约定利用 x64 上可用的寄存器数增加:

  • 前四个整数或指针参数在 rcx、 rdx、 r8 和 r9 寄存器中传递。

  • 前四个浮点参数在前四个 SSE 寄存器 xmm0xmm3-中传递

  • 调用方在堆栈上为寄存器中传递的参数保留空间。 被调用的函数可以使用此空间将寄存器的内容溢出到堆栈。

  • 任何其他参数在堆栈上传递。

  • 在 rax 寄存器中返回整数或指针返回值,而浮点返回值在 xmm0 中返回

  • raxrcxrdxr8r11- 是可变的。

  • rbxrbprdi、rsir12r15- 是非易失性。

C++ 的调用约定非常相似:  指针作为隐式第一个参数传递。 接下来的三个参数在剩余的寄存器中传递,其余参数在堆栈上传递。

寻址模式

64 位模式下的寻址模式类似于 x86,但不完全相同。

  • 引用 64 位寄存器的说明以 64 位精度自动执行。 (例如 mov rax,[rbx] 从 rbx 开始将 8 个字节移动到 rax.)

  • 为 64 位即时常量或常量地址添加了一种特殊形式的 mov 指令。 对于所有其他指令,即时常量或常量地址仍为 32 位。

  • x64 提供新的 与元寻址相关的寻址模式。 引用单个常量地址的指令编码为从进行翻录的 偏移量。 例如,mov rax,[addr] 指令从 addrrip + 开始将 8 个字节移动到 rax

隐式引用指令指针的指令(如 jmp、 调用、 推送和 pop)和堆栈指针将它们视为 x64 上的 64 位寄存器。

在 Win64 下的 registers 用途

Register

Status

Use

RAX Volatile Return value register
RCX Volatile First integer argument
RDX Volatile Second integer argument
R8 Volatile Third integer argument
R9 Volatile Fourth integer argument
R10:R11 Volatile Must be preserved as needed by caller; used in syscall/sysret instructions
R12:R15 Nonvolatile Must be preserved by callee
RDI Nonvolatile Must be preserved by callee
RSI Nonvolatile Must be preserved by callee
RBX Nonvolatile Must be preserved by callee
RBP Nonvolatile May be used as a frame pointer; must be preserved by callee
RSP Nonvolatile Stack pointer
XMM0 Volatile First FP argument
XMM1 Volatile Second FP argument
XMM2 Volatile Third FP argument
XMM3 Volatile Fourth FP argument
XMM4:XMM5 Volatile Must be preserved as needed by caller
XMM6:XMM15 Nonvolatile Must be preserved as needed by callee.

1. 传递参数

在 Win64 里使用下面寄存器来传递参数:

  • rcx - 第 1 个参数
  • rdx - 第 2 个参数
  • r8 - 第 3 个参数
  • r9 - 第 4 个参数

其它多出来的参数通过 stack 传递。

使用下面寄存器来传递浮数数:

  • xmm0 - 第 1 个参数
  • xmm1 - 第 2 个参数
  • xmm2 - 第 3 个参数
  • xmm3 - 第 4 个参数

下面的代码:

void EditTextFile(HWND hEdit, LPCTSTR szFileName)
{
        HANDLE hFile;
        DWORD dwFileSize;
        DWORD dwFileSizeHigh;
        LPTSTR lpFileText;
        LPTSTR lpFileTextW;
        WORD wSignature;
        DWORD dwReadSize;

hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

... ...

}

CreateFile() 的参数有 7 个,那么看看 VC 是怎样安排参数传递:

void EditTextFile(HWND hEdit, LPCTSTR szFileName)
{
000000013F791570 40 56                push        rsi  
000000013F791572 41 54                push        r12  
000000013F791574 41 55                push        r13  
000000013F791576 48 83 EC 50          sub         rsp,50h  
000000013F79157A 48 8B C2             mov         rax,rdx  
        HANDLE hFile;
        DWORD dwFileSize;
        DWORD dwFileSizeHigh;
        LPTSTR lpFileText;
        LPTSTR lpFileTextW;
        WORD wSignature;
        DWORD dwReadSize;

hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
000000013F79157D 45 33 ED             xor         r13d,r13d  
000000013F791580 4C 8B E1             mov         r12,rcx  
000000013F791583 4C 89 6C 24 30       mov         qword ptr [rsp+30h],r13       // 第 7 个参数  
000000013F791588 45 8D 45 01          lea         r8d,[r13+1]                   // 第 3 个参数
000000013F79158C 45 33 C9             xor         r9d,r9d                       // 第 4 个参数
000000013F79158F BA 00 00 00 80       mov         edx,80000000h                 // 第 2 个参数
000000013F791594 48 8B C8             mov         rcx,rax                       // 第 1 个参数  
000000013F791597 C7 44 24 28 80 00 00 00 mov      dword ptr [rsp+28h],80h       // 第 6 个参数
000000013F79159F C7 44 24 20 03 00 00 00 mov      dword ptr [rsp+20h>],3         // 第 5 个参数
000000013F7915A7 FF 15 2B 0B 00 00    call        qword ptr [__imp_CreateFileW (13F7920D8h)]  
000000013F7915AD 48 8B F0             mov         rsi,rax

... ...

上面已经对 7 个参数的传递进行了标注,前 4 个参数通过 rcx,rdx,r8 以及 r9 寄存器传递,后 3 个参数确实通过 stack 传递。

可是,事情并没有这么简单:

在 Win64 下,会为每个参数保留一份用来传递的 stack 空间,以便回写 caller 的 stack

在上面的例子中:

  • [rsp+20h] - 第 5 个参数
  • [rsp+28h] - 第 6 个参数
  • [rsp+30h] - 第 7 个参数

实际上已经为前面 4 个参数保留了 stack 空间,分别是:

  • [rsp] - 第 1 个参数(使用 rcx 代替)
  • [rsp+08h] - 第 2 个参数(使用 rdx 代替)
  • [rsp+10h] - 第 3 个参数(使用 r8 代替)
  • [rsp+18h] - 第 4 个参数(使用 r9 代替)

虽然是使用了 registers 来传递参数,然而还是保留了 stack 空间。接下着就是 [rsp+20h][rsp+28h] 以及[rsp+30h] 对应的 4,5,个参数

2. 回写 caller stack

VC 使用了下面编译参数来实现回写 caller stack

/homeparams

当使用了这个编译选项或者在 Debug 版下,它强制将 registers 里的值写回 stack 中

正如下面的代码:

CreateFileWImplementation:
0000000076EC2A30 48 89 5C 24 08       mov         qword ptr [rsp+8],rbx             // 回写 caller stack
0000000076EC2A35 48 89 6C 24 10       mov         qword ptr [rsp+10h],rbp           // 回写 caller stack
0000000076EC2A3A 48 89 74 24 18       mov         qword ptr [rsp+18h],rsi           // 回写 caller stack
0000000076EC2A3F 57                   push        rdi  
0000000076EC2A40 48 83 EC 50          sub         rsp,50h  
0000000076EC2A44 8B DA                mov         ebx,edx  
0000000076EC2A46 48 8B F9             mov         rdi,rcx  
0000000076EC2A49 48 8B D1             mov         rdx,rcx  
0000000076EC2A4C 48 8D 4C 24 40       lea         rcx,[rsp+40h]  
0000000076EC2A51 49 8B F1             mov         rsi,r9  
0000000076EC2A54 41 8B E8             mov         ebp,r8d  
0000000076EC2A57 FF 15 33 A1 08 00    call        qword ptr [__imp_RtlInitUnicodeStringEx (76F4CB90h)]

上面所显示的是 CreateFile() 在 kernel32.dll 模块里的实现代码,上面对回写机制进行了标注,回写的 stack 正好是 caller 调用时未参数所保留的 stack 空间,上面的代码并不是那么直观。

下面我演示一下使用 /homeparams 选项来编译代码。

上面的 EditTextFile() 函数结果如下:

void EditTextFile(HWND hEdit, LPCTSTR szFileName)
{
000000013F6A15C0 48 89 54 24 10       mov         qword ptr [rsp+10h],rdx          // 第 2 个参数回写
000000013F6A15C5 48 89 4C 24 08       mov         qword ptr [rsp+8],rcx            // 第 1 个参数回写
000000013F6A15CA 56                   push        rsi  
000000013F6A15CB 41 54                push        r12  
000000013F6A15CD 48 83 EC 58          sub         rsp,58h  
 HANDLE hFile;
 DWORD dwFileSize;
 DWORD dwFileSizeHigh;
 LPTSTR lpFileText;
 LPTSTR lpFileTextW;
 WORD wSignature;
 DWORD dwReadSize;

hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
000000013F6A15D1 48 8B 4C 24 78       mov         rcx,qword ptr [szFileName]  
000000013F6A15D6 45 33 E4             xor         r12d,r12d  
000000013F6A15D9 45 33 C9             xor         r9d,r9d  
000000013F6A15DC 4C 89 64 24 30       mov         qword ptr [rsp+30h],r12  
000000013F6A15E1 45 8D 44 24 01       lea         r8d,[r12+1]  
000000013F6A15E6 BA 00 00 00 80       mov         edx,80000000h  
000000013F6A15EB C7 44 24 28 80 00 00 00 mov         dword ptr [rsp+28h],80h  
000000013F6A15F3 C7 44 24 20 03 00 00 00 mov         dword ptr [rsp+20h],3  
000000013F6A15FB FF 15 D7 1A 00 00    call        qword ptr [__imp_CreateFileW (13F6A30D8h)]

第 1 个参数回写 [rsp+8] 处,第 2 个参数回写 [rsp+10h] 处。

注意这里的 stack 就是对应 caller 调用时的 stack,经过调用后 [rsp] 是返回地址值,因此,在 callee 里设置:

  • callee 写 [rsp+8] = caller 的 [rsp]
  • callee 写 [rsp+10h] = caller 的 [rsp+8]
  • callee 的 [rsp] = return address

上面很直观地显示了使用 /homeparams 选项时的效果,对比前一段没有使用 /homeparams 选项编译时的结果,很容易发现这个机制。

回写 caller stack 机制目的是为了 Debug 所需。

3. 由 callee 保存

在一个程序里应尽量使用 registers,在 x64 里有 16 个通用寄存器和 16 个 xmm 寄存器,可是一些 registers 在使用前必须保存原来值,以防丢失原来值。

因此,在 callee 使用它们时会将原值压入栈中保存,在 Win64 里,下面 registers 由 callee 负责保存:

  • rbxrbprsirdi
  • r12 - r15
  • xmm6 - xmm15

每进入一个 callee,在使用它们之前都保存起来,返回 caller 之前,恢复原来值。因此这些寄存器的值是保持恒定的。

void EditTextFile(HWND hEdit, LPCTSTR szFileName)
{
000000013F9115C0 48 89 54 24 10       mov         qword ptr [rsp+10h],rdx  
000000013F9115C5 48 89 4C 24 08       mov         qword ptr [rsp+8],rcx  
000000013F9115CA 56                   push        rsi                     // 保存
000000013F9115CB 41 54                push        r12                     // 保存
000000013F9115CD 48 83 EC 58          sub         rsp,58h  
        HANDLE hFile;
        DWORD dwFileSize;

... ...

000000013F911708 48 83 C4 58          add         rsp,58h  
000000013F91170C 41 5C                pop         r12                    // 恢复
000000013F91170E 5E                   pop         rsi                    // 恢复
000000013F91170F C3                   ret

4. stack frame 结构

进入每个 callee 时,都会生成属于自己的 stack frame 结构,返回时会注销自己的 stack frame

  • rbp
  • rsp

由这两个 registers 来构造 stack frame 结构,rbp  stack frame pointerrsp 是 stack pointer

可是,在 Win64 里,似乎不使用 stack frame 结构,VC 不会为每个函数创建 stack frame 结构

在 Win64 里,始终在使用动态使用 rsp 来维护 stack

void EditTextFile(HWND hEdit, LPCTSTR szFileName)
{
000000013F9115C0 48 89 54 24 10       mov         qword ptr [rsp+10h],rdx  
000000013F9115C5 48 89 4C 24 08       mov         qword ptr [rsp+8],rcx  
000000013F9115CA 56                   push        rsi                    
000000013F9115CB 41 54                push        r12                     
000000013F9115CD 48 83 EC 58          sub         rsp,58h             // 为 callee 分配 stack
        HANDLE hFile;
        DWORD dwFileSize;

... ...

000000013F911708 48 83 C4 58          add         rsp,58h            // 注销 callee stack 结构
000000013F91170C 41 5C                pop         r12                    
000000013F91170E 5E                   pop         rsi                   
000000013F91170F C3

VC 不会生成 x86 下典型的 stack frame 结构,始终由 rsp 维护 stack,/Gd 编译选项在 Win64 下会被忽略,rbp 被保留起来

在 Win64 里,rdi 寄存器的角色变得很微妙,在某些场合下它充当了一部分 stack frame pointer 的角色。

5. r11 与 rcx 以及 r10

在 64 位模式下,在 sysret 指令返回时,将从 rcx 处得到返回地址,从 r11 处得到 rflags 值,因此在进入 system services routine(系统服务例程)前,或者在系统服务例程中的第1个任务是 rcx 与 r11 寄存器,以便 sysret 返回。

在 Win64 里,r10 寄存器充当保存 rcx 值的作用,如下:

NtCallbackReturn:
00000000770FFDA0 4C 8B D1             mov         r10,rcx  
00000000770FFDA3 B8 02 00 00 00       mov         eax,2  
00000000770FFDA8 0F 05                syscall  
00000000770FFDAA C3                   ret

在进入 system call 之前,保存 rcx 的值。

x86:又名 x32 ,表示 Intel x86 架构,即 Intel 的32位 80386 汇编指令集。

x64:表示 AMD64 和 Intel 的 EM64T ,而不包括 IA64 。至于三者间的区别,可自行搜索。

x64 跟 x86 相比寄存器的变化,如图:

从图上可以看到,X64架构相对于X86架构的主要变化,是将原来所有的寄存器都扩大了一倍,例如EAX现在扩充成RAX,同时,又新增加了从R8~R15这8个64位的寄位器,有点RISC的味道(RISC特点就是寄存器多)。

然后还有下面的一些改变:

  • x64上面默认的函数调用约定是 fast call ,也就是 ABI 是 fast call ;
  • 一个函数在调用时,前四个参数是从左至右依次存放于RCX、RDX、R8、R9寄存器里面,剩下的参数从左至右顺序入栈;
  • 调用者负责在栈上分配32字节的“shadow space”,用于存放那四个存放调用参数的寄存器的值(亦即前四个调用参数);
  • 小于64位(bit)的参数传递时高位并不填充零(例如只传递ecx),大于64位需要按照地址传递;
  • 被调用函数的返回值是整数时,则返回值会被存放于RAX;
  • 被调用函数不负责清栈,调用者负责清理栈;
  • RAX,RCX,RDX,R8,R9,R10,R11是“易挥发”的,不用特别保护,其余寄存器需要保护。(x86下只有eax, ecx, edx是易挥发的)
  • 栈需要16字节对齐,“call”指令会入栈一个8字节的返回值(注:即函数调用前原来的RIP指令寄存器的值),这样一来,栈就对不齐了(因为RCX、RDX、R8、R9四个寄存器刚好是32个字节,是16字节对齐的,现在多出来了8个字节)。所以,所有非叶子结点调用的函数,都必须调整栈RSP的地址为16n+8,来使栈对齐。
  • 对于 R8~R15 寄存器,我们可以使用 r8, r8d, r8w, r8b 分别代表 r8 寄存器的64位、低32位、低16位和低8位。

一些其他要注意的小问题:

  • 另外一些小问题要注意,AMD64不支持 push 32bit 寄存器的指令,最好的方法就是 push 和 pop 都用64位寄存器,即 push rbx ,不要使用 push ebx 。
  • 另外要补充的一点是,在一般情况下,X64 平台的 RBP 栈基指针被废弃掉,只作为普通寄存器来用,所有的栈操作都通过 RSP 指针来完成。

遗留问题

  以上都是关于 Windows 上的调用约定,即 Visual Studio 上使用的调用约定,至于 GCC 的函数调用约定是否一致,还不清楚,有知道的请指点一下,我从 asmlib 的64位汇编看,GCC 好像第一个参数用的是 rdi ,而不是 rcx 。

示例:

; 示例代码 1.asm; 语法:GoASMDATA SECTIONtext     db 'Hello x64!',caption  db 'My First x64 Application',CODE SECTIONSTART:sub rsp, 28h           ; 堆栈预留 shadow space (40 + 8)字节xor r9d, r9d           ; r9lea r8, caption        ; r8lea rdx, text          ; rdxxor rcx, rcx           ; rcxcall MessageBoxAadd rsp, 28h           ; 调用者自己恢复堆栈ret

一般编译器实现调用调用约定无外乎以下这几种:

  • CDECL:C/C++默认的调用约定,调用方平栈,不定参数的函数可以使用,参数通过堆栈传递.
  • STDCALL:被调方平栈,不定参数的函数无法使用,参数默认全部通过堆栈传递.
  • FASTCALL32:被调方平栈,不定参数的函数无法使用,前两个参数放入(ECX, EDX),剩下的参数压栈保存.
  • FASTCALL64:被调方平栈,不定参数的函数无法使用,前四个参数放入(RCX, RDX, R8, R9),剩下的参数压栈保存.
  • System V:类Linux系统默认约定,前八个参数放入(RDI,RSI, RDX, RCX, R8, R9),剩下的参数压栈保存.

当栈顶指针esp小于栈底指针ebp时,就形成了栈帧,栈帧中可以寻址的数据有局部变量,函数返回地址,函数参数等。不同的两次函数调用,所形成的栈帧也不相同,当由一个函数进入另一个函数时,就会针对调用的函数开辟出其所需的栈空间,形成此函数的独有栈帧,而当调用结束时,则清除掉它所使用的栈空间,关闭栈帧,该过程通俗的讲叫做栈平衡。而如果栈在使用结束后没有恢复或过度恢复,则会造成栈的上溢或下溢,给程序带来致命错误。

cdecl 调用者平栈: cdecl是C/C++默认调用约定,该调用方式在函数内不进行任何平衡参数操作,而是在退出函数后对esp执行加4操作,从而实现栈平衡。

该约定会采用复写传播优化,将每次参数平衡的操作进行归并,在函数结束后一次性平衡栈顶指针esp,且不定参数函数可使用此约定。

stdcall 被调用者平栈: stdcall与cdecl只在参数平衡上有所不同,其余部分都一样,但该约定不定参数函数无法使用。

cdecl调用方式的函数在同一作用域内多次被调用,会在效率上比stdcall高一些,因为它可以使用复写传播优化,而stdcall在函数内平衡栈,无法使用复写传播优化。

fastcall 被调用者平栈: fastcall效率最高,它可利用寄存器传递参数,一般前两个或前四个参数用寄存器传递,其余参数传递则转换为栈传递,此约定不定参数函数无法使用。

对于32位来说使用ecx,edx传递前两个参数,后面的用堆栈传递。
对于64位则会使用RCX,RDX,R8,R9传递前四个参数,后面的用堆栈传递。

使用esp寻址: 在O2编译器选项中,为了提高程序执行效率,只要栈顶是稳定的,就可以不再使用ebp指针,而是利用esp指针直接访问局部变量,这样可节省一个寄存器资源。

Windows下x64反汇编参数传递约定,一句话,调用顺序为从左到右, Function( rcx, rdx, r8,r9, [rsp+0x20], [rsp+0x28], [rsp+0x30]..相关推荐

  1. 将一个5X5的矩阵中最大的元素放在中心, 4个角分别放4个最小的元素(顺序为从左到右,从上到下,从小到大存放)其余数字从小到大

    将一个5X5的矩阵中最大的元素放在中心, 4个角分别放4个最小的元素(顺序为从左到右,从上到下,从小到大存放) 其余数字从小到大 在以前的要求上更改了一下,其余数字从小到大排序 #include &l ...

  2. php 调用memcache,Windows下的Memcache安装(php调用)

    Windows下的Memcache安装: 1. 下载memcache的windows稳定版,解压放某个盘下面,比如在c:\memcached 2. 在终端(也即cmd命令界面)下输入 'c:\memc ...

  3. Windows下查看dll被哪个进程调用

    转载博客菜鸟leihttp://www.cnblogs.com/leipei2352/archive/2013/02/05/2892482.html 卸载程序,结果没卸载干净---程序的安装目录中还剩 ...

  4. windows下bat文件一直循环一句话如何解决

    最近用GMT画图的,一开始没事,但是后面一直出现bug: 可以看到一直重复一句话,把gmt卸了重装也没用,后来发现了问题所在: 我有一个叫gmt.bat的文件,所以每次调用别的bat文件运行时,默认先 ...

  5. 解决windows下C32ASM反汇编无法打开的问题

    无意间发现C32ASM的启动和Server服务有关,刚好今天我看了网上的win7系统优化的文章, 也就按照上面的方法优化系统,由于server服务和共享有关,于是把server服务也禁用了, 后来我又 ...

  6. WIndows下AppAche服务中调试php页面出现警告:Call to undefined function mysql_connect()

    今天在windows server 2003上调试PHP源码的时候,遇到php连接mysql时的错误:Call to undefined function mysql_connect(): 现总结如下 ...

  7. windows下SecureCRT无法使用backspace(空格键)和上下左右键

    在使用SecureCRT登陆liunx(我的为CenterOS)系统,发现删除(backspace)键.和上下左右键不起作用,郁闷了很久没有找到解决办法, 今天终于看到了一篇有用的文章,在此记录一下! ...

  8. x64 汇编 参数传递

    参数传递在不同的系统上是不一样的 称作 calling convention 调用约定 windows rcx,rdx,r8,r9 用来存储整数或指针参数,按照从左到右的顺序 xmm0,1,2,3 用 ...

  9. windows下python如何安装模块或包? How to install package or module in windows OS when using PYTHON?

    摘要:本文介绍了在windows下利用cmd安装第三方模块或包的方法. 更新20170531:作为小白,发现使用setup.py安装并不是万能的,找到了使用pip安装的方法,步骤为配置好环境变量后(参 ...

最新文章

  1. 在vue中使用vuex,修改state的值示例
  2. Object-C时间与字符串的转化 因多语言设置中造成返回Nil的解决方法
  3. mysql两台服务器怎么做数据同步_两台mysql服务器实现双机互备配置并测试数据同步...
  4. ORM SQLAlchemy 简介
  5. 使用模块化工具Rollup打包自己开发的JS库
  6. oracle--索引--
  7. 设计模式系列 - 装饰器模式
  8. Atitit.jsou html转换纯文本 java c# php
  9. 用ABAP编程破解世界上最难数独游戏
  10. 中国行政区划编码-省市县镇村
  11. visual studio code打不开
  12. 私人问卷收集系统-Surveyking问卷收集系统
  13. 数学之美 吴军 读书笔记
  14. 38.DevOps之基于Jenkins实现的CI与CD
  15. ubuntu teamviewer
  16. Thinkcmf QQ邮箱配置
  17. 由《编程之美》想到的
  18. Javafx+MySQL 学生成绩管理系统
  19. Failed to import package with error: Couldn't decompress package的解决方案
  20. Both setBehindContentView must be called in onCreate in addition to setContentView.

热门文章

  1. vc++实现内核级进程保护
  2. python爬虫— 利用js2xml 获取 script 数据
  3. Python基础入门:(一)从变量到异常处理 --阿里云天池
  4. PEST分析顺丰服务需求_顺丰内外部环境分析.doc
  5. [转载]关于sql连接语句中的Integrated Security=SSPI
  6. 易车的第三个十年不好走
  7. Win10系统下怎么开启管理员administrator权限?
  8. c word to html 走样,打印机打印效果走样解决办法.pptx
  9. Ol4网格生成以及优化
  10. IDEA中两中默认背景颜色的RGB