黑客24小时在线接单网站

黑客在线接单,网站入侵,渗透测试,渗透网站,入侵网站

*** 安全编程:远程线程编程

Windows操作系统下,为了避免各个进程相互影响,每个进程地址空间都是被隔离的。所谓 “远程线程”,它不是跨计算机的,而是跨过程的。简单地说,这是过程A要在进程B创建一个线程叫做远程线程。

远程线程被木马、插件和其他程序广泛使用,远程线程技术在反病毒软件中是不可或缺的。技术应用的两面性取决于他们自己的个人行为意识。良性的技术学习对他们的生活发展非常有益。即使没有好处,至少也不会给自己带来不必要的麻烦。

本文介绍了远程线程知识的三个例子,即DLL远程注入卸载DLL和不依赖DLL注入代码。

1. DLL远程注入

木马或病毒的编写取决于它的隐藏程度,而不是它的功能。木马和病毒都是可执行的程序。EXE如果是文件,在运行过程中会产生一个过程,很容易被发现。为了不被发现,在编写木马或病毒时马或病毒时编写它DLL文件。DLL文件的运行不会单独创建一个过程,其运行被加载到过程的地址空间中,因此其隐蔽性相对较好。DLL如果文件不被过程加载,如何在过程的地址空间中运行? *** 是强制加载过程DLL文件到其地址空间,这种强制手段是现在要介绍的远程线程。

