Shellcode的编写

做病毒和做保护的人,对shellcode一定不陌生。shellcode不依赖环境,放到任何地方都可以执行的机器码。

shellcode编写注意事项:

1.不能有全局变量:因为shellcode不依赖环境

2.不能使用常量字符串:

首先先定义两个相同含义的字符串

在编写shellcode字符串的过程中,不能使用第一种,接下来看一下他的反汇编代码:

双引号字符串编译器会优先存放在常量区中,如果你要使用,现mov一遍,当你注入别的程序时,别的程序不一定有这样一个常量区;

而第二种看他的反汇编代码可以看到,第二种直接放到堆栈当中,不依赖外部环境。(所以一般看到这种东西,肯定不是正常人写程序)

3.不能使用系统调用:

PE文件有导出表和导入表的概念。

当你需要使用MessageBoxA这个函数时,编译器首先生成User32.dll -> MessageBoxA,之后会 call DWORD PTR[MessageBoxA的地址]

你将shellcode放到别的程序中,别的程序导入表可能不存在这个函数,所以你就无法进行调用。

LoadLibrary函数返回装载 DLL 库模块的实例句柄

GetProcAddress函数返回值是DLL中的输出函数地址

LoadLibrary和GetProcAddress都属于kernel32中的函数。若目标程序导入表还是没有这些函数,那这两个函数你也是无法使用的。

但是我们可以找到输出表而去找函数的地址。

nt!_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

nt!_PEB(部分)

+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void
+0x00c Ldr : Ptr32 _PEB_LDR_DATA
+0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
+0x014 SubSystemData : Ptr32 Void
+0x018 ProcessHeap : Ptr32 Void
+0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
+0x020 FastPebLockRoutine : Ptr32 Void
+0x024 FastPebUnlockRoutine : Ptr32 Void
+0x028 EnvironmentUpdateCount : Uint4B
+0x02c KernelCallbackTable : Ptr32 Void

FS:[0x00] 对应的是TEB

TEB结构体中 +0x30是PEB

所以FS:[0x30]对应的是PEB

Ldr : Ptr32 _PEB_LDR_DATA 这个东西是进程加载的模块链表

 

 

nt!_LDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY    加载顺序
   +0x008 InMemoryOrderLinks : _LIST_ENTRY 内存顺序
   +0x010 InInitializationOrderLinks : _LIST_ENTRY 初始化顺序
   +0x018 DllBase          : Ptr32 Void             //模块基地址
   +0x01c EntryPoint       : Ptr32 Void         //模块入口

   +0x020 SizeOfImage      : Uint4B     //模块影像大小 
   +0x024 FullDllName      : _UNICODE_STRING      //包含路径的模块名
   +0x02c BaseDllName      : _UNICODE_STRING    
   +0x034 Flags            : Uint4B
   +0x038 LoadCount        : Uint2B         //该模块的引用计数
   +0x03a TlsIndex         : Uint2B
   +0x03c HashLinks        : _LIST_ENTRY
   +0x03c SectionPointer   : Ptr32 Void

   +0x040 CheckSum         : Uint4B
   +0x044 TimeDateStamp    : Uint4B
   +0x044 LoadedImports    : Ptr32 Void
   +0x048 EntryPointActivationContext : Ptr32 Void
   +0x04c PatchInformation : Ptr32 Void

注意: +0x024 FullDllName      : _UNICODE_STRING     是Unicode编码:

 

找到PEB -> Ldr:

_asm
{
mov eax,fs:[0x30] //PEB
mov eax,[eax+0x0c] //Ldr
add eax,0x0c
mov pBeg,eax
mov eax,[eax]
mov pPLD,eax
}

遍历链表找到kernel32

while (pPLD != pBeg)
{
pLast = (WORD*)pPLD->BaseDLLName.Buffer;
pFirst = (WORD*)kernel32;
while (*pFirst && *pFirst == *pLast)
{
pFirst++, pLast++;
}
if (*pLast == *pFirst)
{
dwKernelBase = (DWORD)pPLD->DLLBase;
break;
}
}

再遍历kernel32的导出表找到GetProcAddress(LoadLibrary同理)

for (;dwCnt<pIED->NumberOfNames;dwCnt++)
{
pFinded = (char*)((DWORD)dwKernelBase + pAddOfofNames_Raw[dwCnt]);
while (*pFinded && *pFinded == *pSrc)//对比GetProcAddress字符串
{
pFinded++;
pSrc++;
}
if (*pFinded == *pSrc)//对比成功
{
pGetProcAddress = (PGETPROCADDRESS)((DWORD)dwKernelBase + pAddOffun_Raw[pAddOfOrd_Raw[dwCnt]]);
break;
}
pSrc = szGetProcAddr;

 

有了GetProcAddress函数,你就可以操控任何API函数了

 

写出shellcode中记得转换成硬编码,记得一定要注意文件中 IMAGE_SECTION_HEADER 中的DWORD Characteristics。

posted @ 2020-07-25 21:49  VIUS_Jcool  阅读(488)  评论(1编辑  收藏  举报