首页
社区
课程
招聘
如何通过编程实现,Kernel32.dll中导出函数的大小识别?
2020-10-10 19:05 4283

如何通过编程实现,Kernel32.dll中导出函数的大小识别?

2020-10-10 19:05
4283

请教老哥们一个问题:如何识别一个函数?
例: 0x1000是函数的开始地址,如果判断这个函数的结束地址?
换句话说,如何计算该函数的大小, 不借助动态分析。
已知:https://www.52pojie.cn/thread-1088258-1-1.html
该贴中给出方法如下:
比较跳转目的地址集合A与ret指令地址集合B。
如果A中的最大值 > B中的最大值, 则需要继续进行探索,丰富集合A和B;
如果A中的最大值 > B中的最大值, 则证明不再需要探索,ret最大值地址 - 函数开始地址 = 函数大小。
请问各位老哥,这个问题有更加专业的称呼?有其他更加的解决办法吗?如果能给出几篇Paper,那就更好了。谢谢。


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2020-10-10 19:06 被baolongshou编辑 ,原因:
收藏
点赞0
打赏
分享
最新回复 (18)
雪    币: 6517
活跃值: (8420)
能力值: ( LV17,RANK:787 )
在线值:
发帖
回帖
粉丝
无名侠 12 2020-10-10 20:56
2
0
IDA 有时候识别都不太准确
雪    币: 4974
活跃值: (3688)
能力值: ( LV13,RANK:270 )
在线值:
发帖
回帖
粉丝
baolongshou 2 2020-10-10 21:15
3
0
一楼老哥,我只想识别Kernel32.dll中的函数。这些函数应该是比较规范的,没有乱起八糟的混淆。
雪    币: 9614
活跃值: (1826)
能力值: ( LV5,RANK:73 )
在线值:
发帖
回帖
粉丝
Sprite雪碧 1 2020-10-10 21:27
4
0
识别别人的函数长度是没法做到百分百准确的,可以参考前辈的文章:https://bbs.pediy.com/thread-102977.htm
雪    币: 4016
活跃值: (5833)
能力值: ( LV7,RANK:102 )
在线值:
发帖
回帖
粉丝
fjqisba 2020-10-10 22:56
5
0
应该就是控制流程图的生成----Control Flow Graph,一般情况都是可以识别的,但是特殊情况却很难处理。参考x64dbg中的Graph生成代码,和IDA的控制流程图生成。
雪    币: 3126
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
我来自南方 2020-10-10 23:06
6
0
函数的表现形式是字节,要统计大小那也只能是统计这个函数的入口地址到结束地址占用的字节数,我理解在函数的入口地址-ret的地址之间的字节数量不就是函数的大小吗?
雪    币: 9614
活跃值: (1826)
能力值: ( LV5,RANK:73 )
在线值:
发帖
回帖
粉丝
Sprite雪碧 1 2020-10-11 02:47
7
0
我来自南方 函数的表现形式是字节,要统计大小那也只能是统计这个函数的入口地址到结束地址占用的字节数,我理解在函数的入口地址-ret的地址之间的字节数量不就是函数的大小吗?
    ...
    jxx $L1
    ret
$L1:
    ...
    ret

这种情况就炸了

最后于 2020-10-11 02:48 被Sprite雪碧编辑 ,原因:
雪    币: 13397
活跃值: (4733)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tDasm 2020-10-11 07:31
8
0
你能告诉我有什么作用?如果没什么用,就是花架子,建议别搞。
雪    币: 4974
活跃值: (3688)
能力值: ( LV13,RANK:270 )
在线值:
发帖
回帖
粉丝
baolongshou 2 2020-10-11 08:21
9
0
我来自南方 函数的表现形式是字节,要统计大小那也只能是统计这个函数的入口地址到结束地址占用的字节数,我理解在函数的入口地址-ret的地址之间的字节数量不就是函数的大小吗?
但可能存在多个ret
雪    币: 4974
活跃值: (3688)
能力值: ( LV13,RANK:270 )
在线值:
发帖
回帖
粉丝
baolongshou 2 2020-10-11 08:22
10
0
tDasm 你能告诉我有什么作用?如果没什么用,就是花架子,建议别搞。
我研究上需要用到
雪    币: 4974
活跃值: (3688)
能力值: ( LV13,RANK:270 )
在线值:
发帖
回帖
粉丝
baolongshou 2 2020-10-11 08:23
11
0
Sprite雪碧 识别别人的函数长度是没法做到百分百准确的,可以参考前辈的文章:https://bbs.pediy.com/thread-102977.htm
谢谢老哥
雪    币: 12837
活跃值: (8993)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
hzqst 3 2020-10-11 09:33
12
0
x64可以从pdata里识别
雪    币: 11227
活跃值: (4799)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
hhkqqs 1 2020-10-11 09:49
13
0
微软编译的函数也有看上去不太标准的,ntoskrnl里面就见过几个在不同区段间跳转的函数,而且跳转地址比函数入口还小,单纯比较max{retaddress}肯定行不通的
雪    币: 13397
活跃值: (4733)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tDasm 2020-10-11 13:50
14
0
baolongshou 我研究上需要用到
要有用才值得研究。你都不知道那就说明毫无作用。