创建远程线程函数CreateRemoteThread()定义如下:

  • HANDLECreateRemoteThread(
  • HANDLEhProcess,
  • LPSECURITY_ATTRIBUTESlpThreadAttributes,
  • DWORDdwStackSize,
  • LPTHREAD_START_ROUTINElpStartAddress,
  • LPVOIDlpParameter,
  • DWORDdwCreationFlags,
  • LPDWORDlpThreadId
  • );
  • 该函数的功能是创建一个远程的线程。我们把CreateThread()函数和CreateRemoteThread对函数进行比较。CreateThread()函数方面,CreateRem oteThread()函数比其多一个函数hProcess参数是指定要创建线程的过程句柄。CreateThread()函数内容的实现取决于CreateRemoteThread()函数完成。CreateThread()函数代码实现如下:

  • /*
  • *@implemented
  • */
  • HANDLE
  • WINAPI
  • CreateThread(LPSECURITY_ATTRIBUTESlpThreadAttributes,
  • DWORDdwStackSize,
  • LPTHREAD_START_ROUTINElpStartAddress,
  • LPVOIDlpParameter,
  • DWORDdwCreationFlags,
  • LPDWORDlpThreadId)
  • {
  • /*创建远程线程
  • returnCreateRemoteThread(NtCurrentProcess(),
  • lpThreadAttributes,
  • dwStackSize,
  • lpStartAddress,
  • lpParameter,
  • dwCreationFlags,
  • lpThreadId);
  • }
  • 在上述代码中,NtGetCurrentProcess()函数的功能是获得当前过程的句柄。

    CreateRemoteThread()函数用于为其他过程创建线程。之一个参数是指定一个过程的句柄,使用获取过程的句柄API函数OpenProcess()需要提供函数PID作为参数。

    除了hProcess除了参数,剩余的关键参数只是lpStartAddress和lpParameter两个了。lpStartAddress指定线程函数的地址,lpParameter指定传递给线程函数的参数。上述每个过程的地址空间都是隔离的,因此新创建的线程函数的地址也应该在目标过程中,而不是调用CreateRemoteThread()函数的过程。同样,传递给线程函数的参数也应该在目标过程中。

    如何让线程函数的地址在目标过程中?如何将线程函数的参数传输到目标过程中?在讨论这个问题之前,首先考虑线程函数的功能。这里的主要功能是注入一个DLL当文件到达目标时,线程函数的功能是加载DLL文件。加载DLL使用文件LoadLibrary()函数。LoadLibrary()函数的定义:

  • HMODULELoadLibrary(
  • LPCTSTRlpFileName
  • );
  • 具体如下:
  • DWORDWINAPIThreadProc(
  • LPVOIDlpParameter
  • );
  • 通过比较两个函数,我们可以发现函数格式是相同的,除了函数的返回值类型和参数类型。这里只考虑相同的部分。由于函数格式相同,首先调用协议相同WINAPI(也就是__stdcall *** );其次,函数数量相同,只有一个。然后,你可以直接把它拿走。LoadLibrary()将函数作为线程函数创建到指定的过程中。LoadLibrary()参数需要加载DLL只要文件的完整路径在CreateRemoteThread()函数中赋值的指向DLL文件完整路径的指针LoadLibrary()函数即可。这样使用。CreateRemoteThread()函数可以创建远程线程。然而,还有两个问题没有解决。首先是如何解决LoadLibrary()将函数的地址放入目标过程空间CreateRemoteThread()调用,其次是传递LoadLibrary()函数的参数也需要在目标过程空间中通过CreateRemoteThread()函数指定给LoadLibrary()函数。

    首先,如何解决之一个问题LoadLibrary()将函数的地址放入目标过程空间。LoadLibrary()函数在系统中Kernel32.dll导出函数,Kernel32.dll这个DLL文件在任何进程中的加载位置都是相同的,也就是说,LoadLibrary()函数的地址在任何过程中都是一样的。因此,只要在过程中获得LoadLibrary()函数的地址也可以在目标过程中使用。CreateRemoteThread()函数的线程地址参数直接传输LoadLibrary()函数地址。

    第二个问题是如何加载DLL在目标过程中写入文件的完整路径。这需要帮助。WriteProcessMemory()函数的定义如下:

  • BOOLWriteProcessMemory(
  • HANDLEhProcess,//handletoprocess
  • LPVOIDlpBaseAddress,//baseofmemoryarea
  • LPVOIDlpBuffer,//databuffer
  • DWORDnSize,//numberofbytestowrite
  • LPDWORDlpNumberOfBytesWritten//numberofbyteswritten
  • );
  • 该函数的功能是把lpBuffer内容写在过程句柄上hProcess进程的lpBaseAddress写入长度为地址nSize。

    参数说明如下。

    hProcess:该参数是指定过程的过程句柄。

    lpBaseAddress:该参数是指定写入目标过程内存的起始地址。

    lpBuffer:该参数是缓冲区的起始地址,应写入目标过程内存。

    nSize:该参数是指定写入目标内存的缓冲区的长度。

    lpNumberOfBytesWritten:该参数用于接收实际写入内容的长度。

    例如,在破解方面,该函数的功能非常强大,可以实现“内存补丁”;该函数可用于修改目标过程中指定的值(如游戏修改器可修改游戏中的钱、红、蓝等)。

    可以使用此函数DLL将文件的完整路径写入目标过程的内存地址,以便在目标过程中使用LoadLibrary()函数加载指定DLL文件。以上两个问题已经解决,第三个问题需要解决。WriteProcessMemory()函数的第二个参数是指定写入目标过程内存的缓冲区的起始地址。这个地址在目标过程中,那么目标过程中的地址在哪里呢?允许在目标过程中使用内存块DLL写文件的路径吗?

    如何确定应该解决的第三个问题?DLL将文件的完整路径写入目标过程的哪个地址。对于目标过程,用户不会提前准备一个地址来写入。用户所能做的就是在目标过程中申请一个内存,然后把它拿走DLL写入文件的路径,申请的内存空间中的文件路径。在目标过程中申请内存的函数是VirtualAllocEx()其定义如下:

  • LPVOIDVirtualAllocEx(
  • HANDLEhProcess,
  • LPVOIDlpAddress,
  • SIZE_TdwSize,
  • DWORDflAllocationType,
  • DWORDflProtect
  • );
  • VirtualAllocEx()函数参数说明如下。

    hProcess:该参数是指定过程的过程句柄。

    lpAddress:该参数是指在目标过程中申请内存的起始地址。

    dwSize:该参数是指在目标过程中申请内存的长度。

    flAllocationType:指定申请内存的状态类型。

    flProtect:指定申请内存的属性。

    该函数的返回值是在目标过程中申请的内存块的起始地址。

    至此,关于写一个DLL所有注入的知识都已经有了。现在开始写一个DLL注入工具的界面如图1所示。

    图1 DLL注入/卸载器

    该工具分别注入两个功能DLL注入卸载DLL。卸载注入DLL以后会介绍功能。界面需要输入两部分,之一部分需要注入DLL文件的完整路径(必须是完整路径),第二部分是过程的名称。

    首先,查看界面操作,代码如下:

  • voidCInjectDllDlg::OnBtnInject()
  • {
  • ///添加处理程序代码
  • charszDllName[MAX_PATH]={0};
  • charszProcessName[MAXBYTE]={0};
  • DWORDdwPid=0;
  • GetDlgItemText(IDC_EDIT_DLLFILE,szDllName,MAX_PATH);
  • GetDlgItemText(IDC_EDIT_PROCESSNAME,szProcessName,MAXBYTE);
  • //由进程名获得PID
  • dwPid=GetProcId(szProcessName);
  • //注入szDllName到dwPid
  • InjectDll(dwPid,szDllName);
  • }
  • 另外两个函数在代码中调用,之一个是通过程名获得的PID第二个用于函数DLL注入函数。GetProcId()函数代码如下:

  • DWORDCInjectDllDlg::GetProcId(char*szProcessName)
  • {
  • BOOLbRet;
  • PROCESSENTRY32pe32;
  • HANDLEhSnap;
  • hSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,NULL);
  • pe32.dwSize=sizeof(pe32);
  • bRet=Process32First(hSnap,&pe32);
  • while(bRet)
  • {
  • //strupr()函数将字符串转化为大写
  • if(lstrcmp(strupr(pe32.szExeFile),strupr(szProcessName))==0)
  • {
  • returnpe32.th32ProcessID;
  • }
  • bRet=Process32Next(hSnap,&pe32);
  • }
  • return0;
  • }
  • InjectDll()函数代码如下:

  • VOIDCInjectDllDlg::InjectDll(DWORDdwPid,char*szDllName)
  • {
  • if(dwPid==0||lstrlen(szDllName)==0)
  • {
  • return;
  • }
  • char*pFunName="LoadLibraryA";
  • //打开目标流程
  • HANDLEhProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwPid);
  • if(hProcess==NULL)
  • {
  • return;
  • }
  • //计算欲注入DLL文件完整路径的长度
  • intnDllLen=lstrlen(szDllName) sizeof(char);
  • ///在目标过程中申请一个长度nDllLen内存空间大小
  • PVOIDpDllAddr=VirtualAllocEx(hProcess,NULL,nDllLen,MEM_COMMIT,PAGE_READWRITE);
  • if(pDllAddr==NULL)
  • {
  • CloseHandle(hProcess);
  • return;
  • }
  • DWORDdwWriteNum=0;
  • ///注入欲望DLL在目标过程中申请的空间中写入文件的完整路径
  • WriteProcessMemory(hProcess,pDllAddr,szDllName,nDllLen,&dwWriteNum);
  • //获得LoadLibraryA()函数地址
  • FARPROCpFunAddr=GetProcAddress(GetModuleHandle("kernel32.dll"),pFunName);
  • //创建远程线程
  • HANDLEhThread=CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)pFunAddr,pDllAddr,0,NULL);
  • WaitForSingleObject(hThread,INFINITE);
  • CloseHandle(hThread);
  • CloseHandle(hProcess);
  • }
  • InjectDll()函数有 2 参数,即目标过程 ID 值和注入 DLL 文件的完整路径。代码中没有 LoadLibrary()函数的地址是 LoadLibraryA()函数地址。系统中没有 LoadLibrary()函数,有的只是 LoadLibraryA()和 LoadLibraryW()两个函数。这两个函数分别针对 ANSI 字符串和 UNICODE 字符串LoadLibrary()函数只是一个宏。在编写程序时,可以直接使用宏。如果你想获得 LoadLibrary()函数地址应明确指定为获取 LoadLibraryA()还是 LoadLibraryW()。

    LoadLibrary()宏定义如下:

  • #ifdefUNICODE
  • #defineLoadLibraryLoadLibraryW
  • #else
  • #defineLoadLibraryLoadLibraryA
  • #endif//!UNICODE
  • 只要涉及到字符串的函数,就会有相应的函数ANSI版本和UNICODE版本;其他不涉及字符串的函数,没有ANSI版本和UNICODE版本差异。

    为了测试DLL代码加载成功与否DllMain()函数中添加以下代码:

  • caseDLL_PROCESS_ATTACH:
  • {
  • MsgBox("!DLL_PROCESS_ATTACH!");
  • break;
  • }
  • 现在测试一下注入的效果,如图2和图3所示。

    图2 DLL注入成功提示的文件

    图3 在此过程中查看DLL列表确认装载成功

    在图2中,弹出对话框是DLL程序在DLL_PROCESS_ATTACH它出现了。它的过程是notepad.exe。从图2中可以看出,弹出提示框的标题处是notepad.exe过程路径。图3是用工具检查过程中加载的DLL通过注入工具注入文件列表过注入工具注入DLL文件已加载notepad.exe在过程空间中。

    如果要注入系统过程,由于过程权限的关系,无法成功注入。用于打开目标过程OpenProcess()函数,由于权限不足,无法打开过程并获得过程句柄。通过调整当前过程的权限,可以打开系统过程并获得过程句柄。Win8如果注入程序在更高版本上运行,则需要单击右键选择注入工具“作为管理员运行”可完成注入。

    2. 卸载注入DLL文件

    DLL如果如果应用于木马,危害很大。在这里注入卸载DLL注入卸载程序DLL程序的想法与注入的想法相同,代码的变化也很小。不同之处在于,当前的功能是卸载,而不是注入。

    DLL卸载使用的API函数是FreeLiabrary()其定义如下:

  • BOOLFreeLibrary(
  • HMODULEhModule//handletoDLLmodule
  • );
  • 该函数的参数是要卸载的模块的句柄。

    FreeLibrary()函数中使用的模块句柄可以通过Module32First()和Module32Next()获取两个函数。正在使用Module32First()和Module32Next()需要使用两个函数MODULEENTRY32模块的句柄保存在结构体中。MODULEENTRY32结构体的定义如下:

  • typedefstructtagMODULEENTRY32{
  • DWORDdwSize;
  • DWORDth32ModuleID;
  • DWORDth32ProcessID;
  • DWORDGlblcntUsage;
  • DWORDProccntUsage;
  • BYTE*modBaseAddr;
  • DWORDmodBaseSize;
  • HMODULEhModule;
  • TCHARszModule[MAX_MODULE_NAME32 1];
  • TCHARszExePath[MAX_PATH];
  • }MODULEENTRY32;
  • typedefMODULEENTRY32*PMODULEENTRY32;
  • 在结构体中hModule为模块句柄,szModule模块名称,szExePath是完整模块的名称(包括路径和模块名称)。

    在卸载远程过程中DLL模块的代码如下:

  • VOIDCInjectDllDlg::UnInjectDll(DWORDdwPid,char*szDllName)
  • {
  • if(dwPid==0||lstrlen(szDllName)==0)
  • {
  • return;
  • }
  • HANDLEhSnap=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,dwPid);
  • MODULEENTRY32me32;
  • me32.dwSize=sizeof(me32);
  • ////找到匹配的过程名称
  • BOOLbRet=Module32First(hSnap,&me32);
  • while(bRet)
  • {
  • if(lstrcmp(strupr(me32.szExePath),
  • strupr(szDllName))==0)
  • {
  • break;
  • }
  • bRet=Module32Next(hSnap,&me32);
  • }
  • CloseHandle(hSnap);
  • char*pFunName="FreeLibrary";
  • HANDLEhProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwPid);
  • if(hProcess==NULL)
  • {
  • return;
  • }
  • FARPROCpFunAddr=GetProcAddress(GetModuleHandle("kernel32.dll"),pFunName);
  • HANDLEhThread=CreateRemoteThread(hProcess,NULL,0,
  • (LPTHREAD_START_ROUTINE)pFunAddr,me32.hModule,0,NULL);
  • WaitForSingleObject(hThread,INFINITE);
  • CloseHandle(hThread);
  • CloseHandle(hProcess);
  • }
  • 在卸载远程过程中DLL实现代码比DLL注入的代码要简单,这里就不多介绍了。

    3. 无DLL的代码注入

    DLL文件的注入和卸载已经完成,整个注入和卸载过程实际上是一次执行远程线程LoadLibrary()函数或FreeLibrary()函数。远程线程装载一个DLL文件,通过DllMain()调用DLL注入具体的功能代码DLL后就可以让DLL做很多事情。你能不依赖它吗?DLL为了完成特定的功能,文件直接将要执行的代码写入目标过程?答案是肯定的。

    要在目标过程中完成某些功能,需要使用相关功能API函数,不同的API不同的函数实现DLL中。Kernel32.dll每个过程中文件的地址都是一样的,但不代表其他DLL每个过程中文件的地址都是一样的。这样,在目标过程中调用API必须使用函数LoadLibrary()函数和GetProcAddress()动态调用函数的每个函数API函数。使用你想要的API函数及API函数所在的DLL所有文件都包装在一个结构中,直接写入目标过程的空间中。同时,远程执行代码也直接写入目标过程的内存空间,最后调用CreateRemoteThread()函数可以运行。

    远程线程通过实现一个简单的例子弹出一个提示对话框,但没有帮助DLL。使用本程序API函数已经在前面介绍过了。根据前面的步骤面的步骤定义的,定义如下:

  • #defineSTRLEN20
  • typedefstruct_DATA
  • {
  • DWORDdwLoadLibrary;
  • DWORDdwGetProcAddress;
  • DWORDdwGetModuleHandle;
  • DWORDdwGetModuleFileName;
  • charUser32Dll[STRLEN];
  • charMessageBox[STRLEN];
  • charStr[STRLEN];
  • }DATA,*PDATA;
  • 该结构体中保存了LoadLibraryA()、GetProcAddress()、GetModuleHandle()和GetModu leFileName()四个API函数地址。这四个API函数都属于Kernel32.dll可在注入前获得导出函数。User32Dll中保存“User32.dll”因为MessageBoxA()函数是由的User32.dll导出函数。Str保存的是通过MessageBoxA()函数弹出的字符串。

    注入代码类似于上述注入代码,但需要在注入代码中定义结构变量并进行初始化,代码如下:

  • VOIDCNoDllInjectDlg::InjectCode(DWORDdwPid)
  • {
  • ///打开过程并获取过程句柄
  • HANDLEhProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwPid);
  • if(hProcess==NULL)
  • {
  • return;
  • }
  • DATAData={0};
  • //获取kernel32.dll相关导出函数
  • Data.dwLoadLibrary=(DWORD)GetProcAddress(
  • GetModuleHandle("kernel32.dll"),"LoadLibraryA");
  • Data.dwGetProcAddress=(DWORD)GetProcAddress(
  • GetModuleHandle("kernel32.dll"),"GetProcAddress");
  • Data.dwGetModuleHandle=(DWORD)GetProcAddress(
  • GetModuleHandle("kernel32.dll"),"GetModuleHandleA");
  • Data.dwGetModuleFileName=(DWORD)GetProcAddress(
  • GetModuleHandle("kernel32.dll"),"GetModuleFileNameA");
  • 其他需要的DLL和导出函数
  • lstrcpy(Data.User32Dll,"user32.dll");
  • lstrcpy(Data.MessageBox,"MessageBoxA");
  • //MessageBoxA()弹出字符串
  • lstrcpy(Data.Str,"InjectCode!!!");
  • ///在目标过程中申请空间
  • LPVOIDlpData=VirtualAllocEx(hProcess,NULL,sizeof(Data),
  • MEM_COMMIT|MEM_RELEASE,PAGE_READWRITE);
  • DWORDdwWriteNum=0;
  • WriteProcessMemory(hProcess,lpData,&Data,
  • sizeof(Data),&dwWriteNum);
  • ///用于保存代码的长度
  • DWORDdwFunSize=0x4000;
  • LPVOIDlpCode=VirtualAllocEx(hProcess,NULL,dwFunSize,
  • MEM_COMMIT,PAGE_EXECUTE_READWRITE);
  • WriteProcessMemory(hProcess,lpCode,&RemoteThreadProc,
  • dwFunSize,&dwWriteNum);
  • HANDLEhThread=CreateRemoteThread(hProcess,NULL,0
  • (LPTHREAD_START_ROUTINE)lpCode,lpData,0,NULL);
  • WaitForSingleObject(hThread,INFINITE);
  • CloseHandle(hThread);
  • CloseHandle(hProcess);
  • }
  • 除了初始化结构变量外,上述注入代码还将线程函数代码写入目标过程空间的内存中。线程函数的代码如下:

  • DWORDWINAPIRemoteThreadProc(LPVOIDlpParam)
  • {
  • PDATApData=(PDATA)lpParam;
  • //定义API函数原型
  • HMODULE(__stdcall*MyLoadLibrary)(LPCTSTR);
  • FARPROC(__stdcall*MyGetProcAddress)(HMODULE,LPCSTR);
  • HMODULE(__stdcall*MyGetModuleHandle)(LPCTSTR);
  • int(__stdcall*MyMessageBox)(HWND,LPCTSTR,LPCTSTR,UINT);
  • DWORD(__stdcall*MyGetModuleFileName)(HMODULE,LPTSTR,DWORD);
  • ///赋值每个函数地址
  • MyLoadLibrary=(HMODULE(__stdcall*)(LPCTSTR))
  • pData->dwLoadLibrary;
  • MyGetProcAddress=(FARPROC(__stdcall*)(HMODULE,LPCSTR))
  • pData->dwGetProcAddress;
  • MyGetModuleHandle=(HMODULE(__stdcall*)(LPCSTR))
  • pData->dwGetModuleHandle;
  • MyGetModuleFileName=(DWORD(__stdcall*)(HMODULE,LPTSTR,DWORD))
  • pData->dwGetModuleFileName;
  • //加载User32.dll
  • HMODULEhModule=MyLoadLibrary(pData->User32Dll);
  • //获得MessageBoxA函数的地址
  • MyMessageBox=(int(__stdcall*)(HWND,LPCTSTR,LPCTSTR,UINT))
  • MyGetProcAddress(hModule,pData->MessageBox);
  • charszModuleFileName[MAX_PATH]={0};
  • MyGetModuleFileName(NULL,szModuleFileName,MAX_PATH);
  • MyMessageBox(NULL,pData->Str,szModuleFileName,MB_OK);
  • return0;
  • }
  • 上面就是无DLL所有注入的代码,编译、连接和操作。启动记事本程序进行测试,但报错了。有什么问题吗?VC6默认编译是Debug在版本中,添加大量的调试信息。代码中不存在一些调试信息,而是其他信息DLL模块中。这样,当执行到调试相关代码时,就会访问不存在的代码DLL模块中的代码导致报错。

    使用上述代码Release编译连接的方式,然后可以正确执行,如图4所示。

    图4 Release成功注入编译 ***

    编译的Debug版也可以进行无DLL只是实现了一点点不同。

       
    • 评论列表:
    •  似暖i
       发布于 2022-06-06 01:21:21  回复该评论
    • 里呢?允许在目标过程中使用内存块DLL写文件的路径吗?如何确定应该解决的第三个问题?DLL将文件的完整路径写入目标过程的哪个地址。对于目标过程,用户不会提前准备一个地址来写入。用户所能做的就是在目标过程中申请一个内存,然后把它拿走DLL写入文件的路径,申
    •  语酌鸢栀
       发布于 2022-06-06 06:24:07  回复该评论
    • Parameter,DWORDdwCreationFlags,LPDWORDlpThreadId){/*创建远程线程returnCreateRemoteThread(NtCurrentProcess(),lpThreadAttrib
    •  掩吻樱甜
       发布于 2022-06-06 01:49:04  回复该评论
    • 递LoadLibrary()函数的参数也需要在目标过程空间中通过CreateRemoteThread()函数指定给LoadLibrary()函数。首先,如何解决第一个问题LoadLibrary()将函数的地址放入目标过程空间。LoadLibrary()
    •  囤梦旧竹
       发布于 2022-06-06 04:20:27  回复该评论
    • 能。这里的主要功能是注入一个DLL当文件到达目标时,线程函数的功能是加载DLL文件。加载DLL使用文件LoadLibrary()函数。LoadLibrary()函数的定义:HMODULELoadLibrary(LPC

    发表评论:

    Powered By

    Copyright Your WebSite.Some Rights Reserved.