首页
社区
课程
招聘
[原创]DLL注入之远线程方式
发表于: 2013-3-30 14:28 21297

[原创]DLL注入之远线程方式

2013-3-30 14:28
21297
远线程注入
     每个进程都有自己的虚拟地址空间,对32位进程来说,这个地址空间的大小为4GB。因为每个进程都有自己专有的地址空间,当进程的各个线程运行的时候,它们只能够访问属于自己进程的内存。这样做的一个好处是维护系统的安全,防止进程的私有空间被入侵。世界上有了矛就有了盾,windows也撕开了一个小口,提供了一些函数来让其它进程对另一个进程进行操作,当然亦邪亦正,全在于你。大名鼎鼎的CreateRemoteThread就是属于这样的函数。
    远线程注入的基本原理就是通过在另一个进程中创建远程线程的方法进入目标进程的内存地址空间。使用插入到目标进程中的远程线程将该DLL插入到目标进程的地址空间,即利用该线程通过调用Windows API LoadLibrary函数来加载DLL,从而实现获取目标进程空间的使用权。如下摘自ReactOS 3.14的代码所示,CreateRemoteThread实际实现的功能就是调用NtCreateThread创建一个属于目标进程的线程。
 
 
HANDLE
WINAPI
CreateRemoteThread(HANDLE hProcess,
                   LPSECURITY_ATTRIBUTES lpThreadAttributes,
                   DWORD dwStackSize,
                   LPTHREAD_START_ROUTINE lpStartAddress,
                   LPVOID lpParameter,
                   DWORD dwCreationFlags,
                   LPDWORD lpThreadId)
{
    /* Clear the Context */
    RtlZeroMemory(&Context, sizeof(CONTEXT));

    /* Write PID */
    ClientId.UniqueProcess = hProcess;

    /* Create the Stack */
    Status = BaseCreateStack(hProcess,
                              dwStackSize,
                              dwCreationFlags & STACK_SIZE_PARAM_IS_A_RESERVATION ?
                              dwStackSize : 0,
                              &InitialTeb);
                              
    /* Create Initial Context */
    BaseInitializeContext(&Context,
                           lpParameter,
                           lpStartAddress,
                           InitialTeb.StackBase,
                           1);

    /* initialize the attributes for the thread object */
    ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
                                                    lpThreadAttributes,
                                                    NULL);

//前面部分都是在初始化新线程的环境,紧接着就是创建线程了
    /* Create the Kernel Thread Object */
    Status = NtCreateThread(&hThread,
                            THREAD_ALL_ACCESS,
                            ObjectAttributes,
                            hProcess,
                            &ClientId,
                            &Context,
                            &InitialTeb,
                            TRUE);
   //...............其它操作                         
}


CreateRemoteThread在MSDN的声明如下:
 HANDLE WINAPI CreateRemoteThread(
  __in          HANDLE hProcess,  //新线程所属的进程句柄
  __in          LPSECURITY_ATTRIBUTES lpThreadAttributes,
  __in          SIZE_T dwStackSize,
  __in          LPTHREAD_START_ROUTINE lpStartAddress,  //新线程函数指针
  __in          LPVOID lpParameter,  //新线程函数所需传入的参数
  __in          DWORD dwCreationFlags,
  __out         LPDWORD lpThreadId
);

     接下来用一个简单的例子来分析说明,本示例所实现的功能就是创建远线程,把DLL注入到目标进程中去。
1)  获取一个需要被注入远线程的进程句柄。本例的目标进程为explorer.exe,这里通过toolhelp32函数来找到该进程的PID,进而用OpenProcess获取该进程的句柄。
 
  HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
  if( hSnapshot == NULL )
  {
    cout<<"Create Snapshot false."<<endl;
    cin.get();
    return;
  }
  PROCESSENTRY32 stProcessEntry32 = {0};
  stProcessEntry32.dwSize = sizeof(PROCESSENTRY32);
  Process32First( hSnapshot, &stProcessEntry32 );
  bool bFind = false;
  do
  {
    if( strncmp( stProcessEntry32.szExeFile, "explorer.exe", strlen("explorer.exe") ) == 0 )
    {
      bFind = true;
      break;
    }
  }while( Process32Next( hSnapshot, &stProcessEntry32 ) );
  CloseHandle( hSnapshot );
  if( !bFind )
  {
    cout<<"查找explorer进程失败."<<endl;
    cin.get();
    return;
  }

  DWORD dwPId = stProcessEntry32.th32ProcessID;
  HANDLE hProcess = OpenProcess(  PROCESS_ALL_ACCESS, false, dwPId );
  if( hProcess == NULL )
  {
    cout<<"打开explorer进程失败."<<endl;
    cin.get();
    return;
  }


