代码注入(Inject your code to a Portable Executable file)

尝试用studyPE和x32dbg修改.exe文件01 标题尝试用studyPE和x32dbg修改.exe文件01 首先,准备一个exe文件,这里我使用自己用c++编写的exe文件,在程序中调用了LoadLibraryA函数动态加载dll文件,并使用GetProcAddress函数查询dll文件中的函数。采用这样的动态加载是为了简化试验过程,通常情况下,由于程序启动时会有地址重定位,因此,修改非动态加载的地址会比较繁琐。下面是源程序,编译后生成的程序就是我们的试验目标exe程序: 下面,启动x32dbg.exe,并点文件->打开,打开上面生成的exe文件 阅读详情

事先声明这片文章并非我的原创,你可以在这个地址http://www.codeproject.com/system/inject2exe.asp察看

到原文,由于本人能力有限,可能有一些翻译错误请见谅(PS:感谢ROOTKIT上的朋友告诉我这片文章)

Download
PE Viewer

PE Maker - Step 1

PE Maker - Step 2 - Travel towards OEP

PE Maker - Step 3 - Support Import Table.

PE Maker - Step 4 - Support DLL and OCX

PE Maker - Step 5 - Final work.

CALC.EXE - test file

0序文

也许你想要了解一个病毒,注入到程序内部并且感染他的方法,或者你对保护你特殊的PE文件的数据感

兴趣。你能使用这篇文章的源代码构建你自定义的EXE BUILDER。如果用在好的方面,他能教你怎样保

护或封装加密你的PE文件,但是同样如果你用在邪恶的方面,他能产生一个病毒。然而,我写这篇文章

的目的是前者,所以,我不会为不道德的使用负责。

1预备知识

按照主题这篇文章不需要特殊的预备知识,如果你已经了解了DEBUGGER和文件结构,那么我建议你跳

过2,3部分,这两部分是为毫无基础的人准备的。

2.PE文件的结构

规定PE文件的结构为WINDOWS OS提供了最好的方式去执行代码,并且储存一个程序运行所需要的基本

数据。例如常量,变量等等。如图:







2.1MS-DOS的数据

MS-DOS数据由你的可执行文件调用DOS内部的一个函数完成。并且 the MS-DOS Stub program让他显示:

This program can not be run in MS-DOS mode" 或"This program can be run only in Windows mode的字样,或者

当你试图在 MS-DOS 6.0中运行一个windows文件时也回显示类似的字样。MS-DOS数据最有意思的部分是

"MZ"!

对于我,在 MS-DOS 数据中仅仅PE署名的偏移是重要的,借助于他我能找到WINDOWS NT数据的位置。

我建议你在看看上图。然后看看 <winnt.h> 文件中IMAGE_DOS_HEADER 的结构。



e_lfanew是一个与WINDOWS NT数据有关的偏移。在这我提供你一个显示EXE文件头信息的工具。

PE Viewer

Download source files - 132 Kb

2.2 The Windows NT data

正如前一节提及的e_lfanew储存者有关WINDOWS NT数据位置的信息,假设 pMem 指针与指向一个PE文

件内存空间的起始点时,借助于下面的代码你既能找会DOS 头,也能找到WINDOWS NT 头

Code:


IMAGE_DOS_HEADER        image_dos_header
;
IMAGE_NT_HEADERS        image_nt_headers;
PCHAR pMem;
…
memcpy
(&image_dos_header, pMem,
       
sizeof(IMAGE_DOS_HEADER));
memcpy(&image_nt_headers,
       
pMem+image_dos_header.e_lfanew,
       
sizeof(IMAGE_NT_HEADERS));


似乎找回头信息是很容易的事,我建议去看MSDN中关于IMAGE_NT_HEADERS 的说明。现在你应该很了

解WINDOW NT结构了,他包含PE署名,the File Header,和the Optional Header。

下面是大多数环境中IMAGE_NT_HEADERS的结构:

<Code:

PRE lang
=c++>FileHeader->NumberOfSections
OptionalHeader
->AddressOfEntryPoint
OptionalHeader
->ImageBase
OptionalHeader
->SectionAlignment
OptionalHeader
->FileAlignment
OptionalHeader
->SizeOfImage
OptionalHeader
->
  
DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]->VirtualAddress
OptionalHeader
->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]->Size</PRE>


你能清晰的观察到,这些值的目的,以及当为一个EXE文件分配虚拟内存时,他们的脚色。

我要对PE数据目录进行一个简单的说明。当你通过Windows NT 信息对Optional header 进行观察时,你会

发现在Optional Header的结尾处有十六个目录,再那你能找到连续的目录,包括一些与虚拟内存和其大小

有关的信息。

Code:

#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor


对于我们,如果你想观察相对虚拟内存地址和数据尺寸,下面的代码是足够的:

Code:

DWORD dwRVA
= image_nt_headers.OptionalHeader->
  
DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]->VirtualAddress;
DWORD dwSize = image_nt_headers.OptionalHeader->
  
DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]->Size;


2.3 The Section Headers and Sections(节头和节)

现在我们观察PE文件怎样声明节的位置和尺寸。为了更好的理解节头(Section header)和节区的特征,我建

议你去看看IMAGE_SECTION_HEADER在MSDN定义的结构,对于一个EXE packer的开发者VirtualSize,

VirtualAddress, SizeOfRawData, PointerToRawData和Characteristics单元有着严格的规则。当开发一个exe

packer,你应该非常了解他们。当你修改他们时,有许多你要注意,你要按OptionalHeader-

>SectionAlignment的顺序小心排列VirtualSize和VirtualAddress同样SizeOfRawData和PointerToRawData要按

OptionalHeader->FileAlignment的顺序排列。否则你将损坏目标EXE文件,并且不能运行他们。至于

Characteristics,更要格外小心,你要按IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE |

IMAGE_SCN_CNT_INITIALIZED_DATA的顺序建立一个节区。我更喜欢我的新节区在进程运行时有初始化

这些数据的能力。当然引入表也要如此。此外,我还需要他能利用loader修改他自己。

同样你要考虑好新的节区名,这样你能仅通过名字就知道他们的作用。如表二

表二



为了更好的理解节头和节区,请运行PE viewer。借助于他,你能了解一个文件映像中节头的应用。要想

观察到虚拟内存,你应该用调试器载入一个PE文件。请记住无论什么时候在PE文件中删除或添加节区,

注意调整PE文件的NumberOfSections( IMAGE_NT_HEADERS-> FileHeader-><CODE>NumberOfSections)他说明

了节区的数目。

3 DEBUGER、DISASSEMBLER和一些有用的工具

3.1 DEBUGER

要想变成一个PE工具的开发者,对BUG跟踪程序的使用经验是一个必备条件。此外,你还不得知道一些

必备的汇编指令,对于我们INTEL公司的文档是最好的参考书,你可以到INTEL的网站获得他们。

IA-32 Intel Architecture Software Developer’s Manuals.
Intel Itanium Architecture Assembly Language Reference Guide.
The Intel Itanium Processor Developer Resource Guide.
对于PE文件的追踪,我认为SOFTICE是最好的调试工具。借助与内核模式我们可以进行进程追踪而无须

应用API函数另外,我还要介绍在一个调试器,他在USER模式下使用有很好的效果,他利用API函数跟踪

PE文件,并把自身进程依附在一个活跃的进程中。在WINDOWS内核的库中,微软提供了这些API函数,

为了跟踪特定的进程,你要使用调试工具。一些重要的API函数包括 CreateThread(), CreateProcess(),

OpenProcess(), DebugActiveProcess(),

GetThreadContext(), SetThreadContext(), ContinueDebugEvent(), DebugBreak(), ReadProcessMemory(),

WriteProcessMemory(), SuspendThread(), and ResumeThread().


3.1.2调试器的那些部分是重要的 (偶省掉了一些没用的部分)

前面我介绍了两个调试器,但是我并没有说明怎样使用他们。我建议你去看他们的HELP文档,但我还是

想简要的说明一些重要的部分。

1。Registers viewer.

Code:

EAX
ECX
EDX
EBX
ESP
EBP
ESI
EDI
EIP
o d t s z a p c



2。Disassembler or Code viewer

Code:

010119E0 PUSH EBP
010119E1 MOV EBP
,ESP
010119E3 PUSH
-1
010119E5 PUSH 01001570
010119EA PUSH 01011D60
010119EF MOV EAX
,DWORD PTR FS:[0]
010119F5 PUSH EAX
010119F6 MOV DWORD PTR FS
:[0],ESP
010119FD ADD ESP
,-68
01011A00 PUSH EBX
01011A01 PUSH ESI
01011A02 PUSH EDI
01011A03 MOV DWORD PTR SS
:[EBP-18],ESP
01011A06 MOV DWORD PTR SS
:[EBP-4],0


3.Memory watcher.

Code:

0023
:01013000 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0023:01013010 01 00 00 00 20 00 00 00-0A 00 00 00 0A 00 00 00 ................
0023:01013020 20 00 00 00 00 00 00 00-53 63 69 43 61 6C 63 00 ........SciCalc.
0023:01013030 00 00 00 00 00 00 00 00-62 61 63 6B 67 72 6F 75 ........backgrou
0023
:01013040 6E 64 00 00 00 00 00 00-2E 00 00 00 00 00 00 00 nd..............

4

4.Stack viewer.

Code:

0010
:0007FFC4 4F 6D 81 7C 38 07 91 7C-FF FF FF FF 00 90 FD 7F Om |8 ‘| .
0010:0007FFD4 ED A6 54 80 C8 FF 07 00-E8 B4 F5 81 FF FF FF FF T .
0010:0007FFE4 F3 99 83 7C 58 6D 81 7C-00 00 00 00 00 00 00 00 Xm |........
0010:0007FFF4 00 00 00 00 E0 19 01 01-00 00 00 00 00 00 00 00 .... ....


5.Command line, command buttons, or shortcut keys to follow the debugging process.
Code:

Command SoftICE OllyDbg
Run F5 F9
Step Into F11 F7
Step Over F10 F8
Set
Break Point F8 F2


3.2一些有用的工具

一个好的PE开发者要熟悉一些工具来节省他们的时间,所以我替大家选择了一些有用的工具,来分

析可执行文件。

3.2.1 LordPE

LordPE仍然是获得PE文件信息并修改他们的首选工具。



3.3.2 PEiD

PEiD对于分析编译器,壳种类,入口点来说是一款很好的工具。到现在为止,他能侦测500种以上的PE文

件类型。







 

. . . . . . . . . . . . . . . . . .
偶也玩起了BLOG,www.code47.blogcn.com

由 刺 于 2006-03-05 09:49 PM 最后编辑

Old Post 2006-03-04 02:57 AM
点这里查看 天魔降临 的个人资料 点这里给 天魔降临 发送一条悄悄话 查找 天魔降临 的更多帖子 将 天魔降临 添加到你的好友列表    天魔降临的ICQ在线状态    天魔降临 的QQ号码:493381043 编辑/删除 引用/回复
天魔降临

路旁的落叶
路旁的落叶

来自:
金钱:423
发帖数: 59

注册日期: 2005-11-03

天魔降临 现在离线

No.2 代码注入(这不是水帖,因为J.Y MM在这,我才在这发的)2

3.3.3 Resource Hacker

他能用来修改一些资源数据信息,如图标,菜单,版本等等。



3.3.4 WinHex



3.3.5 CFF Explorer

他支持PE32/64,PE重建包括CIL文件,换句话说就是.NET文件。他使用更方便,功能更强大。



4添加新的节区并改变EPO


我们已经完成了工作的第一步。所以我添加了一个新的节区,并重建了PE文件。在开始前我想让你借助

OD熟悉PE头。用OD载入文件,选择View->Executable file,Special->PE header,你将看到类似图3的画面。

现在在主菜单选择View->Memory,试着在内存映射窗口区别各个节区。





我想向你解释,我们怎样改变举例文件中的入口点偏移量(CALC.EXE)。首先,使用PE工具,找到入口

点 0x00012475,并且映像基地址为 0x01000000,这个OEP是虚拟地址的相对地址,所以映像基地址来转

变把他为虚拟地址。

Code:


Virtual_Address
= Image_Base + Relative_Virtual_Address

DWORD OEP_RVA
= image_nt_headers->OptionalHeader.AddressOfEntryPoint
;
// OEP_RVA = 0x00012475
DWORD OEP_VA = image_nt_headers->OptionalHeader.ImageBase + OEP_RVA
;
// OEP_VA = 0x01000000 + 0x00012475 = 0x01012475


PE Maker - 1

Download test PE file - 49.5 Kb

in loader.cpp文件中的DynLoader()函数用来保存新的节的数据

Code:

__stdcall void DynLoader
()
{
_asm
{
//----------------------------------
    
DWORD_TYPE(DYN_LOADER_START_MAGIC
)
//----------------------------------
    
MOV EAX,01012475h
// << Original OEP
    
JMP EAX
//----------------------------------
    
DWORD_TYPE(DYN_LOADER_END_MAGIC
)
//----------------------------------
}
}


不幸的是这个源代码只能在sample test file. 中应用,我们应该借助于在新的节区保存前面的OEP的值,来

完善他,并使用他到达真正的OEP。我会在在 Step 2 完善他

4.1找回并重建PE文件

为了使用新的PE文件,我写了一个简单的类库文件,来修复PE信息。

Code:

CPELibrary
Class Step 1
----------------------------------------------------------------
class
CPELibrary
{
private
:
    
//-----------------------------------------
    
PCHAR                   pMem
;
    
DWORD                   dwFileSize
;
    
//-----------------------------------------
protected
:
    
//-----------------------------------------
    
PIMAGE_DOS_HEADER       image_dos_header
;
    
PCHAR                   pDosStub
;
    
DWORD                   dwDosStubSize, dwDosStubOffset
;
    
PIMAGE_NT_HEADERS       image_nt_headers
;
    
PIMAGE_SECTION_HEADER   image_section_header[MAX_SECTION_NUM
];
    
PCHAR                   image_section[MAX_SECTION_NUM
];
    
//-----------------------------------------
protected
:
    
//-----------------------------------------
    
DWORD PEAlign(DWORD dwTarNum,DWORD dwAlignTo
);
    
void AlignmentSections
();
    
//-----------------------------------------
    
DWORD Offset2RVA(DWORD dwRO
);
    
DWORD RVA2Offset(DWORD dwRVA
);
    
//-----------------------------------------
    
PIMAGE_SECTION_HEADER ImageRVA2Section(DWORD dwRVA
);
    
PIMAGE_SECTION_HEADER ImageOffset2Section(DWORD dwRO
);
    
//-----------------------------------------
    
DWORD ImageOffset2SectionNum(DWORD dwRVA
);
    
PIMAGE_SECTION_HEADER AddNewSection(char* szName,DWORD dwSize
);
    
//-----------------------------------------
public
:
    
//-----------------------------------------
    
CPELibrary
();
    ~
CPELibrary
();
    
//-----------------------------------------
    
void OpenFile(char* FileName
);
    
void SaveFile(char* FileName
);    
    
//-----------------------------------------
};



借助于表1,image_dos_header, pDosStub, image_nt_headers, image_section_header [MAX_SECTION_NUM], 和

image_section[MAX_SECTION_NUM] 的用法是很清晰的。我们使用OpenFile()和SaveFile()来重新得到并重建

PE文件此外,一个重要的阶段是使用AddNewSection()来创造新的节区。

4.2为新的节区添加数据

在pecrypt.cpp中,我构建了另一个类,CPECryptor来包含新节区的数据。新节区的数据由loader.cpp文件中

的DynLoader()函数构建。DynLoader Step 1.我们也使用CPECryptor类来填充其他资料。
Code:

CPECryptor
Class Step 1
----------------------------------------------------------------
class
CPECryptor:
public CPELibrary
{
private
:
    
//----------------------------------------
    
PCHAR pNewSection
;
    
//----------------------------------------
    
DWORD GetFunctionVA(void* FuncName
);
    
void* ReturnToBytePtr(void* FuncName, DWORD findstr
);
    
//----------------------------------------
protected
:
    
//----------------------------------------
public
:    
    
//----------------------------------------
    
void CryptFile(int(__cdecl *callback) (unsigned int, unsigned int
));
    
//----------------------------------------
};
//----------------------------------------------------------------

 

. . . . . . . . . . . . . . . . . .
偶也玩起了BLOG,www.code47.blogcn.com

由 天魔降临 于 2006-03-05 08:36 PM 最后编辑

Old Post 2006-03-04 04:05 AM
点这里查看 天魔降临 的个人资料 点这里给 天魔降临 发送一条悄悄话 查找 天魔降临 的更多帖子 将 天魔降临 添加到你的好友列表    天魔降临的ICQ在线状态    天魔降临 的QQ号码:493381043 编辑/删除 引用/回复
天魔降临

路旁的落叶
路旁的落叶

来自:
金钱:423
发帖数: 59

注册日期: 2005-11-03

天魔降临 现在离线

No.3 代码注入(这不是水帖,因为J.Y MM在这,我才在这发的)3

4.3一些关于构建新的PE文件的说明

用一下片断来排列虚拟地址和每个节区的虚拟大小

Code:


image_section_header
[i]->VirtualAddress=
    
PEAlign(image_section_header[i]->VirtualAddress
,
    
image_nt_headers->OptionalHeader.SectionAlignment
);

image_section_header[i]->Misc.VirtualSize
=
    
PEAlign(image_section_header[i]->Misc.VirtualSize
,
    
image_nt_headers->OptionalHeader.SectionAlignment
);


Align the PointerToRawData and the SizeOfRawData of each section by FileAlignment

Code:

image_section_header
[i]->PointerToRawData =
    
PEAlign(image_section_header[i]->PointerToRawData
,
            
image_nt_headers->OptionalHeader.FileAlignment
);

image_section_header[i]->SizeOfRawData
=
    
PEAlign(image_section_header[i]->SizeOfRawData
,
            
image_nt_headers->OptionalHeader.FileAlignment
);


Correct the SizeofImage by the virtual size and the virtual address of the last section

Code:

image_nt_headers
->OptionalHeader.SizeOfImage =
          
image_section_header[LastSection]->VirtualAddress
+
          
image_section_header[LastSection]->Misc.VirtualSize
;


Set the Bound Import Directory header to zero, as this directory is not very important to execute a PE file:

Code:

image_nt_headers
->
  
OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT
].
  
VirtualAddress = 0
;
image_nt_headers
->
  
OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0
;


4.4一些关于连接这个VC程序的解释

Set Linker->General->Enable Incremental Linking to No (/INCREMENTAL:NO).



你能了解增加连接和不增加连接之间的不同



为了获得DynLoader(), 的虚拟地址,再增加LINK中我们获得JMP pemaker.DynLoader 的虚拟地址。但是不使

用LINK时用以下代码获得地址

Code:

DWORD dwVA
= (DWORD) DynLoader;


This setting is more critical in the incremental link when you try to find the beginning and ending of the Loader,

DynLoader(), by CPECryptor::ReturnToBytePtr():(抱歉,这句话偶不知道如何才能翻译通顺 )

Code:

void
* CPECryptor::ReturnToBytePtr(void* FuncName, DWORD findstr)
{
    
void* tmpd
;
    
__asm
   
{
        
mov eax,
FuncName
        jmp df
hjg
:    
inc eax
df
:     mov ebx, [eax
]
        
cmp ebx,
findstr
        jnz hjg
        mov tmpd
,
eax
    
}
    return
tmpd
;
}


5.保存重要的数据和延伸原入口点

现在,我们已经保存了原入口点,并映射了基地址以便于到达虚拟地址入口点。在DynLoader()结尾处我已

经保存一个空白区来储存这些重要的数据。DynLoader Step 2.

PE Maker - Step 2

Download source files - 58.3 Kb

Code:

DynLoaderStep2
>DynLoader Step 2

__stdcall void DynLoader
()
{
_asm
{
//----------------------------------
    
DWORD_TYPE(DYN_LOADER_START_MAGIC
)
//----------------------------------
Main_0
:
    
PUSHAD
    
// get base ebp
    
CALL Main_1
Main_1
:    
    
POP EBP
    SUB EBP
,
OFFSET Main_1
    MOV EAX
,DWORD PTR [EBP+_RO_dwImageBase
]
    
ADD EAX,DWORD PTR [EBP+_RO_dwOrgEntryPoint
]
    
PUSH EAX
    RETN
// >> JMP to Original OEP
//----------------------------------
    
DWORD_TYPE(DYN_LOADER_START_DATA1
)
//----------------------------------<FONT color=red>
_RO_dwImageBase:                DWORD_TYPE(0xCCCCCCCC
)
_RO_dwOrgEntryPoint:            DWORD_TYPE(0xCCCCCCCC)</FONT
>
//----------------------------------
    
DWORD_TYPE(DYN_LOADER_END_MAGIC
)
//----------------------------------
}
}


5.1恢复开始时寄存器间的关系

恢复他们间的关系是重要的,但在DynLoader Step 2的源代码中我们还没有做这件事。我们可以修改

DynLoader() 函数来重建开始的关系

Code:

__stdcall void DynLoader
()
{
_asm
{
//----------------------------------
    
DWORD_TYPE(DYN_LOADER_START_MAGIC
)
//----------------------------------
Main_0
:
    <
FONT color=red>PUSHAD
// Save the registers context in stack</FONT>
    
CALL Main_1
Main_1
:    
    
POP EBP
// Get Base EBP
    
SUB EBP,
OFFSET Main_1
    MOV EAX
,DWORD PTR [EBP+_RO_dwImageBase
]
    
ADD EAX,DWORD PTR [EBP+_RO_dwOrgEntryPoint
]
    
MOV DWORD PTR [ESP+1Ch],EAX
// pStack.Eax <- EAX
    
<FONT color=red>POPAD
// Restore the first registers context from stack</FONT>
    
PUSH EAX
    
XOR  EAX,
EAX
    RETN
// >> JMP to Original OEP
//----------------------------------
    
DWORD_TYPE(DYN_LOADER_START_DATA1
)
//----------------------------------
_RO_dwImageBase:                DWORD_TYPE(0xCCCCCCCC
)
_RO_dwOrgEntryPoint:            DWORD_TYPE(0xCCCCCCCC
)
//----------------------------------
    
DWORD_TYPE(DYN_LOADER_END_MAGIC
)
//----------------------------------
}
}


5.2恢复最初的堆栈

我们能利用设置在堆栈开始处的值加0x34到原入口点,来恢复原始的堆栈,但这不是很重要。然而在下

面的代码中我用了一个简单的技巧来到达OEP以便于修改堆栈,你能利用OD来跟踪执行过程。

Code:

__stdcall void DynLoader
()
{
_asm
{
//----------------------------------
    
DWORD_TYPE(DYN_LOADER_START_MAGIC
)
//----------------------------------
Main_0
:
    
PUSHAD
// Save the registers context in stack
    
CALL Main_1
Main_1
:    
    
POP EBP
    SUB EBP
,
OFFSET Main_1
    MOV EAX
,DWORD PTR [EBP+_RO_dwImageBase
]
    
ADD EAX,DWORD PTR [EBP+_RO_dwOrgEntryPoint
]
    
MOV DWORD PTR [ESP+54h],EAX
// pStack.Eip <- EAX
    
POPAD
// Restore the first registers context from stack
    
CALL _OEP_Jump
    DWORD_TYPE
(0xCCCCCCCC
)
_OEP_Jump
:
    
PUSH EBP
    MOV EBP
,
ESP
    MOV EAX
,DWORD PTR [ESP+3Ch]
// EAX <- pStack.Eip
    
MOV DWORD PTR [ESP+4h],EAX  
// _OEP_Jump RETURN pointer <- EAX
    
XOR EAX,
EAX
    LEAVE
    RETN
//----------------------------------
    
DWORD_TYPE(DYN_LOADER_START_DATA1
)
//----------------------------------
_RO_dwImageBase:                DWORD_TYPE(0xCCCCCCCC
)
_RO_dwOrgEntryPoint:            DWORD_TYPE(0xCCCCCCCC
)
//----------------------------------
    
DWORD_TYPE(DYN_LOADER_END_MAGIC
)
//----------------------------------
}
}


5.3Approach OEP by Structured Exception Handling(借助于构造异常处理来接近OEP)

当程序执行了错误的代码就会抛出一个异常,在这种条件下,程序迅速跳转到一个叫做异常处理的功能

中。

下面的文件包含了异常处理的调用,异常的抛出,以及其功能。

Code:

#include "stdafx.h"
#include "windows.h"

void RAISE_AN_EXCEPTION
()
{    
_asm
{
    
INT 3
    INT 3
    INT 3
    INT 3
}
}

int _tmain(int argc, _TCHAR* argv
[])
{
    
__try
    
{
        
__try
{
            
printf("1: Raise an Exception/n"
);
            
RAISE_AN_EXCEPTION
();
        }
        
__finally
        
{
            
printf("2: In Finally/n"
);
        }
    }
    
__except( printf("3: In Filter/n"), EXCEPTION_EXECUTE_HANDLER
)
    {
        
printf("4: In Exception Handler/n"
);
    }
    return
0
;
}


;Code:

main
()
00401000:
PUSH EBP
00401001
: MOV EBP,
ESP
00401003
: PUSH -
1
00401005
:
PUSH 00407160
; __try
{
;
the structured exception handler (SEH)
installation
0040100A
:
PUSH _except_handler3  
0040100F
: MOV EAX,DWORD PTR FS:[0
]
00401015:
PUSH EAX
00401016
: MOV DWORD PTR FS:[0],
ESP
0040101D
: SUB ESP,
8
00401020
:
PUSH EBX
00401021
:
PUSH ESI
00401022
:
PUSH EDI
00401023
: MOV DWORD PTR SS:[EBP-18],
ESP
;     __try
{
00401026: XOR ESI,
ESI
00401028
: MOV DWORD PTR SS:[EBP-4],
ESI
0040102B
: MOV DWORD PTR SS:[EBP-4],
1
00401032
: PUSH OFFSET
"1: Raise an Exception"
00401037:
CALL printf
0040103C
: ADD ESP,
4
; the raise a exception,
INT 3 exception
; RAISE_AN_EXCEPTION
()
0040103F:
INT3      
00401040
:
INT3
00401041
:
INT3
00401042
:
INT3
;     } __finally
{
00401043: MOV DWORD PTR SS:[EBP-4],
ESI
00401046
:
CALL 0040104D
0040104B
:
JMP 00401080
0040104D
: PUSH OFFSET
"2: In Finally"
00401052:
CALL printf
00401057
: ADD ESP,
4
0040105A
:
RETN
<FONT color=black
>;     }
<
FONT color=black
>; }
<
FONT color=black>; __except
(
0040105B:
JMP 00401080
0040105D
: PUSH OFFSET
"3: In Filter"
00401062:
CALL printf
00401067
: ADD ESP,
4
0040106A
: MOV EAX,1 ; EXCEPTION_EXECUTE_HANDLER =
1
0040106F
:
RETN
;     , EXCEPTION_EXECUTE_HANDLER
)
; }
;
the exception handler funtion
00401070
: MOV ESP,DWORD PTR SS:[EBP-18
]
00401073: PUSH OFFSET
"4: In Exception Handler"
00401078:
CALL printf
0040107D
: ADD ESP,
4
; }
00401080: MOV DWORD PTR SS:[EBP-4],-
1
0040108C
: XOR EAX,
EAX
;
restore previous SEH
0040108E
: MOV ECX,DWORD PTR SS:[EBP-10
]
00401091: MOV DWORD PTR FS:[0],
ECX
00401098
:
POP EDI
00401099
:
POP ESI
0040109A
:
POP EBX
0040109B
: MOV ESP,
EBP
0040109D
:
POP EBP
0040109E
:
RETN

 

. . . . . . . . . . . . . . . . . .
偶也玩起了BLOG,www.code47.blogcn.com

由 天魔降临 于 2006-03-05 09:33 PM 最后编辑

Old Post 2006-03-04 04:39 AM
点这里查看 天魔降临 的个人资料 点这里给 天魔降临 发送一条悄悄话 查找 天魔降临 的更多帖子 将 天魔降临 添加到你的好友列表    天魔降临的ICQ在线状态    天魔降临 的QQ号码:493381043 编辑/删除 引用/回复
天魔降临

路旁的落叶
路旁的落叶

来自:
金钱:423
发帖数: 59

注册日期: 2005-11-03

天魔降临 现在离线

No.4 代码注入(这不是水帖,因为J.Y MM在这,我才在这发的)4

建立一个控制台工程,连接并执行先前的文件,观察结果。

Code:


1
: Raise an Exception
3
:
In Filter
2
:
In Finally
4
:
In Exception Handler
_


程序执行了一个异常的表达式printf("3: In Filter/n");,当异常发生,(在这个例子INT 3 异常,你也能用另外

的异常),在OD中Debugging options->Exceptions你能看到不同类型异常的清单



5.3.1 Implement Exception Handler

我们希望建立一个异常处理来到达程序的OEP,借助前满的代码,你现在已经能区别SEH安装,异常抛

出,异常表达式过滤,为了建立我们的异常接近(OEP),我们需要下面的代码

SEH installation
Code:

LEA EAX
,[EBP+_except_handler1_OEP_Jump]
    
PUSH EAX
    PUSH DWORD PTR FS
:[0
]
    
MOV DWORD PTR FS:[0],
ESP


异常抛出

Code:

INT 3


异常表达式过滤
Code:

except_handler1_OEP_Jump
:
    
PUSH EBP
    MOV EBP
,
ESP
    
...
    
MOV EAX, EXCEPTION_CONTINUE_SEARCH
// EXCEPTION_CONTINUE_SEARCH = 0
    
LEAVE
    RETN
</


所以我们利用ASM嵌套来到达OEP

Code:

__try
// SEH installation
{
    
__asm
    
{
        
INT 3
// An Exception Raise
    
}
}
__except( ..., EXCEPTION_CONTINUE_SEARCH
){}
// Exception handler expression filter


ASM代码

Code:

;
----------------------------------------------------
    ;
the structured exception handler (SEH)
installation
    
; __try
{
    
LEA EAX,[EBP+_except_handler1_OEP_Jump
]
    
PUSH EAX
    PUSH DWORD PTR FS
:[0
]
    
MOV DWORD PTR FS:[0],
ESP
    
<FONT color=green
>; ----------------------------------------------------
    ;
the raise a INT 3 exception
    INT 3
    INT 3
    INT 3
    INT 3
    
; }
    ;
__except
( ...
    ; ----------------------------------------------------
    ;
exception handler expression filter
_except_handler1_OEP_Jump
:
    
PUSH EBP
    MOV EBP
,
ESP
    
...
    
MOV EAX, EXCEPTION_CONTINUE_SEARCH ; EXCEPTION_CONTINUE_SEARCH =
0
    LEAVE
    RETN
   ;
, EXCEPTION_CONTINUE_SEARCH
) { }


异常值__except(..., Value), 定义了这个异常如何被处理,他能由三个值分别是1,0,-1。为了理解他们

请在MSDN Library中寻找有关
try-except 声明的描述。当我们把他设置为0时,就不会执行异常处理的功能,

因此,借助于这个值,异常处理就会被忽略,并且线程继续执行其他的代码。

如何安装SEH

就像你从前面的代码中看到的,SEH是借助于FS段寄存器还完成安装的。WIN32使用FS段寄存器作为主线

程中数据块的指针。开始的0X1C字节包含Thread Information Block (TIB)的信息,此外,FS:[00h] 在与主线程

的ExceptionList 有关(表三),在我们的代码中,我们已经将_except_handler1_OEP_Jump指针压入堆栈,

并改变ExceptionList, FS:[00h]的值为堆栈起始的值,ESP

线程信息块(TIB)

Code:

typedef struct _NT_TIB32
{
    
DWORD ExceptionList
;
    
DWORD StackBase
;
    
DWORD StackLimit
;
    
DWORD SubSystemTib
;
    
union
{
        
DWORD FiberData
;
        
DWORD Version
;
    };
    
DWORD ArbitraryUserPointer
;
    
DWORD Self
;
}
NT_TIB32, *PNT_TIB32
;


表三 FS段寄存器和线程信息块


5.3.2借助于调整线程间的关系获得OEP


在这部分,我们将完成OEP的接近,并改变线程间的关系,忽略每个简单的异常处理。让线程在先前的

OEP中继续执行代码。当产生异常时,进程间的关系就被保存在堆栈中,借助EXCEPTION_POINTERS,我

们能访问ContextRecord的指针。ContextRecord拥有CONTEXT的数据结构(表四),这便是在执行时间中

的线程间的关系。当我们使用 EXCEPTION_CONTINUE_SEARCH (0)来忽略异常时,指令指针间的关系也

被设置到ContextRecord,以便程序可以返回先前的环境中去。因此,如果我们改变 Win32 Thread Context

中的EIP,使其为开始的OEP,这便可以进入OEP。

表四



Code:

MOV EAX
, ContextRecord
MOV EDI
, dwOEP                   ; EAX <-
dwOEP
MOV DWORD PTR DS
:[EAX+0B8h], EDI ; pContext.Eip <-
EAX


WIN32线程间关系的数据结构

Code:

#define MAXIMUM_SUPPORTED_EXTENSION     512

typedef struct _CONTEXT
{
    
//-----------------------------------------
    
DWORD ContextFlags
;
    
//-----------------------------------------
    
DWORD   Dr0
;
    
DWORD   Dr1
;
    
DWORD   Dr2
;
    
DWORD   Dr3
;
    
DWORD   Dr6
;
    
DWORD   Dr7
;
    
//-----------------------------------------
    
FLOATING_SAVE_AREA FloatSave
;
    
//-----------------------------------------
    
DWORD   SegGs
;
    
DWORD   SegFs
;
    
DWORD   SegEs
;
    
DWORD   SegDs
;
    
//-----------------------------------------
    
DWORD   Edi
;
    
DWORD   Esi
;
    
DWORD   Ebx
;
    
DWORD   Edx
;
    
DWORD   Ecx
;
    
DWORD   Eax
;
    
//-----------------------------------------
    
DWORD   Ebp
;
    
DWORD   Eip
;
    
DWORD   SegCs
;
    
DWORD   EFlags
;
    
DWORD   Esp
;
    
DWORD   SegSs
;
    
//-----------------------------------------
    
BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION
];
    
//----------------------------------------
} CONTEXT
,
*
LPCONTEXT
;


用下面的代码,我们就能使用异常处理程序来到达OEP

Code:

__stdcall void DynLoader
()
{
_asm
{
//----------------------------------
    
DWORD_TYPE(DYN_LOADER_START_MAGIC
)
//----------------------------------
Main_0
:
    
PUSHAD  
// Save the registers context in stack
    
CALL Main_1
Main_1
:    
    
POP EBP
    SUB EBP
,OFFSET Main_1
// Get Base EBP
    
MOV EAX,DWORD PTR [EBP+_RO_dwImageBase
]
    
ADD EAX,DWORD PTR [EBP+_RO_dwOrgEntryPoint
]
    
MOV DWORD PTR [ESP+10h],EAX    
// pStack.Ebx <- EAX
    
LEA EAX,[EBP+_except_handler1_OEP_Jump
]
    
MOV DWORD PTR [ESP+1Ch],EAX    
// pStack.Eax <- EAX
    
POPAD  
// Restore the first registers context from stack
    //----------------------------------------------------
    // the structured exception handler (SEH) installation
    
PUSH EAX
    
XOR  EAX,
EAX
    PUSH DWORD PTR FS
:[0]        
// NT_TIB32.ExceptionList
    
MOV DWORD PTR FS:[0],ESP    
// NT_TIB32.ExceptionList <-ESP
    //----------------------------------------------------
    // the raise a INT 3 exception
    
DWORD_TYPE(0xCCCCCCCC
)
    
//--------------------------------------------------------
// -------- exception handler expression filter ----------
_except_handler1_OEP_Jump
:
    
PUSH EBP
    MOV EBP
,
ESP
    
//------------------------------
    
MOV EAX,DWORD PTR SS:[EBP+010h]    
// PCONTEXT: pContext <- EAX
    //==============================
    
PUSH EDI
    
// restore original SEH
    
MOV EDI,DWORD PTR DS:[EAX+0C4h]    
// pContext.Esp
    
PUSH DWORD PTR DS:[EDI
]
    
POP DWORD PTR FS:[0
]
    
ADD DWORD PTR DS:[EAX+0C4h],8    
// pContext.Esp
    //------------------------------
    // set the Eip to the OEP
    
MOV EDI,DWORD PTR DS:[EAX+0A4h]
// EAX <- pContext.Ebx
    
MOV DWORD PTR DS:[EAX+0B8h],EDI
// pContext.Eip <- EAX
    //------------------------------
    
POP EDI
    
//==============================
    
MOV EAX,
EXCEPTION_CONTINUE_SEARCH
    LEAVE
    RETN
//----------------------------------
    
DWORD_TYPE(DYN_LOADER_START_DATA1
)
//----------------------------------
_RO_dwImageBase:                DWORD_TYPE(0xCCCCCCCC
)
_RO_dwOrgEntryPoint:            DWORD_TYPE(0xCCCCCCCC
)
//----------------------------------
    
DWORD_TYPE(DYN_LOADER_END_MAGIC
)
//----------------------------------
}
}


 

. . . . . . . . . . . . . . . . . .
偶也玩起了BLOG,www.code47.blogcn.com

由 天魔降临 于 2006-03-05 09:34 PM 最后编辑

Old Post 2006-03-04 05:46 AM
点这里查看 天魔降临 的个人资料 点这里给 天魔降临 发送一条悄悄话 查找 天魔降临 的更多帖子 将 天魔降临 添加到你的好友列表    天魔降临的ICQ在线状态    天魔降临 的QQ号码:493381043 编辑/删除 引用/回复
天魔降临

路旁的落叶
路旁的落叶

来自:
金钱:423
发帖数: 59

注册日期: 2005-11-03

天魔降临 现在离线

No.5 代码注入(这不是水帖,因为J.Y MM在这,我才在这发的)5

6.构建一个引入表,并重建开始的引入表


我们有两种方法在应用程序中使用DLL文件:

.通过额外的支持使用库文件




.在运行时间中使用DLL文件

Code:


// DLL function signature
typedef HGLOBAL (*importFunction_GlobalAlloc)(UINT, SIZE_T
);
...
importFunction_GlobalAlloc __GlobalAlloc
;

// Load DLL file
HINSTANCE hinstLib = LoadLibrary("Kernel32.dll"
);
if (
hinstLib == NULL
)
{
    
// Error - unable to load DLL
}

// Get function pointer
__GlobalAlloc
=
    (
importFunction_GlobalAlloc)GetProcAddress(hinstLib
,  
                                         
"GlobalAlloc"
);
if (
addNumbers == NULL
)
{
     
// Error - unable to find DLL function
}

FreeLibrary(hinstLib
);


当你建立一个WINDOWS应用程序的工程,连接器至少包括kernel32.dll,没有kernel32.dll文件中的

LoadLibrary()和GetProcAddress()函数我们就不能在运行时间中装载DLL文件,所依赖的信息被储存在引入表

的节区内。如果利用Dependency Walker,我们将很容易的观察DLL模块,以及那些被引入PE文件的重要

功能。



我打算建立我们自定义的引入表来管理我们的工程,我们必须修改原来的引入表,以便于运行程序中真

正的代码。

PE Maker - Step 3


Download source files - 65.4 Kb

6.1构件客户端引入表


我建议你读一下6.4节
Microsoft Portable Executable and the Common Object File Format Specification

文挡,这节包含了重要的知识来让你了解引入表的性能。

利用optional header 中的第二页数据,便可得到引入表数据。因此,你能用下面的代码获得他

Code:

DWORD dwVirtualAddress
= image_nt_headers->
  
OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
;
DWORD dwSize = image_nt_headers
->
  
OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size
;


The VirtualAddress refers to structures by IMAGE_IMPORT_DESCRIPTOR. This structure contains the pointer to

the imported DLL name and the relative virtual address of the first thunk(抱歉,THUNK我不知道该如何翻译,

所以我无法理解这段的含义 )

Code:

typedef struct _IMAGE_IMPORT_DESCRIPTOR
{
    
union
{
        
DWORD   Characteristics
;
        
DWORD   OriginalFirstThunk
;
    };
    
DWORD   TimeDateStamp
;
    
DWORD   ForwarderChain
;
    
DWORD   <FONT color=red>Name</FONT>;         
// the imported DLL name
    
DWORD   <FONT color=red>FirstThunk</FONT>;   
// the relative virtual address of the first thunk
} IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR
;


When a program is running, the Windows task manager sets the thunks by the virtual address of the function. The

virtual address is found by the name of the function. At first, the thunks hold the relative virtual address of the

function name, Table 5; during execution, they are fixed up by the virtual address of the functions, Table 6.

表五--在文件映像中的引入表



表六--在虚拟内存中的引入表



我想制作一个简单的引入表来引入Kernel32.dll中的LoadLibrary(),和 GetProcAddress() 函数,在运行时间中我

要用这两个基本的API函数来覆盖其他的API函数。下面的ASM代码将展示实现他是多么的容易。



运行后.....




 

. . . . . . . . . . . . . . . . . .
偶也玩起了BLOG,www.code47.blogcn.com

由 天魔降临 于 2006-03-05 09:35 PM 最后编辑

Old Post 2006-03-05 03:20 AM
点这里查看 天魔降临 的个人资料 点这里给 天魔降临 发送一条悄悄话 查找 天魔降临 的更多帖子 将 天魔降临 添加到你的好友列表    天魔降临的ICQ在线状态    天魔降临 的QQ号码:493381043 编辑/删除 引用/回复
天魔降临

路旁的落叶
路旁的落叶

来自:
金钱:423
发帖数: 59

注册日期: 2005-11-03

天魔降临 现在离线

No.6 代码注入(这不是水帖,因为J.Y MM在这,我才在这发的)6

我已经准备了一个类库来制作每个引入表,这将借助于字符串。CITMaker类库在itmaker.h中,他将使用

sz_IT_EXE_strings 来建立一个引入表,以及一个相对虚拟地址引入表。

Code:


static const char *sz_IT_EXE_strings[]=
{
    
"Kernel32.dll"
,
    
"LoadLibraryA"
,
    
"GetProcAddress"
,
    
0
,,
    
0
,
};


下面我使用这个类库来建立引入表以支持DLL和OCX,所以这是一个常规库来表现所有可能的引入表。下

个一步骤,我用下面的代码来完成。



6.2在运行时间内使用其他的API函数

在这次,我们使用LoadLibrary() 和GetProcAddress()来装载其他的DLL文件,找出其他的API函数的进程地

址。



我想有一个完善的被引入函数表,类似于一个真正执行中的EXE文件,如果你看一下一个PE文件的内

部,你将发现API的调用是API函数在虚拟地址间的间接跳转完成的。

JMP DWORD PTR [XXXXXXXX]




借助于这个性能我们可以很容易的扩展我们工程的其他部分 ,因此我们建立两个数据表,一个是API函

数,另一个是JMP [XXXXXXXX]

Code:

#define __jmp_api               byte_type(0xFF) byte_type(0x25)
__asm
{
...
//----------------------------------------------------------------
_p_GetModuleHandle:             dword_type(0xCCCCCCCC
)
_p_VirtualProtect:              dword_type(0xCCCCCCCC
)
_p_GetModuleFileName:           dword_type(0xCCCCCCCC
)
_p_CreateFile:                  dword_type(0xCCCCCCCC
)
_p_GlobalAlloc:                 dword_type(0xCCCCCCCC
)
//----------------------------------------------------------------
_jmp_GetModuleHandle:           __jmp_api   dword_type(0xCCCCCCCC
)
_jmp_VirtualProtect:            __jmp_api   dword_type(0xCCCCCCCC
)
_jmp_GetModuleFileName:         __jmp_api   dword_type(0xCCCCCCCC
)
_jmp_CreateFile:                __jmp_api   dword_type(0xCCCCCCCC
)
_jmp_GlobalAlloc:               __jmp_api   dword_type(0xCCCCCCCC
)
//----------------------------------------------------------------
...
}


