Windows逆向安全(一)之基础知识(七)

news2025/1/21 15:41:19

汇编C语言类型转换

类型转换

类型转换的使用场景

类型转换一般为由数据宽度小的转换成数据宽度大的,不然可能会有高位数据被截断的现象,引起数据丢失

需要一个变量来存储一个数据,刚开始这个数据的数据宽度较小,后来发现存不下了,需要换一个数据宽度更大的变量来存储

类型转换相关汇编指令

MOVSX

先符号扩展,再传送

MOV AL,0FF
MOVSX CX,AL
MOV AL,80
MOVSX CX,AL

MOVZX

先零扩展,再传送

MOV AL,0FF
MOVZX CX,AL
MOV AL,80
MOVSX CX,AL

类型转换例子

#include "stdafx.h"
int main(int argc, char* argv[])
{
        unsigned char i=0xFF;
        printf("%d\n",i);
        int j=i+1;
        i=i+1;
        printf("%d\n",i);
        printf("%d\n",j);
        return 0;
}

我们来看看以上代码的运行结果:

在这里插入图片描述
在这里插入图片描述

分析结果

首先输出的是一个无符号数 i,0xFF对应的十进制为255

接着输出的是i自增1后的结果,我们发现255+1变成了0,这是因为char的数据宽度为8位,最大便是0xFF了,再加上一就超出了char的数据宽度,也就是发生了上溢。于是数据变成了0

最后输出的是类型转换后i+1的结果,正确地显示为256

汇编观察

汇编代码

大致了解了产生上述结果的原因,用汇编来更透彻地分析:

在这里插入图片描述
我们这里提取出 去除printf输出的部分,得到汇编代码如下:

8:        unsigned char i=0xFF;
0040D708   mov         byte ptr [ebp-4],0FFh
10:       int j=i+1;
0040D722   mov         ecx,dword ptr [ebp-4]
0040D725   and         ecx,0FFh
0040D72B   add         ecx,1
0040D72E   mov         dword ptr [ebp-8],ecx
11:       i=i+1;
0040D731   mov         edx,dword ptr [ebp-4]
0040D734   and         edx,0FFh
0040D73A   add         edx,1
0040D73D   mov         byte ptr [ebp-4],dl

结果有些尴尬,ԾㅂԾ, 我们发现并没有用到前面所说的movsx或movzx指令,但是先不着急,先看看这段汇编代码做了些什么

对应i赋值

很稀松平常的,char对应数据宽度为byte赋值

8:        unsigned char i=0xFF;
0040D708   mov         byte ptr [ebp-4],0FFh

对应j赋值

接下来就是j的赋值

10:       int j=i+1;
0040D722   mov         ecx,dword ptr [ebp-4]
0040D725   and         ecx,0FFh
0040D72B   add         ecx,1
0040D72E   mov         dword ptr [ebp-8],ecx

可以看到:首先是直接将前面的 i 赋值给ecx,并且赋值的长度为dword,很明显将超出char长度的内容也赋值到了ecx

mov         ecx,dword ptr [ebp-4]

然后下一句很关键

and ecx,0FFh

与操作,将之前多超出的部分和0相与,也就是将超出的部分全部清零(用零填充),相当于MOVZX指令的零填充

接下来就是加一的操作

add ecx,1

最后就是将ecx赋值给了我们的变量j

mov         dword ptr [ebp-8],ecx

对应i=i+1

和前面j的赋值似曾相识,直接将前面i赋值给edx,并且赋值的长度为dword,很明显将超出char长度的内容也赋值到了edx

mov         edx,dword ptr [ebp-4]

接着也是与操作,超出来的部分清零

and         edx,0FFh

接下来就是加一的操作

add         edx,1

最后是赋值

mov         byte ptr [ebp-4],dl

将edx的低8位赋值给i

通过前面的分析,可以发现,无论是 i 自己+1还是用数据宽度较高的 j 来接收 i +1的结果

期间都是要先取出超出 i 数据宽度的dword长度的数据,然后再使用and 0xFF,把超出的部分清零

