0


安全学习记录——逆向篇(一)汇编基础

最近工作了杂事有点多,学习进度没以前那么快了,但是还是在学的嘛。之前因为纠结域渗透部分的提权原理入了一个超级无敌深坑:汇编。老实说,我不知道汇编具体能应用到什么地方,只是听说可以做程序逆向分析和破解。这个是非常接近计算机底层的东西,如果想要研究病毒木马,汇编应该是必学的东西;还有外挂脚本这些。最重要的是,能破解欸,这可太酷了。所以我毅然决然的钻进了这个坑中。

计算机结构

我觉得讲一个东西要从大到小来讲,首先有个宏观的认知,因为我们普通人玩电脑最先接触都是集成模块。如果不知道装一台计算机需要什么东西,那先学基础知识吧。

为了方便理解,计算机核心简单来讲可以分为两个板块:CPU和内存。CPU就是主板中间那个方块,很多人会习惯性的把它叫做芯片。如果这个叫芯片的话,那旁边那些带针脚的小方块叫什么呢?而内存的话,很多装机大佬会把内存条等同于内存。但是内存条只是一个公用的RAM,主板上有很多我们不可更改,写死了的内存叫做ROM,BIOS就是ROM的一种。ROM为计算机提供运行的必要指令,断电不消失,待处理的内容就被缓存在RAM中,RAM中的所有数据断电就丢。RAM和ROM的组合就是一个芯片,不同芯片对接不同内容,芯片通过总线和CPU连接。CPU存在的价值是接受外部输入的指令,通过自己内部储存地址去内存里找指令(注意:ROM只读),然后读到指令后再通过某些芯片交给设备去执行。

打个比方:内存就是坐办公室的领导,它们手里有对应公章和下发命令的权力;CPU就是秘书,负责接收整理事项,把事项一件一件交给领导,领导就负责下发命令签字盖章,然后把已经写好的命令发给秘书,秘书再把命令分类交给下面的各个办事员,也就是各个外接设备,办事员收到后就负责干事。这就是计算机的运行原理,要注意的一件事是,无论CPU、内存、设备和人一样,都有自己的行事逻辑,没有谁是必须要依靠另一方才能工作,没有绝对的核心,只要有平台,都可以工作。

那么汇编是干什么的?我们知道,计算机的底层语言是某些元素在通电情况下展现的物理特性所导致的通电或断电以及高低电压差。为了方便我们人和计算机沟通,我们将电压高低变化进行了编码,用01来表示近似的电压,也可以叫做电频变化。所以01就成为了计算机的底层语言。但是01看起来还是太费劲了,比如00010010,天知道这是什么意思,到底是数字还是文字还是命令。所以就把指令编成了我们人能看懂的字,比如add reg16/16,这样可读性就大大提升了。先不用管这是什么意思。根据我们之前了解的计算机原理,设备太多太杂,内存不可执行,所以汇编的作用就是控制CPU。

CPU工作原理

再根据我们上面讲到的,CPU作为秘书,需要整理接收的数据,所以它要有储存活动数据的能力,这就需要一个缓冲器来让数据排队;而且它还要能找内存要命令,需要找对内存,那么它自身就需要储存部分内存地址,这项功能由寄存器来完成;然后CPU还需要将从内存得到的命令交给设备去执行,这就需要一个控制器去控制设备;要完成上述工作,计算能力必不可少,所以还有一个运算器来帮助处理信息。接下来我们就看看它到底是怎么工作的。这里借用王爽老师汇编语言(第三版)里面的图来说明:

首先我们有两个寄存器CS和IP,这是16位的CPU,只用于说明原理,学习建议从32位开始。CS(code segment)是储存代码段地址的寄存器,这个图中20000-20002就是一个代码段其中B8 23 01是这个代码段对应的机器码,机器看不懂汇编,只懂机器码。机器码可以写成任何进制,写成16进制是因为它和2进制算是个倍数关系,数字有区分好认。而IP(Instruction pointer)是偏移地址,比如此时0000就是从B8开始,0003那就是从BB开始读。16进制中文字占两位,数字按照换算来占位,所以我们能看到mov ax,0123H占了2+4=6位,也就是三行。

然后就是找地址的加法,这里没有直接将两个数字加在一起,而是做了一个进位:CS*16+IP,做这个错位加法的原因更多的是为了方便管理,比如我们说322c4,就可以直接确定CS是3000,IP是22c4。而如果直接做加法,还要花时间去算数字,0-ffff两两相加,有接近一半的结果还是会进位到5位,超过位数限制容易造成寻址混乱,而且浪费。而现在,我们只需要保证CS不是f开头就可以百分百保证不进位了。管理一个总比两个简单。

最后去内存里找到了指令后,根据指令组合的位数自动查找机器码,然后CPU将读取的指令通过执行控制器去让设备区执行命令,比如寄存器也算是一个执行部件,此时第一个命令就会往寄存器ax中放入0123。CPU就通过这样的指令来控制整个电脑,甚至包括自己。

汇编