2)  把进程的参数数据写入到目标进程的地址空间。
 
  char lpDllName[100] = {0};
GetCurrentDirectory( 100, lpDllName );
strcat( lpDllName, "\\InjectProcessDll.dll" );
  LPVOID lpDllNameAddr = VirtualAllocEx( hProcess, NULL, strlen(lpDllName)+1, MEM_COMMIT, PAGE_READWRITE );
  if( lpDllNameAddr == NULL )
  {
    cout<<"explorer进程中申请内存失败."<<endl;
    CloseHandle(hProcess);
    cin.get();
    return;
  }
  cout<<"在目标进程:"<<stProcessEntry32.szExeFile<<"中申请的空间地址:"<<hex<<lpDllNameAddr<<endl;
  DWORD dwRes = 0;
  bool bRet = WriteProcessMemory( hProcess, lpDllNameAddr, lpDllName, strlen(lpDllName), &dwRes );
  if( !bRet )
  {
    cout<<"explorer进程写信息失败."<<endl;
    VirtualFreeEx( hProcess, lpDllNameAddr, strlen(lpDllName)+1, MEM_DECOMMIT );
    CloseHandle(hProcess);
    cin.get();
    return;

  }


3)  用 CreateRemoteThread启动远程的 ThreadFunc 拷贝。
 
  HMODULE hModuleKernel32 = GetModuleHandle("kernel32.dll");
  DWORD lp = (DWORD)LoadLibraryA;
  LPTHREAD_START_ROUTINE lpLoadLibraryAddr = (LPTHREAD_START_ROUTINE)GetProcAddress( hModuleKernel32, "LoadLibraryA" );
  if( lpLoadLibraryAddr != NULL )
  {
    cout<<"获得函数地址:"<<hex<<lpLoadLibraryAddr<<endl;
    HANDLE hRemote = CreateRemoteThread( hProcess, NULL, 0, lpLoadLibraryAddr, lpDllNameAddr, 0, NULL );


4)  等待远程线程终止,判断注入是否成功
 
    if( hRemote != NULL )
    {
      cout<<"创建远程线程成功,句柄:"<<hex<<hRemote<<endl;
      WaitForSingleObject( hRemote, INFINITE );
      CloseHandle( hRemote );
      cout<<"远程线程运行结束"<<endl;
      cin.get();

    }
    else
      cout<<"创建远程线程失败."<<endl;


     至此,该DLL注入到目标进程中。有些人在使用CreateRemoteThread的时候,会把它分为两种,一种是加载DLL的时候,一种是加载一段代码。其实仔细观察,加载DLL,也是加载一段代码形式的特例,只不过这段代码就是LoadLibraryA。

在另外两篇文章中讲到的RING3下的IAT HOOK和INLINE HOOK,都是非常常用的API拦截方法。但是没有好的DLL注入方法,无法显示它们的功力,下面用一个例子来演示:
功能简介:HOOK explorer.exe中的CreateProcessW,当遇到每一个打开记事本程序的时候,注入一个TestDll。
测试环境:XP SP3
a)  InjectDll.exe
即上面实现远线程注入的代码

b)  RemoteThreadDll.dll
  远线程要注入的DLL,其功能是IAT HOOK函数CreateProcess,当打开的是NOTEPAD时候,使用INLINE hook注入一段代码,令记事本加载一个DLL模块。

IAT HOOK 函数CreateProcess
特别注意的是,当我们鼠标去打开一个记事本程序的时候,系统会调用ShellExecuteEx-CreateProcessW,这里需要HOOK的是SHELL32.DLL的输入表。
 