换言之,char的计算也会先转换为int的计算,最后再转回来

还有就是汇编指令并非一成不变,不是一定要使用movsx或movzx指令,也可以通过这种取出超出长度的数据,然后再将超出的部分清零的操作来实现类movzx指令的结果

自写汇编实现功能

前面虽然我们分析了,汇编代码,但很可惜编译器并没有使用movzx指令来实现操作,本着学习巩固的精神,我们自己写汇编来实现上述的功能,以此来加深对movzx的理解

#include "stdafx.h"
unsigned char i=0xFF;
int j=0;
void _declspec (naked) func(){
        _asm{
                                //保留调用前堆栈
                push ebp
                //提升堆栈
                mov ebp,esp
                sub esp,0x40
                //保护现场
                push ebx
                push esi
                push edi
                //初始化提升的堆栈,填充缓冲区
                mov eax,0xCCCCCCCC
                mov ecx,0x10
                lea edi,dword ptr ds:[ebp-0x40]
                rep stosd
                //函数核心功能

                                //将i零扩充赋值给ecx
                                movzx ecx,i
                                //ecx自增1
                                inc ecx
                                //将ecx赋值给j
                                mov j,ecx
                                //直接让i自增1
                                inc i

                //恢复现场
                pop edi
                pop esi
                pop ebx
                //降低堆栈
                mov esp,ebp
                pop ebp                
                //返回
                ret 
        }
}

int main(int argc, char* argv[])
{
        printf("%d\n",i);
        func();
        printf("%d\n",i);
        printf("%d\n",j);
        return 0;
}

执行后的结果为:

在这里插入图片描述
和前面的执行结果一致

上面的代码看似很多,其实核心功能就只有四句:

//函数核心功能
//将i零扩充赋值给ecx
movzx ecx,i
//ecx自增1
inc ecx
//将ecx赋值给j
mov j,ecx
//直接让i自增1
inc i

我们这里使用了movzx指令,实现了相同的功能,也顺便引入了一个新的汇编语句:

inc eax 相当于 add eax,1     也就是eax=eax+1

优点是速度比add指令快,占用空间小

与之相对的便是:dec指令,自减1

关于movzx和movsx的区别

案例一

#include "stdafx.h"
void function(){
    char i=0xFF;
    int j=0;
    _asm{
        movsx eax,i
        mov j,eax
    }
    printf("%d\t%x\n",j,j);
    _asm{
        movzx eax,i
        mov j,eax
    }
    printf("%d\t%x\n",j,j);
}
int main(int argc, char* argv[])
{
    function();
    return 0;
}

运行结果

在这里插入图片描述

分析

void function(){
    char i=0xFF;
    int j=0;
    _asm{
        movsx eax,i
        mov j,eax
    }
    printf("%d\t%x\n",j,j);
    _asm{
        movzx eax,i
        mov j,eax
    }
    printf("%d\t%x\n",j,j);
}

首先声明一个char类型的变量i,默认为有符号数,然后再声明一个变量j初始化

char i=0xFF;
int j=0;

然后就是先将i带符号扩展到eax,接着再用eax赋值给j

_asm{
        movsx eax,i
        mov j,eax
    }

带符号扩展解释:

首先将 i :0xFF转化为二进制 : 1111 1111

i是一个有符号数,最高位为符号位即1

符号扩展就是将要扩展的高位全部用符号位进行填充,这里就是将eax的高24位用1填充(低8位直接为i赋值),得到的结果转换为十六进制就是ffffffff,8个f,对应输出的结果

再看下面:

_asm{
        movzx eax,i
        mov j,eax
    }

和前面类似,只不过是将movsx改为movzx

无符号扩展解释:

直接将eax的高24位用0填充(低8位直接为i赋值),得到的结果为000000ff,2个f,对应输出的结果

案例二

将上面的ff改为8b即可

void function(){
    char i=0x8b;
    int j=0;
    _asm{
        movsx eax,i
        mov j,eax
    }
    printf("%d\t%x\n",j,j);
    _asm{
        movzx eax,i
        mov j,eax
    }
    printf("%d\t%x\n",j,j);
}

