BeaEngine反汇编引擎
BeaEngine反汇编引擎
下载
https://github.com/BeaEngine/beaengine
编译
编译就没什么好说的了.
我按照运行库分别生成了四份
注意:编译lib时,预处理加上这两句
#define BEA_ENGINE_STATIC // 指明使用静态Lib库
#define BEA_USE_STDCALL // 指明使用stdcall调用约定
使用
把他的头文件 BeaEngine.h和Includes文件夹里的两个文件 放到自身目录下
然后把四个静态库放到lib目录下
自身调用的头文件加上预编译宏
//->使用lib 也要加上这两句
#define BEA_ENGINE_STATIC // 指明使用静态Lib库
#define BEA_USE_STDCALL // 指明使用stdcall调用约定
#ifdef __cplusplus
extern "C" {
#endif
#include "BeaEngine.h"
#ifdef _DEBUG
#ifdef _DLL
#pragma comment(lib, "./lib/BeaEngine_MD_d.lib")
#else
#pragma comment(lib, "./lib/BeaEngine_MT_d.lib")
#endif
#else //发布版本
#ifdef _DLL
#pragma comment(lib, "./lib/BeaEngine_md.lib")
#else
#pragma comment(lib, "./lib/BeaEngine_mt.lib")
#endif
#endif
#ifdef __cplusplus
};
#endif
Disasm
指令分析函数
BEA_API int __bea_callspec__ Disasm (LPDISASM pDisAsm);
//传入LPDISASM指针 成功返回的是一条指令的长度,失败就返回的是负数
结构体成员解释
typedef struct _Disasm {
UIntPtr EIP;
UInt64 VirtualAddr;
UInt32 SecurityBlock;
char CompleteInstr[INSTRUCT_LENGTH];
UInt32 Archi;
UInt64 Options;
INSTRTYPE Instruction;
OPTYPE Operand1;
OPTYPE Operand2;
OPTYPE Operand3;
OPTYPE Operand4;
OPTYPE Operand5;
OPTYPE Operand6;
OPTYPE Operand7;
OPTYPE Operand8;
OPTYPE Operand9;
PREFIXINFO Prefix;
Int32 Error;
UInt32 Reserved_[48];
} DISASM, *PDISASM, *LPDISASM;
DISASM.EIP:引擎进行解码的地址
DISASM.VirtualAddr:可选 - (用于指令 CALL - JMP - 条件 JMP - LOOP)默认情况下,此值为 0(禁用)。反汇编程序使用 VirtualAddr(不是 EIP)计算分支指令的目标地址。该地址可以是 64 位长。此选项允许我们解码位于内存中任何位置的指令,即使它们不在其原始位置。
DISASM.CompleteInstr:用于存储完整指令的字符串。
DISASM.Archi:解码的架构,0 = IA-32 ,1 = Intel 64
DISASM.Options:显示解码风格,在他们官网上有写
DISASM.Instruction:分析的指令信息
DISASM.Operand1:分析的操作数信息
DISASM.Prefix:分析的前缀信息,可以查看段寄存器等
DISASM.Error:错误信息
DISASM.Reserved_:保留
那么就来调用一下Disasm函数看看
DISASM Op_Disasm = { 0 };
Op_Disasm.EIP = (DWORD64)main; //查看main函数
Op_Disasm.Options = Tabulation + MasmSyntax + ShowSegmentRegs;
Op_Disasm.Archi = 0; //64位
int nRet = Disasm(&Op_Disasm);
调用后返回值是5,说明调用成功了
然后再看一下DISASM结构体里存了什么
名称 | 值 | 类型 | |
---|---|---|---|
EIP | 0x00007ff6ecde3c3b | unsigned __int64 | |
VirtualAddr | 0x0000000000000000 | unsigned __int64 | |
SecurityBlock | 0x00000000 | unsigned int | |
▶ | CompleteInstr | 0x00000095d894d034 "jmp 00007FF6ECECC4F0h" | char[0x00000050] |
Archi | 0x00000000 | unsigned int | |
Options | 0x0000000001000001 | unsigned __int64 | |
▶ | Instruction | INSTRTYPE | |
▶ | Operand1 | OPTYPE | |
▶ | Operand2 | OPTYPE | |
▶ | Operand3 | OPTYPE | |
▶ | Operand4 | OPTYPE | |
▶ | Operand5 | OPTYPE | |
▶ | Operand6 | OPTYPE | |
▶ | Operand7 | OPTYPE | |
▶ | Operand8 | OPTYPE | |
▶ | Operand9 | OPTYPE | |
▶ | Prefix | PREFIXINFO | |
Error | 0x00000000 | int | |
▶ | Reserved_ | 0x00000095d894d8c4 | unsigned int[0x00000030] |
挑对我们有用的几个成员
EIP 就是开始解析指令的内存地址
CompleteInstr:当前指令的解析的文本信息
Instruction:指令分析的信息
名称 | 值 | 类型 | |
---|---|---|---|
◢ | Instruction | INSTRTYPE | |
Category | 0x00010006 | int | |
Opcode | 0x000000e9 | int | |
▶ Mnemonic | 0x00000095d894d098 "jmp" | char[0x00000018] | |
BranchType | 0x0000000b | int | |
▶ Flags | EFLStruct | ||
AddrValue | 0x00007ff6ececc4f0 | unsigned __int64 | |
Immediat | 0x0000000000000000 | __int64 | |
▶ ImplicitModifiedRegs | REGISTERTYPE | ||
▶ ImplicitUsedRegs | REGISTERTYPE |
Category:分类,我没仔细研究这个,应该是指令集相关的
Opcode:操作码
Mnemonic:操作码解释
BranchType:分支类型,就是JCC跳转 CALL JMP 之类的. 改变EIP的指令的类型
Flags:标志位。SF ZF PF 那些
AddrValue:附加值.可以理解为 要跳转的指令地址
后面那三个没用到,暂时不知道干啥用的
大部分已经分析完了.main函数第一条指令就是 jmp 00007FF6ECECC4F0h(因为我是DEBUG版本)
那这有趣的就来了.我们可以用这个库来分析指令跳转的到哪里, 函数的大小,查找特征码. 之类的功能
下面是我简单封装的几个功能
GetDisasmLenght
// @取指令长度
ULONG ACEHook::GetDisasmLenght(PVOID Address)
{
DISASM Op_Disasm = { 0 };
Op_Disasm.EIP = (DWORD64)Address;
Op_Disasm.Options = Tabulation + MasmSyntax + ShowSegmentRegs;
Op_Disasm.Archi = 0;
return Disasm(&Op_Disasm);
}
// @取指令长度次数
ULONG64 ACEHook::GetDisasmLenght(PVOID Address, ULONG64 Count)
{
ULONG AsmLenght = 0; //指令长度
DWORD64 dwAddress = (DWORD64)Address;
for (int i = 0; i < Count; i++)
{
AsmLenght += GetDisasmLenght((PVOID)(dwAddress + AsmLenght));
}
return AsmLenght;
}
GetDisamCompleteInstr
/*
* @指令解释
* @Param:内存地址
* @Param:次数
* @返回反汇编指令
*/
std::string ACEHook::GetDisamCompleteInstr(PVOID Address, ULONG64 ulCount)
{
std::string OutText = "\n";
DWORD64 dwAddress = (DWORD64)Address;
for (size_t i = 0; i < ulCount; i++)
{
ULONG64 uLenght = 0;
DISASM Op_Disasm = GetDisasmInfo((PVOID)dwAddress, uLenght);
//没有下条指令了.跳出
if (uLenght == OUT_OF_BLOCK)
{
break;
}
else if (uLenght == UNKNOWN_OPCODE)
{
//无法解析的指令.跳过.
dwAddress += 1;
continue;
}
dwAddress += uLenght;
OutText.append(fmt::format("{:X} > {}", (DWORD64)Op_Disasm.EIP, Op_Disasm.CompleteInstr));
OutText.append("\n");
}
return OutText;
}
GetScanOpCodeByAddress
struct ACE_OPCODE
{
ULONG64 StartAddress;
BOOL Status;
ULONG64 Eip;
ULONG64 Len;
ULONG64 OpCode;
ULONG64 AddrValue; //要跳转的地址
ULONG64 Value;
std::string Complete;
inline ACE_OPCODE()
{
Status = FALSE;
Eip = 0;
Len = 0;
OpCode = 0;
AddrValue = 0;
Value = 0;
Complete = "";
}
template<typename OStream>
friend OStream& operator<<(OStream& os, const ACE_OPCODE& c)
{
auto Text = fmt::format(xorstr_("[ACE_OPCODE StartAddress:0x{:X} Status:{} Eip:0x{:X} Len:0x{:X} OpCode:0x{:X} AddrValue:0x{:X} Value:0x{:X} Complete:{} ]"), c.StartAddress, c.Status, c.Eip, c. Len, c.OpCode, c. AddrValue, c.Value,c.Complete);
return os << Text;
}
};
/*
* @查找特征指令
* @param 开始地址
* @param 长度
* @param 次数
* @param 要查找的指令类型
* @返回 ACE_OPCODE
*/
ACE_OPCODE ACEHook::GetScanOpCodeByAddress(PVOID Address, ULONG64 Lenght, ULONG64 Count, ACE_SCAN_OPCODE_TYPE Type)
{
m_OpCode.Status = FALSE;
m_OpCode.StartAddress = (DWORD64)Address;
DISASM Op_Disasm = { 0 };
Op_Disasm.EIP = (DWORD64)Address;
Op_Disasm.Options = MasmSyntax + ShowSegmentRegs;
//结束地址
ULONG64 EndAddres = (DWORD64)Address + Lenght;
int len = 0;
while (Op_Disasm.EIP < EndAddres)
{
Op_Disasm.Archi = 0;
len = Disasm(&Op_Disasm);
//没有下条指令了.跳出
if (len == OUT_OF_BLOCK)
{
break;
}
else if (len == UNKNOWN_OPCODE)
{
//无法解析的指令.跳过.
Op_Disasm.EIP += 1;
continue;
}
INSTRTYPE* InstType = &Op_Disasm.Instruction;
auto CopyResult = [&]() -> void
{
m_OpCode.Status = TRUE;
m_OpCode.Eip = Op_Disasm.EIP;
m_OpCode.Len = len;
m_OpCode.OpCode = InstType->Opcode;
m_OpCode.AddrValue = InstType->AddrValue;
m_OpCode.Value = InstType->Immediat;
m_OpCode.Complete = Op_Disasm.CompleteInstr;
};
if (LOWORD(InstType->Category) == CONTROL_TRANSFER) //控制指令
{
//查找CALL -> 查找JMP ->查找Ret
if ((Type & ACE_SCAN_OPCODE_TYPE::ACE_SCAN_OPCODE_TYPE_CALL) && InstType->BranchType == CallType || (Type & ACE_SCAN_OPCODE_TYPE::ACE_SCAN_OPCODE_TYPE_JMP) && InstType->BranchType == JmpType || (Type & ACE_SCAN_OPCODE_TYPE::ACE_SCAN_OPCODE_TYPE_RET) && InstType->BranchType == RetType)
{
if (--Count <= 0)
{
CopyResult();
break;
}
}
//查找->Int3
else if ((Type & ACE_SCAN_OPCODE_TYPE::ACE_SCAN_OPCODE_TYPE_INT3 )&& InstType->BranchType == 0 && InstType->Opcode == 0xCC)
{
if (--Count <= 0)
{
CopyResult();
break;
}
}
}
Op_Disasm.EIP += (UIntPtr)len;
}
return m_OpCode;
}
GetCallAddress
/*
* @返回CALL地址
*/
DWORD64 ACEHook::GetCallAddress(DWORD64 dwAddress)
{
return GetScanOpCodeByAddress((PVOID)dwAddress, 0x10, 1, ACE_SCAN_OPCODE_TYPE_CALL).AddrValue;
}
利用这些 我们可以自动Hook windowsAPI 头部 自动找填充的指令,自动查找可以Hook的地方.等等