首页 > 编程语言 >反汇编strlen函数

反汇编strlen函数

时间:2023-03-18 18:13:36浏览次数:47  
标签:字节 函数 eax 反汇编 ff strlen asm ecx

0. 本文结构概述

  1. strlen库函数实现原理
  2. 公式分析(变换形式)
  3. 计算步骤分析(有些繁琐,后续会修改优化)
  4. C语言实现该函数
  5. 汇编语言实现该函数

1. strlen库函数实现原理

使用VS2019进行反汇编,发现字符串操作的库函数,多数都是汇编实现的(没有看完所有的)。按照我预想的strlen函数计算字符串长度,就是一位一位的判断是否为'\0',然而库函数是4位比较。简要步骤如下所示:

  • 步骤1:判断地址是否为 4字节对齐
    为什么要判断4字节对齐呢? 【CPU一次能取多少内存需要看总线的位数,如果是32位,一次读取4个字节,而且CPU不能跨内存区间访问。也就是说如果内存地址为编号为0x00000003-0x00000007,想取这4个地址内的数据,CPU要取两次。这样的效率太低了,所以需要等地址为4字节对齐时,再以4字节取出判断。】

    • 1.1 公式:addr & 0x11 == 0
    • 1.2 若结果为0,说明 addr 能被4整除,addr 是4字节对齐的,直接进入步骤2
    • 1.3 若不为0,需要按位判断是否为 '\0',然后再次进行 1.1 步骤
  • 步骤2:通过运算判断此4字节中是否有 '\0'

    • 公式:
      a  = 值 + 0x7efefeff
      值 = 值 ^ 0xffffffff
      值 = 值 ^ a
      值 = 值 & 0x81010100
      ----> 结果为0 ----> 每一位都非0
      ----> 结果非0 ----> 某一位为0
      
  • 步骤3:若该4字节中存在 '\0' ,则按位判断是否为 '\0'

  • 步骤4:返回值:eax = 字符串首地址 - 当前位 (首地址为低地址)

  • 流程图如下所示:
    image

2. 公式分析(变换形式)

  1. 想要变换形式,需要对这个公式表示认同,以此达到共识
    值 ^ 0xffffffff == 0xffffffff - 值

  2. 将如下公式变成一个表达式

a  = 值 + 0x7efefeff
值 = 值 ^ 0xffffffff
值 = 值 ^ a
值 = 值 & 0x81010100
----> 结果为0 ----> 每一位都非0
----> 结果非0 ----> 某一位为0

  • 表达式:结果 = ((值 + 0x7efefeff) ^ (0xffffffff - 值)) & 0x81010100
  1. 将表达式写成表格形式
运算 第4字节 第3字节 第2字节 第1字节
7e + a fe + b fe + c ff + d
ff - a ff - b ff - c ff - d
^ ------ ------ ------ ------
中间结果
81 01 01 00
& ------ ------ ------ ------
结果

3. 计算步骤分析(有些繁琐,后续会修改优化)

  • 第1字节是否为0,都不影响当前字节计算结果的最低位等于0。
  • 第2、3、4字节的最低位满足以下条件
    • 若第1字节不为0,肯定会发生进位,那么第2字节的运算公式就发生了改变:(fe + c + 1) ^ (ff - c) = (ff + c) ^ (ff - c) = xxxx xxx0,此时第2字节的运算公式和第1字节一致,那么第2字节的计算结果的最低位也一定等于0
    • 若第1字节为0,那么第2字节的运算公式为:(fe + c) ^ (ff - c) = xxxx xxx1
  • 第4字节中,若第4字节等于0,那么分两种情况
    • 第3位进位:(7e + 0 + 1) ^ (ff - 0) = 7f ^ ff = 1000 0000,高位等于1
    • 第3位不进位:(7e + 0) ^ (ff - 0) = 7e ^ ff = 1000 0001,高位与低位都等于1
  • 第4字节中,若第4字节大于0,同样分两种情况
    • 第3位进位:(7e + 1 + 1) ^ (ff - 1) = 80 ^ fe = 0111 1110,高位与低位都等于0
    • 第3位不进位分两种情况
      • 第4字节等于1:(7e + 1 + 0) ^ (ff - 1) = 7f ^ fe = 1000 0001,高位与低位都等于1,因为我们要用第4字节判断第3和第4字节的进位情况,当我们第4字节等于1时,无法确定这个1是第3字节进位得来的,还是第4字节本身就为1,但是结果符合我们的判断依据,他们都等于1,最终的与运算结果必然是非0的。
      • 第4字节大于1:(7e + 2 + 0) ^ (ff - 2) = 7f ^ fe = 0111 1101,高位等于0,低位等于1,从大于1开始,高位持续等于0,低位一直等于1

4. C语言实现该函数