运行结果

在这里插入图片描述

分析

有了前面分析的经验,直接来看f8如何有符号扩展

先将8b转化为二进制数:

8b:1000 1011

这里的符号位也就是最高位为1,于是将eax的高24位用1(符号位)填充,低8位直接为8b,得到的结果转化为十六进制:ffffff8b

与之类似,无符号扩展则是将eax的高24位用0填充,低8位依旧为8b,得到的结果转化为十六进制:0000008b

得到的结果运行结果一致

简单地来说,有符号扩展:movsx,就是将被扩展数的最高位(符号位),对应SF标志位的内容(修改SF标志位可以改变符号扩展的结果),填充到扩展的高位部分

而无符号扩展则直接用0填充扩展的高位部分

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/434390.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么是UML?

文章目录 00 | 基础知识01 | 静态建模类图对象图用例图 02 | 动态建模时序图通信图状态图活动图 03 | 物理建模构件图部署图 UML(Unified Model Language),统一建模语言,是一种可以用来表现设计模式的直观的,有效的框图…

【图像分割】Grounded Segment Anything根据文字自动画框或分割环境配置和基本使用教程

1 环境配置 要求:python>3.8, pytorch>1.7, torchvision>0.8 官方地址:https://github.com/IDEA-Research/Grounded-Segment-AnythingMarrying Grounding DINO with Segment Anything & Stable Diffusion & BLIP & Whisper &am…

命运坎坷的保定县

保定的词语分解 保护而安定之。诗小雅天保:“天保定尔,亦孔之固。”谓稳固地保有。 晋 陆云 大将军宴会被命作诗:“皇皇帝祜,诞隆骏命。四祖正家,天禄保定。” 保定府?保定县? 今天提起“保定…

将“每日造型”变成长久习惯,戴森Airwrap™美发棒为何成为最好的“美丽投资”?

做头发、换发型是一个大工程,这几乎成了一种固定印象。虽然卷发棒已成为几乎“人手必备”的头发造型工具,但使用起来往往“现实很骨感”,不是使用频次极低,就是被束之高阁,每天都自己做头发换造型,只能是一…

陶泓达:4.19黄金原油早间精准策略!

黄金方面: 周二(4月18日)美市早盘,现货黄金一度跌至1991.05美元低点,但随后迅速拉升近20美元,向上触及2010.71美元高点,短线走出V型反转行情。本周,美国经济日历上没有太多关键数据会影响黄金和美元。因此&…

docker的数据卷详解

数据卷 数据卷是宿主机中的一个目录或文件,当容器目录和数据卷目录绑定后,对方修改会立即同步 一个数据卷可以同时被多个容器同时挂载,一个容器也可以被挂载多个数据卷 数据卷作用:容器数据持久化 /外部机器和容器间接通信 /容器…

leetCode算法第二天

好好刷刷算法题,提高自己的编码能力。 文章目录 将整数转为罗马数字将罗马数字转为整数编写一个函数来查找字符串数组中的最长公共前缀电话号码的字母组合 将整数转为罗马数字 leetcode链接:https://leetcode.cn/problems/integer-to-roman/ 解题思路…

施工阶段如何应用BIM技术,建模助手有话说

​近些年来,越来越多的建筑项目采用BIM来提升管理水平和品质,特别在施工阶段,通过BIM技术可以将施工现场3D模型与施工进度链接,超前模拟施工情况,完成各种精细化施工方案,除了保障施工工作顺利推进&#xf…

GitHub Copilot 快速入门

GitHub Copilot 是 AI 结对程序员。 可以使用 GitHub Copilot 在编辑器中获取整行或整个函数的建议。 1. 简介 让我们首先了解一些关于 GitHub Copilot 的内容。 这是 GitHub 和 OpenAI 的合作成果。 Copilot 是一种基于人类程序员编写的数十亿行代码训练的语言模型。 &#…

【WSN定位】基于RSSI的加权质心定位算法-Matlab代码