BOOL ImportAddressTableHook
(
 IN HMODULE hModule,
 IN LPCTSTR pImageName,
 IN LPCVOID pTargetFuncAddr,
 IN LPCVOID pReplaceFuncAddr
 )
{
  IMAGE_DOS_HEADER* pImageDosHearder = (IMAGE_DOS_HEADER*)hModule;
  IMAGE_OPTIONAL_HEADER* pImageOptionalHeader = (IMAGE_OPTIONAL_HEADER*)((DWORD)hModule+pImageDosHearder->e_lfanew+24);  
  IMAGE_IMPORT_DESCRIPTOR* pImageImportDescriptor = (IMAGE_IMPORT_DESCRIPTOR*)
    ((DWORD)hModule+
    pImageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
  IMAGE_THUNK_DATA* pImageThunkData = NULL;
  string TargetLibraryName;
  DWORD Value = 0, OldProtect = 0, NewProtect = 0;
  LPDWORD FunctionAddress = NULL;
  MEMORY_BASIC_INFORMATION InforMation;


  while(pImageImportDescriptor != 0)   
  {   
    TargetLibraryName.clear();
    TargetLibraryName = (LPCTSTR)((DWORD)hModule + pImageImportDescriptor->Name);
    transform(TargetLibraryName.begin(), TargetLibraryName.end(), TargetLibraryName.begin(), ::toupper);
    if(TargetLibraryName.compare(pImageName) == 0)
    {   
      pImageThunkData = (IMAGE_THUNK_DATA*)((DWORD)hModule + pImageImportDescriptor->FirstThunk);
      while(pImageThunkData->u1.Function)   
      {   
        FunctionAddress = (LPDWORD)&(pImageThunkData->u1.Function);
        //_asm int 3
        //char pLog[100] = {0};
        //sprintf(pLog, "library name is %s, FunctionAddress is 0x%x , pTargetFuncAddr is 0x%x\n", TargetLibraryName.c_str(), *FunctionAddress, (DWORD)pTargetFuncAddr);
        ////MessageBox(NULL, pLog, "", MB_OK);
        //OutLog(pLog);
        if(*FunctionAddress == (DWORD)pTargetFuncAddr)
        {    
          if (!VirtualProtect(FunctionAddress, sizeof(DWORD), PAGE_READWRITE, &OldProtect)) return FALSE;
          if(!(WriteProcessMemory((HANDLE)-1, FunctionAddress, &pReplaceFuncAddr, 4, NULL)
            && VirtualProtect(FunctionAddress, sizeof(DWORD), OldProtect, &NewProtect)))
          {
            return FALSE;
          }

          //MessageBox(NULL, "end", "", MB_OK);
          return TRUE; 
        }  
        pImageThunkData++;   
      }
      break; 
    } 
    pImageImportDescriptor++;   
  } 

  if (pImageThunkData == NULL)
  {
    return FALSE;
  } 

  //MessageBox(NULL, "hello world", "", MB_OK);
  return FALSE;
}


在NOTEPAD.EXE注入一段SHELLCODE
 001D0000    60              pushad
001D0001    9C              pushfd
001D0002    68 00001B00     push    0x1B0000; ASCII "C:\WINDOWS\TestDll.dll"
001D0007    E8 6F1D637C     call    kernel32.LoadLibraryA
001D000C    9D              popfd
001D000D    61              popad
001D000E  - E9 5575E300     jmp     notepad.01007568

 char lpShellCode[] = {
      0x60,
      0x9c,
      0x68,0x90,0x90,0x90,0x90,
      0xe8,0x90,0x90,0x90,0x90,
      0x9d,
      0x61,
      0xe9,0x90,0x90,0x90,0x90};

      char lpCurrentDirectory [100] = {0};
      GetCurrentDirectory( 100, lpCurrentDirectory );
      strncat( lpCurrentDirectory, "\\TestDll.dll", strlen("\\TestDll.dll") );
      LPVOID lpDllPathAddr = VirtualAllocEx( lpProcessInformation->hProcess, NULL, strlen(lpCurrentDirectory)+1, MEM_COMMIT, PAGE_READWRITE );
      if (NULL == lpDllPathAddr) goto EndFunc;

      BOOL bOk = WriteProcessMemory( lpProcessInformation->hProcess, lpDllPathAddr, lpCurrentDirectory, strlen(lpCurrentDirectory), NULL);
      if( !bOk )
      {
        VirtualFree( lpDllPathAddr, strlen(lpCurrentDirectory)+1, MEM_DECOMMIT );
        goto EndFunc;
      }

      LPVOID lpFuncAddr = GetProcAddress( GetModuleHandle("kernel32.dll"), "LoadLibraryA" );

      LPVOID lpShellCodeAddr = VirtualAllocEx( lpProcessInformation->hProcess, NULL, strlen(lpShellCode)+1, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
      if( lpShellCodeAddr == NULL )
      {
        VirtualFree( lpDllPathAddr, strlen(lpCurrentDirectory)+1, MEM_DECOMMIT );
        goto EndFunc;
      }

      //组合shellcode
      //push    XXXXXXXX
      memcpy( lpShellCode + 3, (char*)&lpDllPathAddr, 4 );

      //call    XXXXXXXX,先计算偏移
      DWORD dwFuncAddr = (DWORD)lpFuncAddr - ((DWORD)lpShellCodeAddr + 7) - 5;
      memcpy( lpShellCode + 8, (char*)&dwFuncAddr, 4 );

      //jmp     XXXXXXXX,先计算偏移
      DWORD dwJmpEnd = dwChangedFuncAddr - ((DWORD)lpShellCodeAddr + 14) -5;
      memcpy( lpShellCode + 15, (char*)&dwJmpEnd, 4 );

      //写入shellcode,及改变原来函数入口
      bOk = WriteProcessMemory( lpProcessInformation->hProcess, lpShellCodeAddr, lpShellCode, 30, NULL );
      if( !bOk )
      {
        VirtualFreeEx( lpProcessInformation->hProcess, lpDllPathAddr, strlen(lpCurrentDirectory)+1, MEM_DECOMMIT );
        VirtualFreeEx( lpProcessInformation->hProcess, lpShellCodeAddr, strlen(lpShellCode)+1, MEM_DECOMMIT );
        goto EndFunc;
      }
      //计算call的新地址
      DWORD dwNewCallAddr = (DWORD)lpShellCodeAddr - (dwReadAddrPtr + 4);
      //写入写call地址

      MEMORY_BASIC_INFORMATION  stMemBasicInfor = {0};
      VirtualQueryEx( lpProcessInformation->hProcess, (PVOID)dwReadAddrPtr, &stMemBasicInfor, sizeof(MEMORY_BASIC_INFORMATION) );
      DWORD dwOldProtect = 0;
      VirtualProtectEx( lpProcessInformation->hProcess, stMemBasicInfor.BaseAddress, stMemBasicInfor.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect );
      bOk = WriteProcessMemory( lpProcessInformation->hProcess, (PVOID)dwReadAddrPtr, &dwNewCallAddr, 4, NULL );
      VirtualProtectEx( lpProcessInformation->hProcess, stMemBasicInfor.BaseAddress, stMemBasicInfor.RegionSize, dwOldProtect, NULL );
      if( !bOk )
      {
        VirtualFreeEx( lpProcessInformation->hProcess, lpDllPathAddr, strlen(lpCurrentDirectory)+1, MEM_DECOMMIT );
        VirtualFreeEx( lpProcessInformation->hProcess, lpShellCodeAddr, strlen(lpShellCode)+1, MEM_DECOMMIT );
        goto EndFunc;
      }

[课程]Android-CTF解题方法汇总!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (22)
雪    币: 325
活跃值: (650)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
占楼编辑。。。。。。。。
2013-3-30 14:32
0
雪    币: 6695
活跃值: (1159)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
前来膜拜学习~~~
2013-3-30 14:42
0
雪    币: 33
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
4
站位  现在白加黑玩注入的有福了!
2013-3-30 16:26
0
雪    币: 1149
活跃值: (833)
能力值: ( LV13,RANK:260 )
在线值:
发帖
回帖
粉丝
5
lz 把win7 所有的试试呗....太单一拉拉
2013-3-30 17:02
0
雪    币: 1103
活跃值: (461)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
麻烦楼主检查一下上传的RemoteThreadDll.haozip01.zip和RemoteThreadDll.haozip02.zip文件,我下载后文件校验错误,解压不出
2013-3-30 19:02
0
雪    币: 149
活跃值: (150)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
7
那个没问题的,得用好压解压,我上传了WINRAR版本的
上传的附件:
2013-3-31 09:47
0
雪    币: 142
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
这些注入和hook,论坛都讲烂了吧,有新颖点的嘛?
2013-3-31 10:52
0
雪    币: 1103
活跃值: (461)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
感谢楼主上传RAR格式源代码,虽然Hook 讲的很多,但是串讲起来还是挺不错的
2013-3-31 13:42
0
雪    币: 153
活跃值: (260)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
10
感谢楼主上传
2013-4-1 11:43
0
雪    币: 29
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
楼主辛苦了,谢谢!
2013-4-2 16:34
0
雪    币: 148
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
注入都是被视为危险行为的,会被数字公司和谐的
2013-4-2 23:32
0
雪    币: 93908
活跃值: (200199)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
13
Thanks for share.
2013-4-3 00:51
0
雪    币: 119
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
虽然早就掌握了,但是,看了楼主的文章,又明白了一些知识,谢谢啊。
2013-4-3 09:53
0
雪    币: 50
活跃值: (10)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
15
学习!!!感谢
2013-4-3 11:31
0
雪    币: 334
活跃值: (92)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
16
这个技术太老了吧,都多少年了,还有谁在用这个技术吗???
2013-4-3 15:15
0
雪    币: 253
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
老东西对新手来说,还是的有人因引导
2013-4-3 16:18
0
雪    币: 30
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
谢谢楼主,好代码
2013-4-3 18:55
0
雪    币: 327
活跃值: (30)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
19
强烈支持win7 32位的,请楼主给讲讲!
2013-4-4 10:42
0
雪    币: 62
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
支持楼主,适合我们新手学习
2013-4-16 12:58
0
雪    币: 8
活跃值: (21)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
耽误人。不用这么复杂
2013-4-16 21:02
0
雪    币: 508
活跃值: (202)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
22
用WIN32汇编非常间单明了!!!!!!!!!!!!!!!!!!!
2013-4-17 16:09
0
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23
学习!!!感谢
2022-2-3 02:20
0
游客
登录 | 注册 方可回帖
返回
//