雪    币: 4974
活跃值: (3688)
能力值: ( LV13,RANK:270 )
在线值:
发帖
回帖
粉丝
baolongshou 2 2020-10-11 16:43
15
0
tDasm 要有用才值得研究。你都不知道那就说明毫无作用。
行吧
雪    币: 6
活跃值: (164)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lmwjt 2020-10-12 10:29
16
0
x64里面的 pdata的 Runtime_Function
雪    币: 2914
活跃值: (2809)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
boursonjane 2020-10-12 10:48
17
0

我写过这个功能, 就输入一个起始地址, 然后输出函数范围的数组.
主要就遍历条件跳转将目的地址加入todolist, 无条件跳转以及ret类指令当终点, 得到所有的beginrva和endrva之后, 判断是否有重叠, 有重叠就合成.
之后就根据目标汇编代码增加条件跳转的处理, 那种jcc imm的好处理, 很多其他的mov r32,xx再jmp r32这种就看情况一个个情况加.

最后于 2020-10-12 10:49 被boursonjane编辑 ,原因:
雪    币: 2914
活跃值: (2809)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
boursonjane 2020-10-12 10:58
18
0
#define GE_X_LE(a,x,b) (((x)>=(a)) && ((x) <= (b)))
#define GE_X_LS(a,x,b) (((x)>=(a)) && ((x) <  (b)))
#define GT_X_LE(a,x,b) (((x)> (a)) && ((x) <= (b)))
#define GT_X_LS(a,x,b) (((x)> (a)) && ((x) <  (b)))

typedef struct range
{
	DWORD startrva;
	DWORD dwsize;
	DWORD endrva;   //endrva = startrva + dwsize - 1
}*prange;

int myidgroup1[] =
{
	X86_INS_JAE,
	X86_INS_JA,
	X86_INS_JBE,
	X86_INS_JB,
	X86_INS_JCXZ,
	X86_INS_JECXZ,
	X86_INS_JE,
	X86_INS_JGE,
	X86_INS_JG,
	X86_INS_JLE,
	X86_INS_JL,
	X86_INS_JNE,
	X86_INS_JNO,
	X86_INS_JNP,
	X86_INS_JNS,
	X86_INS_JO,
	X86_INS_JP,
	X86_INS_JRCXZ,
	X86_INS_JS,
	0
};


int checkid(int id, int* myidset)
{
	while (*myidset)
	{
		if (id == *myidset)
		{
			return true;
		}
		++myidset;
	}
	return false;
}

bool comparer(range& ra, range& rb)
{
	return ra.startrva < rb.startrva;
}

bool checkcommon(range& ra, range& rb)
{
	if (ra.endrva < rb.startrva - 1 || rb.endrva < ra.startrva - 1) 
	{
		return false;
	}
	return true;
}