下面的代码我们来安装自定义的引入表



6.3修复最初的引入表

In order to run the program again, we should fix up the thunks of the actual import table, otherwise we have a

corrupted target PE file. Our code must correct all of the thunks the same as Table 5 to Table 6. Once more,

LoadLibrary() and GetProcAddress() aid us in our effort to reach our intention.(8会翻译,抱歉)




 

. . . . . . . . . . . . . . . . . .
偶也玩起了BLOG,www.code47.blogcn.com

由 天魔降临 于 2006-03-05 09:35 PM 最后编辑

Old Post 2006-03-05 04:53 AM
点这里查看 天魔降临 的个人资料 点这里给 天魔降临 发送一条悄悄话 查找 天魔降临 的更多帖子 将 天魔降临 添加到你的好友列表    天魔降临的ICQ在线状态    天魔降临 的QQ号码:493381043 编辑/删除 引用/回复
天魔降临

路旁的落叶
路旁的落叶

来自:
金钱:423
发帖数: 59

注册日期: 2005-11-03

天魔降临 现在离线

No.7 代码注入(这不是水帖,因为J.Y MM在这,我才在这发的)7

7.支持DLL和OCX

下面我将把DLL和OCX加入我们的PE工程中,如果我们注意到两次到达入口点的偏移地址,客户引入表,

重新调整后的表的话,要支持他们是很容易的事。

PE Maker - Step 4

Download source files - 68.6 Kb

7.1 Twice OEP approach

DLL或着OCX文件的入口点偏移最少要被主程序接触两次:

.构造[B]

当一个DLL文件被LoadLibrary()函数装载,或者通过调用 DllRegisterServer()函数使用LoadLibaray()和

GetProcAddress() 函数对一个OCX文件注册,第一次到达OEP就完成了。
Code:


hinstDLL
= LoadLibrary( "test1.dll" );


Code:

hinstOCX
= LoadLibrary( "test1.ocx" );
_DllRegisterServer = GetProcAddress( hinstOCX, "DllRegisterServer"
);
_DllRegisterServer();
// ocx register


[B].Destructor(破坏)


当主程序使用FreeLibrary()函数释放库文件,就会第二次到达OEP

Code:

FreeLibrary
( hinstDLL );


Code:

FreeLibrary
( hinstOCX );





To perform this, I have employed a trick, that causes in the second time again, the instruction pointer (EIP) traveling

owards the original OEP by the structured exception handler.(抱歉8会翻译)



我希望在前面的代码中你已经掌握了那个技巧,但是那还不够,我们还要考虑映像基址(ImageBase)的问

题,当库文件被主程序载入不同映像基址我们应该写一些代码来找到真正的映像基址,记住他并在将来

利用他。



借助于观察堆栈信息,这个代码找到真正的映像基址。通过使用真正的和形式上的映像基址映像基址,

我们要修正程序内部所有的内存调用,不要对此担心,通过重新调整表的信息就可以轻松的完成。

7.2 对表进行重新调整

为了更好的了解重置表(relocation table ),你应该看一看6.6节中
Microsoft Portable Executable and

Common Object File Format Specification
文档的内容。重置表包含许多package来包含在虚拟内存映像中虚拟

地址的相关信息。每一个package包含一个8bit的报头以显示虚拟地址基址和数据的数量。下面我用

IMAGE_BASE_RELOCATION的数据结构来说明。

Code:

typedef struct _IMAGE_BASE_RELOCATION
{
    
DWORD   VirtualAddress
;
    
DWORD   SizeOfBlock
;
}
IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION
;


表七


表七 表现了重置表的主要概念。此外你能在OD中装载一个DLL或OCX文件来观察重置表,在内存映

射窗口中便可观察到".reloc" 节区。使用我们工程中的下面代码,你能找到重置表的位置。

Code:

DWORD dwVirtualAddress
= image_nt_headers->
  
OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC
].
  
VirtualAddress
;
DWORD dwSize = image_nt_headers
->
  
OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size
;


借助于OD的Long Hex viewer模式,我们可以看到,在这个例子中虚拟内存基址是0x1000,块大小是

0x184。

Code:

008E1000
: 00001000  00000184  30163000  30403028
008E1010
:
30683054  308C3080  30AC309C  30D830CC
008E1020
:
30E030DC  30E830E4  30F030EC  310030F4
008E1030
:
3120310D  315F3150  31A431A0  31C031A8
008E1040
:
31D031CC  31F431EC  31FC31F8  32043200
008E1050
:
320C3208  32143210  324C322C  32583254
008E1060
:
3260325C  32683264  3270326C  32B03274


他重新安排了后面的虚拟地址:

Code:

0x1000
+ 0x0000 = 0x1000
0x1000
+ 0x0016 =
0x1016
0x1000
+ 0x0028 =
0x1028
0x1000
+ 0x0040 =
0x1040
0x1000
+ 0x0054 =
0x1054


每个package使用其内部连续的4字节表格来完成表的重置.第一个字节与重置表的类型有关,下面的连续三

个字节是那些必须用来纠正映像信息的虚拟地址基址和映像基址的偏移.



我所指的类型是什么?

我所说的类型是以下一系列值的一个:

.IMAGE_REL_BASED_ABSOLUTE (0):不操作(NO effect)

.IMAGE_REL_BASED_HIGH (1):重新部署虚拟地址和偏移地址的高十六字节。

.IMAGE_REL_BASED_LOW (2):重新部署虚拟地址和偏移地址的低十六字节。

.IMAGE_REL_BASED_HIGHLOW (3): 重新部署虚拟地址和偏移地址。

在重新部署是都做了什么?

通过重新部署,借助".reloc"节区packageS按照现在的映像基址,虚拟内存中的一些值被纠正。

Code:

delta_ImageBase
= current_ImageBase - image_nt_headers->OptionalHeader.ImageBase


Code:

mem
[ current_ImageBase + 0x1000 ] =
   
mem[ current_ImageBase + 0x1000 ] + delta_ImageBase
;
mem[ current_ImageBase + 0x1016
] =
   
mem[ current_ImageBase + 0x1016 ] + delta_ImageBase
;
mem[ current_ImageBase + 0x1028
] =
   
mem[ current_ImageBase + 0x1028 ] + delta_ImageBase
;
mem[ current_ImageBase + 0x1040
] =
   
mem[ current_ImageBase + 0x1040 ] + delta_ImageBase
;
mem[ current_ImageBase + 0x1054
] =
  
mem[ current_ImageBase + 0x1054 ] + delta_ImageBase
;
...


I have employed the following code from Morphine packer to implement the relocation.(抱歉实在翻译不通顺)



7.3建立一个特殊的引入表

为了支持 OLE-ActiveX控件注册,我们应该给予目标OCX和DLL文件一个特殊的入口表。

因此,我用如下的字符串建立了一个引入表:


Code:

const char *sz_IT_OCX_strings[]=
{
    
"Kernel32.dll"
,
    
"LoadLibraryA"
,
    
"GetProcAddress"
,
    
"GetModuleHandleA"
,
    
0
,
    
"User32.dll"
,
    
"GetKeyboardType"
,
    
"WindowFromPoint"
,
    
0
,
    
"AdvApi32.dll"
,
    
"RegQueryValueExA"
,
    
"RegSetValueExA"
,
    
"StartServiceA"
,
    
0
,
    
"Oleaut32.dll"
,
    
"SysFreeString"
,
    
"CreateErrorInfo"
,
    
"SafeArrayPtrOfIndex"
,
    
0
,
    
"Gdi32.dll"
,
    
"UnrealizeObject"
,
    
0
,
    
"Ole32.dll"
,
    
"CreateStreamOnHGlobal"
,
    
"IsEqualGUID"
,
    
0
,
    
"ComCtl32.dll"
,
    
"ImageList_SetIconSize"
,
    
0
,
    
0
,
};


缺少这些API函数,库文件就不能装载,此外 DllregisterServer() and DllUregisterServer()也不会起作用。

在CPECryptor::CryptFile中,在新的目标引入表创造时我对DLL和EXE文件做了区别。

Code:

if(( image_nt_headers->FileHeader.Characteristics
             
& IMAGE_FILE_DLL ) == IMAGE_FILE_DLL
)
{
    
ImportTableMaker = new CITMaker( IMPORT_TABLE_OCX
);
}
else
{
    
ImportTableMaker = new CITMaker( IMPORT_TABLE_EXE
);

}


8.保护线程局部存储

使用线程局部存储(TLS),程序能执行一个线状的进程,这个性能通常被Borland使用:Delphi和C++