文章目录 1. 原始质心定位算法2. 基于RSSI的加权质心定位算法基本思想3. 基于RSSI的加权质心定位算法流程图4. 部分代码展示5. 运行结果展示6. 资源获取 1. 原始质心定位算法 可参考质心定位算法 2. 基于RSSI的加权质心定位算法基本思想 传统的质心算法在求解过程中只是将未…

网络协议-加密和HTTPs证书

目录 对称加密和非对称加密 加密解密 加密方法/解密方法 对称加密/非对称加密 非对称加密(秘钥对) 思考 解决信任问题 证书体系 算法如何验证证书就是Alibaba 实际的证书体系 ​编辑 常见算法介绍 DES(Data Encryption Standard&…

量子力学、波函数与量子计算:揭开宇宙微观奥秘的神奇之门

在一个遥远的星球,生活着一群拥有超自然力量的智慧生物。他们能够随心所欲地让物体在空间瞬移,甚至能够预测未来。有一天,一位地球科学家意外穿越到了这个星球。经过一番了解,科学家惊奇地发现,他们所掌握的这种神奇力…

ai智能文章改写软件-ai智能文案自动生成

AI同义转换,掀起内容创作新革命! 如今,在数字化时代,内容创作日益受到重视,越来越多的人致力于网站排名优化、内容创意提升以及用户体验改善。然而,吸引用户阅读和提供有价值的信息并不那么容易。因此&…

云原生个人线路 K8s本地集群搭建(实操)

声明:此文章为博主个人学习记录,仅供学习和交流,如有侵权请联系博主。 kubernetes本地集群部署 三种方式 minikube 集群模拟器 裸机 kindminikube Docker客户端部署 裸机 环境 虚拟机 VMware 三台centos7 每台2g内存 2cpu 30g硬盘 k8s-mas…

Keil5 MDK新建项目工程

本文重点介绍基于Keil5 MDK实现新建一个完整的工程,第一次新建比较繁琐,建好后续工程可以直接复用 一、新建工程框架 1.新建一个文件夹,命名为Template 2.点击 MDK 的菜单: Project –>New Uvision Project ,然后将目录定位…

( “树” 之 前中后序遍历) 145. 二叉树的后序遍历 ——【Leetcode每日一题】

基础概念:前中后序遍历 1/ \2 3/ \ \ 4 5 6层次遍历顺序:[1 2 3 4 5 6]前序遍历顺序:[1 2 4 5 3 6]中序遍历顺序:[4 2 5 1 3 6]后序遍历顺序:[4 5 2 6 3 1] 层次遍历使用 BFS 实现,利用的就是 BFS…

Git从远程仓库克隆仓库后推送到指定分支

git克隆到本地仓库 在得到一个git仓库地址后,首先要配置本地仓库,配置远程仓库地址才可以远程拉取项目。 本地配置的一般流程: git init初始化一个空白git仓库 2. 配置在自己额用户名和邮箱 配置个人信息时方便再团队合作时能知道是谁再何…

2023 CCBN广电展顺利召开,ATEN宏正携广电专属系列解决方案亮相

4月19日-4月21日,第二十九届中国国际广播电视信息网络展览会(CCBN2023)于北京市石景山区首钢会展中心盛大开幕。本届CCBN广电展以“大视听向未来”为主题,内容涵盖广播电视、网络视听、电影、信息化视听、视听消费电子、通信、IT等多个领域创新科技和行业…

MySQL高级第十三篇:MySQL事物日志(redo日志-undo日志执行流程)

MySQL高级第十三篇:MySQL事物日志(redo日志-undo日志执行流程) 一、概述二、redo 日志1. 为什么需要 redo日志?2. redo 日志的特点3. redo log 整体流程4. redo log 的刷盘策略? 三、undo 日志1. 什么是 undo 日志&…

1026. 节点与其祖先之间的最大差值(4-19日

题目:给定二叉树的根节点 root,找出存在于 不同 节点 A 和 B 之间的最大值 V,其中 V |A.val - B.val|,且 A 是 B 的祖先。 (如果 A 的任何子节点之一为 B,或者 A 的任何子节点是 B 的祖先,那么…