int functionparser::getfuncrange(DWORD dwfuncrva, _Inout_ std::vector<range>& rangelist, BYTE* pebase)
{
	csh handle;
	cs_insn* insn;
	size_t count;

	if (cs_open(CS_ARCH_X86, CS_MODE_64, &handle) != CS_ERR_OK)
		return CAPSTONE_FAIL;

	cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON);

	DWORD dwprevrva = dwfuncrva;
	DWORD nextrva = 0;

	std::vector<DWORD> todorva;

	BYTE* tempdata = NULL;
	while (true)
	{
		for (int i = 0;;)
		{
			count = cs_disasm(handle, pebase + rva2fo(pebase, dwprevrva) + i, 0x10, dwprevrva + i, 1, &insn);  

			if (count == 0)
			{
				tempdata = pebase + rva2fo(pebase, dwprevrva) + i;

				//capstone好像没支持这个指令, 我临时修补一下.
				//0F 01 FA                          monitorx rax, rcx, rdx
				if (tempdata[0] == 0xf && tempdata[1] == 0x1 && tempdata[2] == 0xfa) 
				{
					i += 3;
					continue;
				}
				//0F 01 FB                          mwaitx  rax, rcx, rbx
				if (tempdata[0] == 0xf && tempdata[1] == 0x1 && tempdata[2] == 0xfb)
				{
					i += 3;
					continue;
				}
				//F3 48 0F 1E CA              rdsspq  rdx

				if (*(DWORD*)tempdata == 0x1e0f48f3 && tempdata[4] == 0xca)
				{
					i += 5;
					continue;
				}

			}

#pragma region α
			////注意 跟β的顺序不能颠倒
			//发现条件跳转 或 jmp
			if (checkid(insn[0].id,myidgroup1) || insn[0].id == X86_INS_JMP)
			{
				//得到跳转目标地址
				DWORD dwdest = strtoul(insn[0].op_str, NULL, 16);

				//assert(dwdest != 0);

				
				if (GE_X_LE(dwprevrva, dwdest, dwprevrva + i + insn[0].size) == FALSE && dwdest != 0)
				{
					bool everbeenbefore = false;

					for (auto& fuck : rangelist)
					{
						if (GE_X_LE(fuck.startrva, dwdest, fuck.endrva))
						{
							everbeenbefore = true;
							break;
						}
					}
					for (auto& fuck : todorva)
					{
						if (dwdest == fuck)
						{
							everbeenbefore = true;
							break;
						}
					}

					if (everbeenbefore == false)
					{
						todorva.push_back(dwdest);
					}
				}

			}
#pragma endregion

#pragma region β
			//发现终点, 保存本块区域, 看是否有其他路径点未遍历
			if (cs_insn_group(handle, insn, CS_GRP_RET) || insn[0].id == X86_INS_JMP)
			{
				range temp = { 0 };
				temp.startrva = dwprevrva;
				temp.endrva = dwprevrva + i + insn[0].size - 1;
				temp.dwsize = i + insn[0].size;
				rangelist.push_back(temp);

				//将todorva中, 本块走过的rva全部删除
				for (auto damn = todorva.begin(); damn != todorva.end(); )
				{
					if (GE_X_LE(dwprevrva, *damn, dwprevrva + i + insn[0].size - 1))
					{
						damn = todorva.erase(damn);
					}
					else
					{
						++damn;
					}
						
				}

				if (todorva.empty())
				{
					//没有未遍历的了..
					goto outofwhile;
				}
				else
				{
					nextrva = todorva.back();
					todorva.pop_back();
					goto outoffor;
				}
			}
#pragma endregion


			i += insn[0].size;

			cs_free(insn, count);
		}

outoffor:
		dwprevrva = nextrva;
	}

	cs_close(&handle);

outofwhile:

	//开始合并rangelist
	//先对rangelist按照startrva升序排列

	if (rangelist.size() > 1)
	{
		std::sort(rangelist.begin(), rangelist.end(), comparer);


		//因为已经排序过, 所以只需要判断相邻两个是否能合成即可
		for (auto iter = rangelist.begin(); iter != rangelist.end() - 1;)
		{
			if (checkcommon(*iter, *(iter + 1)))
			{
				(*iter).startrva = min((*iter).startrva, (*(iter + 1)).startrva);
				(*iter).endrva = max((*iter).endrva, (*(iter + 1)).endrva);
				(*iter).dwsize = (*iter).endrva - (*iter).startrva + 1;

				iter = rangelist.erase(iter + 1);
				--iter;
			}
			else
			{
				++iter;
			}
		}
	}

	return 0;
}

我这代码对我当时研究的已经够用了, 你的可能得改改, 库用的capstone

雪    币: 4974
活跃值: (3688)
能力值: ( LV13,RANK:270 )
在线值:
发帖
回帖
粉丝
baolongshou 2 2020-10-12 14:49
19
0
boursonjane #define&nbsp;GE_X_LE(a,x,b)&nbsp;(((x)&gt;=(a))&nbsp;&amp;&amp;&nbsp;((x ...

好的,我好好研究一下,谢谢大佬

最后于 2020-10-12 14:54 被baolongshou编辑 ,原因:
游客
登录 | 注册 方可回帖
返回