当前位置: 首页 > article >正文

代码注入

  1. 代码注入是一种向目标进程插入独立运行代码并使之运行的技术,它一般调用CreateRomoteThread() API以远程线程形式运行插入的代码,所以也被称为线程注入。
    在这里插入图片描述
  2. 首先向目标进程target.exe插入代码与数据,在此过程中,代码以线程过程(Thread Procedure)形式插入,而代码中使用的数据则以线程参数的形式传入,也就是说,代码与数据是分别注入的。

DLL注入与代码注入

  1. 下面这段代码用来弹出Windows消息框:
DWORD WINAPI ThreadProc(LPVOID lParam)
{MessageBoxA(NULL, "www.reversecore.com", "ReverseCore", MB_OK);return 0;
}
  1. 若想使用DLL注入技术,则需要把上述代码放入某个DLL文件,然后再将整个DLL文件注入目标进程,采用该技术完成注入后,运行OllyDbg调试器,查看上述ThreadProc()代码区域。
    在这里插入图片描述
  • 10009290与1000929C分别指向DLL数据节区中的字符串(“ReverseCore"、“www.reversecore.com”),上面2条PUSH指令将MessageBoxA() API中要使用的字符串地址存储到栈,1000100E地址处由一条CALL DWORD PTR DS:[100080F0]指令,该指令即使调用user32!MessageBoxA() API的命令,转到100080F0地址处查看。
    在这里插入图片描述
  • 100080F0地址就是DLL的IAT区域,在其上方可用看到其他API的地址,向这样,DLL代码中使用的所有数据均位于DLL的数据区域,采用DLL注入技术,整个DLL会被插入目标进程,代码与数据共存于内存,所以代码能够正常运行,以此不同,代码注入仅向目标进程注入必要的代码,要想使注入的代码正常运行,还必须将代码中使用的数据一同注入。
  1. 使用代码注入的原因
  • 占用内存少:如果要注入的代码与数据较少,那么就不需要将它们做成DLL的形式再注入,此时直接采用代码注入的方式同样能够获得与DLL注入相同的效果,且占用内存会更少。
  • 难以查找痕迹:采用DLL注入方式会在目标进程的内存中留下相关痕迹,很容易让人判断出目标进程是否被执行注入操作,但采用代码注入方式几乎不会留下任何痕迹。

例子

  1. 运行notepad.exe,然后使用Process Explorer查看notepad.exe进程的PID。
    在这里插入图片描述
  2. 运行如下命令
C:\Users\12586\Downloads\example\03\27\bin>CodeInjection.exe 1196
  • notepad.exe进程中弹出一个消息框。
    在这里插入图片描述
  1. CodeInjection.cpp
  • 看一下main()函数
int main(int argc, char *argv[])
{DWORD dwPID     = 0;if( argc != 2 ){printf("\n USAGE  : %s <pid>\n", argv[0]);return 1;}// change privilegeif( !SetPrivilege(SE_DEBUG_NAME, TRUE) )return 1;// code injectiondwPID = (DWORD)atol(argv[1]);InjectCode(dwPID);return 0;
}
  • main()函数用来调用InjectCode()函数,传入的函数参数为目标进程的PID。
  • ThreadProc()函数即为要注入目标进程的代码。
typedef struct _THREAD_PARAM 
{FARPROC pFunc[2];               // LoadLibraryA(), GetProcAddress()char    szBuf[4][128];          // "user32.dll", "MessageBoxA", "www.reversecore.com", "ReverseCore"
} THREAD_PARAM, *PTHREAD_PARAM;typedef HMODULE (WINAPI *PFLOADLIBRARYA)
(LPCSTR lpLibFileName
);typedef FARPROC (WINAPI *PFGETPROCADDRESS)
(HMODULE hModule,LPCSTR lpProcName
);typedef int (WINAPI *PFMESSAGEBOXA)
(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType
);DWORD WINAPI ThreadProc(LPVOID lParam)
{PTHREAD_PARAM   pParam      = (PTHREAD_PARAM)lParam;HMODULE         hMod        = NULL;FARPROC         pFunc       = NULL;// LoadLibrary()hMod = ((PFLOADLIBRARYA)pParam->pFunc[0])(pParam->szBuf[0]);    // "user32.dll"if( !hMod )return 1;// GetProcAddress()pFunc = (FARPROC)((PFGETPROCADDRESS)pParam->pFunc[1])(hMod, pParam->szBuf[1]);  // "MessageBoxA"if( !pFunc )return 1;// MessageBoxA()((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], MB_OK);return 0;
}
  • 稍微整理下:
hMod = LoadLibraryA("user32.dll");
pFunc = GetProcAddress(hMod, "MessageBoxA");
pFunc = (NULL,"www.revercore.com","ReverseCore",MB_OK);
  • 代码注入技术的核心内容是注入可独立运行的代码,为此,需要同时注入代码与数据,并且要保证代码能够准确引用注入的数据。
  • 上述代码中的ThreadProc()函数可以看到,函数中并未直接调用相关API,也未直接定义使用字符串,它们都是通过THREAD_PARAM结构体以线程参数的形式传递使用。
  • ThreadProc()函数中使用THREAD_PARAM结构体接收2个API地址与4个字符串数据,其中2个API分别为LoadLibraryA()与GetProcAddress(),只要有了这2个API,就能调用所有库函数。
00401000  /. 55             PUSH EBP
00401001  |. 8BEC           MOV EBP,ESP
00401003  |. 56             PUSH ESI
00401004  |. 8B75 08        MOV ESI,DWORD PTR SS:[EBP+8]; [EBP+8] = IParam = address of THREAD_PARAM
00401007  |. 8B0E           MOV ECX,DWORD PTR DS:[ESI]
00401009  |. 8D46 08        LEA EAX,DWORD PTR DS:[ESI+8] ; EAX = "user32.dll"
0040100C  |. 50             PUSH EAX
0040100D  |. FFD1           CALL ECX     ; kernel32.LoadLibraryA()
0040100F  |. 85C0           TEST EAX,EAX
00401011  |. 75 0A          JNZ SHORT 0040101D                       ;  0040101D
00401013  |> B8 01000000    MOV EAX,1
00401018  |. 5E             POP ESI
00401019  |. 5D             POP EBP
0040101A  |. C2 0400        RETN 4
0040101D  |> 8D96 88000000  LEA EDX,DWORD PTR DS:[ESI+88]
00401023  |. 52             PUSH EDX  ; EDX = "MessageBoxA"
00401024  |. 50             PUSH EAX  ; EAX = hMod
00401025  |. 8B46 04        MOV EAX,DWORD PTR DS:[ESI+4]
00401028  |. FFD0           CALL EAX ;kernel32.GetProcAddress
0040102A  |. 85C0           TEST EAX,EAX
0040102C  |.^74 E5          JE SHORT 00401013                        ;  00401013
0040102E  |. 6A 00          PUSH 0
00401030  |. 8D8E 88010000  LEA ECX,DWORD PTR DS:[ESI+188]
00401036  |. 51             PUSH ECX ; ECX = "ReverseCore"
00401037  |. 81C6 08010000  ADD ESI,108
0040103D  |. 56             PUSH ESI ; ESI = "www.reversecore.com"
0040103E  |. 6A 00          PUSH 0
00401040  |. FFD0           CALL EAX ;user32.MessageBoxA()
00401042  |. 33C0           XOR EAX,EAX
00401044  |. 5E             POP ESI
00401045  |. 5D             POP EBP
00401046  \. C2 0400        RETN 4
  • 所有重要数据都是从线程参数IParam[EBP+8]接收使用的,也就说,ThreadProc()函数是可以独立运行的代码。
  1. InjectCode()函数
BOOL InjectCode(DWORD dwPID)
{HMODULE         hMod            = NULL;THREAD_PARAM    param           = {0,};HANDLE          hProcess        = NULL;HANDLE          hThread         = NULL;LPVOID          pRemoteBuf[2]   = {0,};DWORD           dwSize          = 0;hMod = GetModuleHandleA("kernel32.dll");// set THREAD_PARAMparam.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");param.pFunc[1] = GetProcAddress(hMod, "GetProcAddress");strcpy_s(param.szBuf[0], "user32.dll");strcpy_s(param.szBuf[1], "MessageBoxA");strcpy_s(param.szBuf[2], "www.reversecore.com");strcpy_s(param.szBuf[3], "ReverseCore");// Open Processif ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS,   // dwDesiredAccessFALSE,                // bInheritHandledwPID)) )             // dwProcessId{printf("OpenProcess() fail : err_code = %d\n", GetLastError());return FALSE;}// Allocation for THREAD_PARAMdwSize = sizeof(THREAD_PARAM);if( !(pRemoteBuf[0] = VirtualAllocEx(hProcess,          // hProcessNULL,                 // lpAddressdwSize,               // dwSizeMEM_COMMIT,           // flAllocationTypePAGE_READWRITE)) )    // flProtect{printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());return FALSE;}if( !WriteProcessMemory(hProcess,                       // hProcesspRemoteBuf[0],                  // lpBaseAddress(LPVOID)&param,                 // lpBufferdwSize,                         // nSizeNULL) )                         // [out] lpNumberOfBytesWritten{printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());return FALSE;}// Allocation for ThreadProc()dwSize = (DWORD)InjectCode - (DWORD)ThreadProc;if( !(pRemoteBuf[1] = VirtualAllocEx(hProcess,          // hProcessNULL,                 // lpAddressdwSize,               // dwSizeMEM_COMMIT,           // flAllocationTypePAGE_EXECUTE_READWRITE)) )    // flProtect{printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());return FALSE;}if( !WriteProcessMemory(hProcess,                       // hProcesspRemoteBuf[1],                  // lpBaseAddress(LPVOID)ThreadProc,             // lpBufferdwSize,                         // nSizeNULL) )                         // [out] lpNumberOfBytesWritten{printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());return FALSE;}if( !(hThread = CreateRemoteThread(hProcess,            // hProcessNULL,                // lpThreadAttributes0,                   // dwStackSize(LPTHREAD_START_ROUTINE)pRemoteBuf[1],     // dwStackSizepRemoteBuf[0],       // lpParameter0,                   // dwCreationFlagsNULL)) )             // lpThreadId{printf("CreateRemoteThread() fail : err_code = %d\n", GetLastError());return FALSE;}WaitForSingleObject(hThread, INFINITE);	CloseHandle(hThread);CloseHandle(hProcess);return TRUE;
}
  • injectCode()函数的set THREAD_PARAM部分用来设置THREAD_PARAM结构体变量,它们会注入目标进程,并且以参数形式传递给ThreadProc()线程。
  • 接下来调用了一系列API函数,API函数归纳如下:
OpenProcess();
//data:THREAD_PARAM
VirtualAllocEx();
WriteProcessMemory();
//code:ThreadProc()
VirtualAllocEx();
WriteProcessMemory();CreateRemoteThread();
  • 上述代码主要用在目标进程中分别为data与code分配内存,并将它们注入目标进程,最后调用CreateRemoteThread() API,执行远程线程。

代码注入调试练习

  1. 用Ollydbg开始调试notepad.exe文件,按F9运行键,是notepad.exe处于Runing状态。
    在这里插入图片描述
  2. 设置OllyDbg的选项后,即可从注入的线程代码开始调试。
    在这里插入图片描述
  • 每当notepad.exe进程中生成新线程,调试器就暂停在线程函数开始的代码位置。
  1. 使用Process Explorer查看notepad.exe的进程PID。
    在这里插入图片描述
  • 在命令窗口输入PID作参数,运行CodeInjection.exe。
C:\Users\12586\Downloads\example\03\27\bin>CodeInjection.exe 13716
  1. 按F9继续运行调试器,代码注入成功后,调试器就会暂停在被注入的线程代码的开始位置,由此开始调试。
    在这里插入图片描述
  • 004E0004地址处的MOV ESI,DWORD PTR SS:[EBP+8],[EBP+8]地址就是ThreadProc()函数的IParam参数,而参数IParam则指向被一同注入的THREAD_PARAM结构体。
    在这里插入图片描述
http://www.lryc.cn/news/2420772.html

相关文章:

  • 运维管理平台 - 自动部署salt被控端minion
  • 最新坦白说破解方法!!!
  • 跨学科的AI量化其实很简单
  • 判断入射满射c语言编码,例4,判断下列函数是否是满射、单射、双射。.PDF
  • 打造我们心中永恒的m500
  • 武汉Uber优步司机奖励政策(8月31日~9月6日)
  • eccv 2020_为什么我停止使用gan eccv2020
  • 你管这破玩意叫操作系统源码 | 第一回 最开始的两行代码
  • Ubuntu命令全集
  • fabric 智能合约开发
  • 1024分辨率《圣徒/天神魔煞/猎魔教士》BD中字无水印
  • day 09--函数和模块
  • 列名 userid 不明确。 存储过程_写给高二美术生:如果你还不知道这些问题,那你就惨了!...
  • python实现抢票github_面向回家编程!GitHub标星两万的”Python抢票教程”,我们先帮你跑了一遍...
  • 英语阅读积累一
  • js中文转拼音插件源码
  • 2022-3-12 【webrtc应用】yangrtc/metaRTC开源库源码分析(一)
  • 知识见闻 - 苏州旅游
  • Html 中表格导出生成excel文件,解决中文导出失败问题。
  • 陈继儒 -- 《小窗幽记》
  • 摩托罗拉发布最新款Android 3.0平板电脑Xoom
  • Android仿抖音主页效果实现
  • Tinywebserver-一个简易的web服务器
  • 项目管理术语中英文对照
  • 男生vs女生,谁更加适合做软件测试?—我觉得男生更胜一筹!
  • 一键评教,查询成绩,批量免验证码选课,退课,-云大urp教务系统大作战(3)...
  • Openstack 单控制节点部署实例
  • CSS开发案例【新闻热榜】
  • 拼音汉字对照表
  • 数据补全与数据质量: 如何实现数据完整性