BeaEngine反汇编引擎

BeaEngine反汇编引擎

下载

https://github.com/BeaEngine/beaengine

编译

编译就没什么好说的了.

我按照运行库分别生成了四份

image-20220815202752974

image-20220815202757954

注意:编译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:显示解码风格,在他们官网上有写

image-20220815204336639

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,说明调用成功了

image-20220815205757483

然后再看一下DISASM结构体里存了什么

image-20220815205901527

名称 类型
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的地方.等等

posted @ 2022-08-15 21:34  Saga007  阅读(907)  评论(1编辑  收藏  举报