从上面的过程中我们能看到,CPU在读取了内存指令后就能根据内存指令去控制计算机。那么只要我们修改内存的内容不就可以通过CPU直接控制整个电脑了。所以汇编是一种运行在内存中的语言,在我们通过dos环境直接写入汇编后,语言环境会把汇编语言直接翻译成机器码储存在内存中。CPU在读取时就会直接读取我们新写入的命令。所以实际上CPU并不知道自己读取的指令到底有多少行,而是指令在内存里分了段。简单举个例子:下面这段汇编指令

assume cs:codeseg
 
codeseg segment
start:
    mov ax,0001h
    mov bx,0002h
    mov cx,0003h
codeseg ends
end start

end

在我们运行过程中会发现,当我们运行了到最后这个指令后,出现了一个新的指令:ADD AL,CH。这个意思是将AL和CH中的值相加,这说明CPU执行汇编并不会因为我们写了一个end就停止,这个end只是说明后面的所有的内容不会被理会,相当于一个断点。CPU仍然会自己去执行内存中本身有的指令。

上述只是讲了汇编的基本原理,并不代表汇编就只有这么点东西,和学其它语言一样,搞清楚每个指令的用途,然后打开思路,尝试从最最基础的方向去思考问题,而不是以用户的思路来看表面的东西。举个简单例子,已知mov ax,0001h表示将0001放入ax中,add ax,0001h表示ax+1,请问2^4怎么求:

mov ax,0002    ax=0002
add ax,ax      ax=0004
add ax,ax      ax=0008
add ax,ax      ax=000f

学其它语言时也是一样的,一个复杂的问题可以通过简单条件循环来得到。这就是汇编的基本思路,将高级语言的浓缩表述用简单的底层逻辑来展开显示。学会了这种思路,记用指令就行。同样的思路,如果说我们知道内存什么地方有什么指令,那么我们也可以直接修改CS和IP去指向我们所想的指令,所以思路打开,没有必须的顺序,也没有那么严格的编写规矩。

内存

既然我们的语言是运行在内存上的,那么我们就需要知道内存是怎么工作的。当我们一个程序在运行时,会在内存里占据部分空间,这个我们打开任务管理器就能看到,此时被占用的内存被称作堆heap。堆一旦被分配了就会一直占用内存空间直到主动释放。曾经玩天涯明月刀ol时玩久了就会特别卡,就是因为内存释放机制有问题导致一直在堆,退出重进就会好。而我们运行的程序语言会放入到另外一半内存中,叫做栈stack,栈的特点就是可以分段放入取出,而且后入先出,适合中间插入函数。内存会把整个代码一起放进去,中间存在外部调用的就会去读取外部函数并插入到中间的帧中,然后从最上面开始逐步丢给CPU执行并取出已执行部分释放空间。阮一峰老师这个部分讲得很清楚汇编语言入门教程 - 阮一峰的网络日志 (ruanyifeng.com)。学过C的朋友应该懂,所以汇编和C最好一起学,这两个语言转换起来有个对应,方便理解,java和python的包太多了,转换成汇编会非常复杂,极其劝退。

举个转换的例子:

#include <stdio.h>
#include <string.h>
#define PASSWORD "1234567"
int verif_password(char *password) 
{
    int authenticated;
    authenticated = strcmp(password, PASSWORD);
    return authenticated;
}
int main()
{
    int valid_flag=0;
    char password[1024];
    while (1)
    {
        printf("please input password: "   );
        scanf("%s", password);
        valid_flag = verif_password(password);
        if (valid_flag){
            printf("incorrect password, try again\n");
        }
        else
        {
            printf("Congratulations, you have entered the correct password!\n");
            break;
        }
    }
}
; Attributes: bp-based frame fpd=3B0h

; int __fastcall main(int argc, const char **argv, const char **envp)
public main
main proc near

var_410= byte ptr -410h
var_4= dword ptr -4

push    rbp
sub     rsp, 430h
lea     rbp, [rsp+80h]
call    __main
mov     [rbp+3B0h+var_4], 0

# while循环
loc_4015A7:
lea     rcx, Format     ; "please input password: "
call    printf
lea     rax, [rbp+3B0h+var_410]
mov     rdx, rax
lea     rcx, aS         ; "%s"
call    scanf
lea     rax, [rbp+3B0h+var_410]
mov     rcx, rax
call    verif_password
mov     [rbp+3B0h+var_4], eax
cmp     [rbp+3B0h+var_4], 0
jz      short loc_4015EF

# if分支密码输入错误
lea     rcx, Buffer     ; "incorrect password, try again"
call    puts
jmp     short loc_4015A7

# if分支密码输入正确
loc_4015EF:
lea     rcx, aCongratulation ; "Congratulations, you have entered the c"...
call    puts
nop
mov     eax, 0
add     rsp, 430h
pop     rbp
retn
main endp

OK,这些就是汇编的最最基础的部分。后面再写这个c文件转换成的exe文件是怎么一步一步被破解掉的,还没做呢,所以下一篇再写,等完全搞懂了来,让我们看看逆向到底是个什么东西。

标签: 学习 汇编 安全

本文转载自: https://blog.csdn.net/2401_82449242/article/details/140143056
版权归原作者 爆肝的麻薯 所有, 如有侵权,请联系我们删除。

“安全学习记录——逆向篇(一)汇编基础”的评论:

还没有评论