Builder。当你封装一个PE文件,你应该注意保持TLS清空(you should take care to keep clean the TLS),否

则你的packer将不支持Borland Delphi and C++连接成EXE文件。请查阅6.7节的
Microsoft Portable Executable

and Common Object File Format Specification
文档

Code:

typedef struct _IMAGE_TLS_DIRECTORY32
{
    
DWORD   StartAddressOfRawData
;
    
DWORD   EndAddressOfRawData
;
    
DWORD   AddressOfIndex
;
    
DWORD   AddressOfCallBacks
;
    
DWORD   SizeOfZeroFill
;
    
DWORD   Characteristics
;
}
IMAGE_TLS_DIRECTORY32, * PIMAGE_TLS_DIRECTORY32
;


为了保证TLS目录的安全,我把他拷贝到装载者(loader)内部一个特殊的地方
Code:

...
_tls_dwStartAddressOfRawData:   dword_type(0xCCCCCCCC
)
_tls_dwEndAddressOfRawData:     dword_type(0xCCCCCCCC
)
_tls_dwAddressOfIndex:          dword_type(0xCCCCCCCC
)
_tls_dwAddressOfCallBacks:      dword_type(0xCCCCCCCC
)
_tls_dwSizeOfZeroFill:          dword_type(0xCCCCCCCC
)
_tls_dwCharacteristics:         dword_type(0xCCCCCCCC
)
...


It is necessary to correct the TLS directory entry in the Optional Header:(entry怕翻译错,这句就看英文把 )

Code:

if(image_nt_headers->
  
OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS
].
  
VirtualAddress!=0
)
{
    
memcpy(&pDataTable->image_tls_directory
,
           
image_tls_directory
,
           
sizeof(IMAGE_TLS_DIRECTORY32
));    
    
dwOffset=DWORD(pData1)-DWORD(pNewSection
);
    
dwOffset+=sizeof(t_DATA_1)-sizeof(IMAGE_TLS_DIRECTORY32
);
    
image_nt_headers
->
      
OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS
].
      
VirtualAddress=dwVirtualAddress + dwOffset
;
}


9.注入你的代码(PS:我已等了很久~~~~~~~~~~)

我已经放置了一个"Hello World!" 的代码

Code:

push MB_OK
| MB_ICONINFORMATION
lea eax
,[ebp+_p_szCaption
]
push eax
lea eax
,[ebp+_p_szText
]
push eax
push NULL
call _jmp_MessageBox
// MessageBox(NULL, szText, szCaption, MB_OK | MB_ICONINFORMATION) ;
   
...


PE Maker - Step 5

Download source files - 71.7 Kb



10.结论


通过该文你应该知道了把代码注射到PE文件中是多么容易~~~~~~~~(后面的都是废话不翻译了,吼吼~~~~~~~)





























 

. . . . . . . . . . . . . . . . . .
偶也玩起了BLOG,www.code47.blogcn.com

由 天魔降临 于 2006-03-05 09:36 PM 最后编辑

Mac安装Claude Code:Homebrew+pipx环境配置全指南 CLI工具是现代开发者工作流的核心组件,其本质是通过命令行接口调用后端服务实现自动化任务。原理上依赖Python运行时、PATH环境变量路径解析机制与安全的API密钥注入方式。技术价值在于零网络开销、本地化提示组装及与终端生态无缝集成。典型应用场景包括Git Hook自动代码审查、VS Code集成终端调用、CI/CD流水线嵌入等工程实践。本文聚焦macOS平台下Claude Code这一Anthropic官方CLI工具的可靠部署,深入解析Homebrew作为基础设施管理器的关键作用,以及pipx在隔离虚拟 阅读详情

相关推荐

macOS Homebrew 安装与配置全指南:Apple Silicon 适配与国内镜像实战

Homebrew 是 macOS 上事实标准的包管理器,其本质是基于 Git 元数据(Formulae)与预编译二进制(Bottles)协同工作的基础设施。它通过 Ruby 脚本定义构建逻辑,结合架构感知路径(如 /opt/homebrew)绕过 SIP 限制,在 Apple Silicon 和 Intel 双平台实现安全、可复现的命令行工具链部署。技术价值在于屏蔽系统级摩擦——包括 Gatekeeper 签名验证、依赖图谱解析与多版本共存管理,广泛应用于开发环境搭建、CI/CD 构建节点初始化及服务自启动

weixin_33911824的博客 442

给未加皮肤的EXE文件加皮肤

给未加皮肤的EXE文件加皮肤(加壳程序也可以)   用到的工具:LordPE.EXE   首先写一个测试用的EXE   再写一个关于皮肤的动态链接库我们命名为a.dll里面API接口命名为abc   把接口公开而且调用接口程序(名字可以任意密码)   步骤首先   1.打开LordPE.EXE   2.打开PE编辑器(可以把测试exe拖到LordPE.EXE上自动打开会PE编辑器)

陈刚编程心得与知识集 1402

Scoop:Windows 开发者环境即代码的实践指南

在现代软件开发中,'环境即代码'(Environment as Code)已成为提升可重复性、可审计性与团队协同效率的核心范式。其本质是将开发环境的配置、安装、版本切换与清理等操作,转化为可版本控制、可自动化执行、可精准回滚的技术流程。Scoop 作为 Windows 平台首个深度践行该理念的用户级包管理器,通过沙箱化安装、shim 代理机制、manifest 声明式定义及 persist 数据隔离等设计,实现了对 Python、Node.js、Git 等工具链的原子化管理。它不依赖管理员权限,规避注册表与

weixin_34178244的博客 341

PE格式文件的代码注入

 PE格式文件的代码注入   本文演示了在不需要重新编译源代码的情况下,怎样向Windows PE(Portable Executable)格式的文件(包括EXE、DLL、OCX)中注入自己的代码。 程序如图:         前言       或许,你想了解一个病毒程序是怎样把自身注入到一个正常的PE文件中的,又或者是,你为了保护某种数据而加密自己的P

谢启东的专栏 8399

PE脱壳软件修复LordPE_fix.EXE

程序名称:LordPE Deluxe 增强版 版 本:1.4 汉 化 人:cao_cong 联系方式:cao_cong_hx@yahoo.com.cn 使用说明: 这个工具大家应该也比较熟悉吧?这是另一款PE编辑工具,号称是“最好的PE文件修改工具”。这个增强版本是我在看雪学院上看到的,原来是看雪兄的大作。正好我以前汉化过这个软件(自己用的,没发布过),顺便套用一下以前的资源,把它给汉化了(里面所附带的工具基本上都汉化了)。可能有许多兄弟都比较喜欢用这个软件,这次发出来希望能给大家带来一点方便。这个增强版的主要更新(根据看雪兄的readme文件): (1) 为LordPE查看输入表部分加上搜索功能 (2) 为LordPE查看输入表部分加右键菜单(仅复制ThunkRVA/FirstThunk列). (3) 当点击LordPE查看输入表部分中"View always FirstThunk",保持光条在原来位置.(LordPE默认会将光条置到0行) (4) 修改FLC(File Location Calulator)窗口中各个文本框(VA,RVA,Offset)为只读属性,此时可以用鼠标复制里面的文本.(LordPE原来是将文本框禁止变灰,此时不可复制) 不过上面的第二条查看输入表部分的右键菜单我没看到。难道是我的系统(XP_SP2)有问题? 其它内容请看附带在内的readme.txt文件。LordPE的原版和增强版的原版我都放在英文原版文件夹中,大家可以进行比较。 声明: 1、此汉化软件是免费软件,请在转载时保留其内容的完整性! 2、此软件仅用于个人学习使用,禁止用于商业用途,否则后果自负! (1) 为LordPE查看输入表部分加上搜索功能 (2) 为LordPE查看输入表部分加右键菜单(仅复制ThunkRVA/FirstThunk列). (3) 当点击LordPE查看输入表部分中"View always FirstThunk",保持光条在原来位置.(LordPE默认会将光条置到0行) (4) 修改FLC(File Location Calulator)窗口中各个文本框(VA,RVA,Offset)为只读属性,此时可以用鼠标复制里面的文本.(LordPE原来是将文本框禁止变灰,此时不可复制) 文件列表: LordPE.EXE .............原版 LordPE_fix.EXE .............增强版 LordPlug.dll .............功能插件 LordPlug.dll_src .............功能插件源码

LordPE软件

LordPE是一款功能强大的PE文件分析、修改、脱壳软件。LordPE是查看PE格式文件信息的首选工具,并且可以修改相关信息。LordPE的最新版本,改进了许多东东,PE 编辑器的功能更加强大,加入了各项目的16进制编辑和列表,除了修补了LDS(LordPE Dumper Server)的一些bugs,还增加了一个LDE(LordPE Dumper Engine),支持新的插件格式,功能增加了,还加入了一些yoda自己写的软件,对了,还有一项新的功能,就是:全球首先支持新的 pe 文件格式---pe+,但是只是支持编辑,还不支持脱壳等别的操作。配合手动脱壳工具(Ollydbg等)、ImportREC 等,学习调试手动脱壳必备的工具!

