大神论坛

找回密码
快速注册
查看: 472 | 回复: 0

[交流讨论] 汇编反转一道ctf数字题

主题

帖子

19

积分

初入江湖

UID
17
积分
19
精华
威望
38 点
违规
大神币
68 枚
注册时间
2021-03-06 09:04
发表于 2021-03-07 20:12
本帖最后由 河南程冠希 于 2021-03-07 20:12 编辑

先前学习了不少汇编的内容,来整点新花样,试试用汇编来解决一些简单的题目

题目

给定一个32位的有符号数x,返回x中每位上的数字反转后的结果。

假设环境不允许存储64位整数(有符号或无符号)

示例1:

输入:x = 123
输出:321

示例2:

输入:x = -123
输出:-321

例3:

输入:x = 120
输出:21

解题代码

#include "stdafx.h"

void revert(int num){
int sign=0;
int cnt=0;
int arr[50]={0};
if(num<0){
num=-num;
sign=1;
}
__asm{
mov eax,num
_begin:
cdq
mov ecx,0Ah
div ecx
mov ebx,cnt
lea ecx,dword ptr[arr+ebx*4]
inc cnt
mov dword ptr [ecx],edx
test eax,eax
jnz _begin
_ret:

}
if(sign){
printf("-");
}

int i=0;
while(arr[i]==0){
i++;
}
for(;i<cnt;i++){

printf("%d",arr[i]);
}
printf("\n");

}
int main(int argc, char* argv[])
{
revert(-10086);
return 0;
}

运行结果

整体代码分析

整体代码就是做了以下几个工作:

  1. 判断参数是否为负数,如果是负数,则取其绝对值,并在后面输出时会先输出一个符号
  2. 通过汇编依次将除以10的余数存入数组中,并记录下位数
  3. 将数组头部为0的部分跳过,不输出
  4. 输出数组的剩下有效部分

汇编代码分析

__asm{                
mov eax,num
_begin:
cdq
mov ecx,0Ah
div ecx
mov ebx,cnt
lea ecx,dword ptr[arr+ebx*4]
inc cnt
mov dword ptr [ecx],edx
test eax,eax
jnz _begin
_ret:

}

可以看到汇编的代码非常简洁,只有短短几行

来分析一下这段代码做了些什么:

首先将要被反转的数赋值给eax

mov         eax,num

_begin

接着声明一个程序段_beign,用于之后跳回来实现循环

_begin:

cdq指令

接着是一个新的汇编代码:

cdq

cdq—Convert Double to Quad (386+),该指令把edx扩展为eax的高位,也就是借助两个寄存器来存储一个64位的数

以123为例,看一下cdq执行前和执行后的效果:

执行前

此时EAX为7B,也就是123的十六进制,EDX则暂不清楚数据含义

执行后

执行后,可以发现EDX被清零了,效果和movsx类似,只不过被扩展的高位存放在edx中

为什么要进行扩展?之后的div指令所要求的,且先向下看


然后是将除数10赋值给ecx,10的十六进制对应A

mov         ecx,0Ah

div指令

再接着就是div指令了

先介绍一下div指令


div为无符号除法  ; idiv为有符号除法

div 指令只需要一个操作数,即除数

既然div指令只给出的除数,那么被除数呢?

被除数:

  • 如果除数为8位, 被除数则为 16 位, 在 ax 中存放
  • 如果除数为16位, 被除数则为 32 位, 在 dx 和 ax 中存放, dx 存放高 16 位, ax 存放低 16 位
  • 如果除数为32位,被除数则为64为,在edx和eax中存放,edx存放高16位,eax存放低16位

接着看我们的代码:

div        ecx

我们指定ecx为除数,除数ecx=10


依旧以123为例,查看div指令执行前后寄存器的变化

执行前


执行前EDX和EAX组成除数,0x00000000 0000007B = 123;ECX作为除数0x0A=10

执行后

执行后可以看到EAX=0x0C=12,EDX=3

得知,div指令执行后EAX存放除后的结果,EDX存放除后的余数


数据放入数组

得到除后的结果和余数以后,就将余数存放到数组即可

mov ebx,cnt
lea ecx,dword ptr[arr+ebx*4]
inc cnt
mov dword ptr [ecx],edx
  1. 这里先将cnt(用来记录被除数的位数)赋值给ebx
  2. 然后通过lea指令获得要存放的数组的成员位置
  3. 获取完成后,将cnt加1
  4. 然后就是将edx也就是余数存放到数组中

比较循环

test eax,eax
jnz _begin

判断eax是否为0,如果不为0,则继续跳到先前的代码继续执行循环,直到被除数为0才跳出循环


_ret

这后面并没有写代码,只是声明了一个返回的段,算是为了代码的结构完整性吧


总结

将数字顺序反转这种入门题算是十分经典了,但用汇编来完成的人却并不多,通过这个案例来巩固之前所学的汇编知识,加强理解。

PS:不同于以往对汇编冗余的观念,在某些场景使用汇编反而会更简洁,而非更加冗余,这个案例就展现出汇编的优点;但并非所有项目都适合使用汇编,不要为了用汇编而用汇编,要结合具体场景考虑

更多逆向分析资源, 大神论坛


返回顶部