int myStrlen(char buf[])
{
	int* p = buf;
	int len = 0;
	while (1)
	{
		int tmp = ((*p + 0x8efefeff) ^ (~(*p))) & 0x81010100;
		if (!tmp)
		{
			p++;
			continue;
		}
		else
		{
			len = (char*)p - buf;
			if (!(*p & 0xff))
			{
				len +=  0;
				break;
			}
			else if (!(*p & 0xff00))
			{
				len += 1;
				break;
			}
			else if (!(*p & 0xff0000))
			{
				len += 2;
				break;
			}
			else if (!(*p & 0xff000000))
			{
				len += 3;
				break;
			}
		}
	}
	return len;
}

5. 汇编语言实现该函数

int __declspec(naked)asm_strlen(char* buf)
{
	__asm
	{
		push ebp
		mov ebp, esp
		push ecx
		push edx
		push [ebp + 8]

		mov ecx, [ebp + 8]
		test ecx, 3
		je asm_strlen@main_loop

	asm_strlen@misligned:
		mov al, byte ptr[ecx]
		add ecx, 1
		test al, al
		je byte_0
		test ecx, 3
		jne asm_strlen@misligned

	asm_strlen@main_loop:
		mov eax, dword ptr[ecx]
		add ecx, 4
		mov edx, eax
		add edx, 0x7efefeff
		xor eax, 0xffffffff
		xor eax, edx
		test eax, 0x81010100
		je asm_strlen@main_loop

		mov eax, dword ptr[ecx - 4]
		test al, al
		je asm_strlen@byte_0
		test ah, ah
		je asm_strlen@byte_1
		test eax, 0x00ff0000
		je asm_strlen@byte_2
		test eax, 0xff000000
		je asm_strlen@byte_3

	asm_strlen@byte_3:
		lea eax, [ecx - 1]
		pop ecx
		sub eax, ecx
		jmp asm_strlen@end

	asm_strlen@byte_2:
		lea eax, [ecx - 2]
		pop ecx
		sub eax, ecx
		jmp asm_strlen@end

	asm_strlen@byte_1:
		lea eax, [ecx - 3]
		pop ecx
		sub eax, ecx
		jmp asm_strlen@end

	asm_strlen@byte_0:
		lea eax, [ecx - 4]
		pop ecx
		sub eax, ecx
		jmp asm_strlen@end

	asm_strlen@end:
		pop edx
		pop ecx
		pop ebp
		ret
	}
}

标签:字节,函数,eax,反汇编,ff,strlen,asm,ecx
From: https://www.cnblogs.com/qinghuan190319/p/17231270.html

相关文章

  • webpack原理(3):Tapable源码分析及钩子函数作用分析
    webpack本质上是一种事件流的机制,它的工作流程就是将各个插件串联起来,而实现这一切的核心就是Tapable,webpack中最核心的负责编译的Compiler和负责创建bundles的Compilation......
  • VBA函数
    space(8)加空格vbcrlf换行trim()去掉两侧空格lrim()去掉左侧空格rtrim()去掉右侧空格left()right()mid()int()整除mod()取余row()column()setsh=nothingForm......
  • 使用chatgt(GPT-4)将过程式(的java代码)改成函数式(的elixir代码)
    天啦噜太可怕了,之前我还嘲笑chatgpt不会小众语言来着。chatgt(GPT-4)在接收2次prompt后,把过程式(的java代码)改成了函数式(的elixir代码),给出的Elixir代码可以不经修改直......
  • c++常用STL库及常用函数
    临近各种算法比赛,相信很多人想笔者一样还总是记不住很多函数的用法,特此总结一下常用的STL标准库以及标准函数,希望能够有所帮助。1.输入输出输入输出一般用两个标准库:#i......
  • 多元函数驻点与极值点关系
    驻点与极值点(一元)链接多元极值点f(\(x_0\),\(y_0\))>f(x,y)就为极大值,小于为极小值多元驻点也不一定是极值点xy极值点也不一定是驻点|x|+|y|在(0,0)点因为|x|+|y|......
  • 奇怪现象,父类构造函数中的赋值子类无法取得
    实战中这样的类,Shize保持了原始值,但单元测试,又是好的,很奇怪。publicabstractclassGrandPa{protectedintNothing;protectedGrandPa(intnothing)=>Not......
  • Vue.js 非单文件组件-VueComponent构造函数
    视频<!DOCTYPEhtml><html> <head> <metacharset="UTF-8"/> <title>VueComponent</title> <scripttype="text/javascript"src="../js/vue.js"></script> </h......
  • 函数
    1、概念执行一个任务的一组语句体,可以通过调用函数,处理一类问题。main是主函数,一般一个程序里面都有一个主函数,是程序的入口和出口2、函数定义return_typefunc......
  • SetFilePointerEx函数释义以及用法
    一、函数介绍SetFilePointerEx是一个WindowsAPI函数,用于设置文件指针的位置。它可以在文件中移动指针,以便读取或写入文件的不同部分。这个函数通常用于处理大型文件或需......
  • 测试方法如果想调用私有函数
    //試験方法(neCollectionCheck)booleanresult=ReflectionTestUtils.invokeMethod(collectionService_,"neCollectionCheck",getNeInfo(),vl......