【逆向基础】三、x32dbg使用技巧随记

对于出入C/C++逆向领域的小伙伴,熟练使用x32dbg,x64dbg是很有必要的;本文章简单介绍了工具的使用情况,供大家一起学习;

幸福之家的博客 9028

LordPE(PE文件分析、修改、脱壳软件)

LordPE,是一款功能强大的PE文件分析、修改、脱壳软件。LordPE是查看PE格式文件信息的首选工具,并且可以修改相关信息。

使用工具分析PE文件

使用工具分析PE文件。

qq_52185773的博客 3687

LordPE.EXE 简装绿色版LordPE 分析PE结构壳教研版

LordPE.EXE 简装绿色版LordPE 分析PE结构壳教研版

VX Portable Executable Viewer-开源

结合十六进制查看器,浏览 Windows 可移植可执行文件(标题、目录、部分、导出、导入)的结构。

Inject your code to a Portable Executable file

Downloads PE Viewer PE Maker - Step 1 - Add new Section. PE Maker - Step 2 - Travel towards OEP. PE Maker - Step 3 - Support Import Table. PE Maker - Step 4 - Support DL

3015

告别手动复制粘贴:3种高效合并Excel表格的方法对比(含代码)

本文对比了VBA、Power Query和Python三种高效合并Excel表格的方法,并提供了可直接运行的代码。针对不同数据量、更新频率和技术背景,详细分析了各方案的优缺点与适用场景,帮助读者告别低效的手动操作,实现自动化数据整合。

weixin_29057237的博客 442

Codex插件直连Claude Code:本地进程内委托实现AI编程工作流升级

AI编程助手正从单点问答工具演进为可建模的协作者,其核心瓶颈在于多模型协同时的延迟、上下文一致性与资源效率。本地进程内委托(In-process Delegation)作为一种新型集成范式,绕过HTTP API与跨进程通信,通过复用CLI环境、共享内存与Unix域套接字,显著降低IPC开销并保障Git、FS、Docker等运行时上下文完整性。该技术支撑标准审查、对抗质疑与任务移交三类高信噪比workflow,适用于已具备本地LLM运行能力、熟悉MCP协议及Claude Code Skill系统的进阶开发者,

weixin_30918633的博客 601

RK3566添加ffpeg【Buildroot系统】

有很多人都发现了RK3566系列的芯片是不可以直接使用ffmpeg官网源码的!会报错: Not a video capture device. /dev/video0: No such device 接下来我们就来解决这个问题!!

honmy18的博客 4070

新手小白入门编程第2讲 JAVA编程环境搭建 JDK与环境变量、Eclipse

1 JDK 1.1 JDK概述 JDK是 Java 语言的软件开发工具包,主要用于移动设备、嵌入式设备上的java应用程序。 JDK是整个java开发的核心,它包含了: JAVA开发工具(jdk\bin) 基础开发库(jdk\jre\lib\rt.jar) 基础开发库的源码(jdk\src.zip) 1.2 JDK下载与安装 本次使用的是JDK1.8,详细安装步骤可以参见”JDK培优安装手册”. 注意事项: 同时安装多个JDK时需要使用执行环境变量来确认使用的是哪个jdk 安装时选择默认选项安装到默认

程序媛泡泡的博客 4万+

5分钟掌握EXE转DLL的终极工具:exe_to_dll完整指南

在Windows开发领域,**EXE到DLL转换**技术一直是逆向工程和代码复用的重要课题。今天,我们将深入探讨一款专业的**EXE转DLL工具**——exe_to_dll,它不仅支持32位和64位架构,还能将可执行文件无缝转换为动态链接库,为开发者提供前所未有的代码复用解决方案。 ## 🔧 为什么需要EXE转DLL? ### 核心应用场景 **代码模块化复用**:将独立的EXE程序转换为

gitblog_00445的博客 521

流体动力学仿真软件:Star-CCM+_(19).软件定制与脚本编写

自定义插件开发可以扩展Star-CCM+的功能,实现特定的仿真需求。插件开发通常需要使用Java语言,并且需要对Star-CCM+的API有深入的了解。以下是一个简单的Java插件示例,展示如何创建一个自定义插件来显示仿真项目的信息。// 导入必要的Star-CCM+ API import star . ccm . product . CCMProject;// 自定义插件类 public class CustomPlugin {// 插件主方法 public void run() {

kkchenjj的博客 937

linux脚本打不开文件夹,为什么这个程序向文件地址和内存地址不一样的文件加入shellcode会导致文件打不开...

[C] 纯文本查看 复制代码#include "iostream"#include #define size_shellcode 0x12#define messagebox_add 0x76c9fdf6BYTE shellcode[] = {0x6A,00,0x6A,00,0x6A,00,0x6A,00,0XE8,00,00,00,00,0XE9,00,00,00,00};char file_p...

weixin_39668965的博客 106

把 SAP 输出稳稳落到 PDF:从 SPAD 前端打印到 ABAP 程序级转换的一套方法

摘要:本文介绍了三种在SAP系统中将报表输出转为PDF文件的方法。路线A通过SPAD配置前端输出设备,结合Windows打印机设置实现用户交互式PDF保存;路线B在SP01事务码中将已有Spool请求另存为PDF,适用于补救导出;路线C通过ABAP程序自动转换Spool为PDF,适合批量处理。文章详细说明了配置步骤,并针对常见问题(如乱码、文件格式错误等)提供了排查思路。三种方案分别适用于不同场景,用户可根据实际需求选择最合适的PDF导出方式。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 221

AutoGLM-Phone 实现“豆包 AI 手机”的自动化操作手机的实战方案教程 让普通手机秒变ai手机

是一个基于视觉语言模型(VLM)的 AI 手机智能助理框架。❌ 不依赖固定坐标❌ 不需要提前录制脚本✅ 能“看懂”手机屏幕✅ 能理解自然语言指令✅ 能自动规划操作路径并执行让 AI 像人一样看屏幕、想步骤、点手机。从工程角度看,AutoGLM-Phone 已经具备“AI 手机助理”的完整雏形能看能理解能规划能执行能跨 App所谓的「豆包 AI 手机」,本质上就是这类能力的产品化封装。而现在,这套能力已经可以被普通开发者亲手跑起来、改起来、甚至接入自己的业务场景。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 593

从数字考古到现代防御:PE结构在安全演进中的双重角色

本文探讨了PE文件结构在Windows安全演进中的双重角色,既作为历史遗产承载早期系统设计哲学,又在现代防御中对抗代码注入等攻击技术。通过分析PE核心组成、节表特性和内存映射机制,揭示了其在恶意软件分析和EDR防护中的关键作用,为安全研究人员提供深入洞察和实战分析技术。

z4a5b6的博客 160

NSGA-II 多目标优化 —— 理论、案例与交互式 GUI 实现

本文详细介绍了 NSGA-II 多目标优化算法的基本原理、数学模型和实现流程。从非支配排序、拥挤距离计算到遗传操作,NSGA-II 通过对种群进行分层和排序,能够有效求解多目标优化问题,获得 Pareto 前沿解集。通过机械结构优化、资源分配和控制参数调优等典型案例,我们展示了 NSGA-II 在实际工程中的广泛应用。为帮助工程师和研究者更直观地理解算法求解过程,本文提供了基于 Python 与 PyQt6 的交互式 GUI 演示系统代码示例。

闲人编程的博客 2120

Windows AI工具链统一配置方案:免改环境变量的跨工具协同

AI编程工具链在Windows平台长期面临配置碎片化问题——Codex Desktop用TOML、Claude Code依赖.env、Gemini CLI需命令行参数,彼此隔离且无法共享API密钥、模型选择与MCP服务。其根源在于Windows缺乏轻量级、进程级、可审计的环境变量调度层。本方案基于Tauri+Rust构建原生Windows配置中枢,通过注册表劫持、WebView2沙箱、SQLite原子写入与进程级环境注入等技术,实现多工具配置自动同步与热切换。它不替代原有工具,而是作为‘操作系统层’提供统一

weixin_30263073的博客 479

如何在 Ubuntu 24.04 LTS 上安装 SSH 服务器?

Ubuntu是一个基于Debian的开源Linux操作系统,由Canonical Ltd.开发和维护。Ubuntu致力于提供用户友好、稳定且安全的操作系统环境,适用于个人用户、企业和服务器等不同场景。Ubuntu 24.04 LTS是Ubuntu发布的一个长期支持版本,LTS代表长期支持(Long Term Support)。这意味着Ubuntu 24.04 LTS将获得长达5年的安全更新和维护支持,使其成为企业和个人用户的理想选择。

网络技术联盟站 5888

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

上一篇: 小巧的驱动测试工具
下一篇: 黑客之门的魅力:感染与加载
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值