逆向工程 - 第2部分(高级编程概念)

2019-04-04 约 4476 字 预计阅读 9 分钟

声明:本文 【逆向工程 - 第2部分(高级编程概念)】 由作者 N1neSun 于 2019-01-25 09:36:00 首发 先知社区 曾经 浏览数 4018 次

感谢 N1neSun 的辛苦付出!

翻译自:https://medium.com/@danielabloom/bolo-reverse-engineering-part-2-advanced-programming-concepts-b4e292b2f3e

前言
在本文中,我们将分解以下编程概念并分析每条指令的反编译的汇编版本:
1.数组
2.指针
3.动态内存分配
4.套接字编程(网络编程)
5.线程
对于逆向工程系列的第1部分,请单击此处
请注意:虽然本文使用IDA Pro来反汇编编译代码,但IDA Pro的许多功能(即图形,伪代码转换等)都可以在其他免费反汇编程序(如radare2)的插件和构建中实现。此外,在准备本文时,我冒昧地将反汇编代码中的一些变量名称从IDA预设(如“v20”)更改为它们在C代码中对应的内容。这样做是为了使每个部分更容易理解。最后,请注意,此C代码已编译为64位可执行文件,并使用IDA Pro的64位版本进行反汇编。尤其是在计算数组大小,32位寄存器(即eax)的大小通常加倍并转换为64位寄存器(即rax)。
好的,我们开始吧!
虽然第1部分介绍并描述了循环和IF语句等基本编程概念,但本文旨在解释在逆向工程时必须解密的更高级主题。
数组
让我们从数组开始,首先,让我们看一下整个代码:

现在,我们来看一下整个反编译程序集:

如您所见,12行代码变成了相当大的代码块。但不要被吓倒!请记住,我们在这里所做的就是设置数组!
让我们一点一点地分解它:

在使用整型初始化数组时,编译器只需通过局部变量初始化长度。
注意:上面的声明数组的反编译的图片实际上标注时错误的。但是在使用整型初始化数组时,编译器首先需通过局部变量初始化长度。上面的屏幕截图实际上是Stack Canaries的初始化。Stack Canaries用于检测溢出攻击,如果不这样的话,可能会导致执行恶意代码。在编译期间,编译器要为litArray的元素分配足够的使用空间。litArray[0](见下图标注有“局部变量-数组”的图片)如你所见,唯一被分配的litArray元素是litArray [0 ])。编译器优化可以显着提高应用程序的速度。
如果因此而产生困惑的话,我表示抱歉!



首先,将数组长度保存到局部变量(ArraySize),然后计算最大和最小索引值以及数组的总长度,然后使用它们计算内存中数组的基本位置。


当声明具有预定义索引定义的数组时,编译器只需将每个预定义对象保存到自己的变量中,此变量名反映数组中的索引号。(如objArray4 = objArray [4])。


就像声明具有预定义索引定义的数组一样,在初始化(或设置)数组中的索引时,编译器会为所述索引创建一个新变量。


从数组中检索项时,该项从数组中的索引中获取并设置为所需的变量。


创建矩阵时,首先将行和列大小设置为它们的row和col变量。接下来,计算行和列的最大和最小索引,并用于计算内存中矩阵的基本位置或者总大小。


当从矩阵中检索时,再次执行与在矩阵索引的输入序列期间执行的相同的计算,但是不是将某些内容输入到索引中,而是检索索引的内容并将其设置为期望的变量(如,MatrixLeet)。
指针
现在我们已经了解了如何使用/查看汇编数组,让我们继续讨论指针。

让我们现在来分解汇编代码:

首先我们将int num赋值为10。

接下来,我们将num变量(即10)的内容设置为指针变量的内容。

我们输出num变量。

我们输出指针变量。

我们使用lea(加载有效地址)操作码而不是用mov输出num变量的地址。


我们使用lea(加载有效地址)操作码而不是用mov来输出指针变量的地址。
动态内存分配
我们列表中的下一个项目是动态内存分配。在本教程中,我将内存分配分解为以下内容:

  1. malloc
  2. calloc
  3. realloc
    malloc - 动态内存分配
    首先,我们来看看代码:

    在这个函数中,我们使用malloc分配11个字符,然后将“Hello World”复制到分配的内存空间中。
    现在,我们来看看反编译程序集:
    请注意:在整个汇编过程中,您可能会看到“nop”说明。这些说明是我在本文的准备阶段专门添加的,这样我就可以轻松地在整个汇编代码中定位和注释。

    使用malloc时,首先将分配的内存大小(0x0B)首先移入edi寄存器。接下来,调用_malloc系统函数来分配内存。然后将分配的存储区存储在ptr变量中。接下来,“Hello World”字符串被分解为“Hello Wo”和“rld”,因为它被复制到分配的存储空间中。最后,输出新复制的“Hello World”字符串,并使用_free系统函数释放分配的内存。
    calloc - 动态内存分配
    首先,我们来看看代码:

    与malloc非常相似,分配了11个字符的空间,然后将“Hello World”字符串复制到所述空间中。然后,输出新重定位的“Hello World”并释放分配的内存空间。

    通过calloc进行的动态内存分配看起来几乎与通过malloc分解为程序集时的动态内存分配相同。
    首先,使用_calloc系统函数分配11个字符(0x0B)的空间。然后,“Hello World”字符串被分解为“Hello Wo”和“rld”,因为它被复制到新分配的存储区中。接下来,输出新重定位的“Hello World”字符串,并使用_free系统函数释放分配的内存区域。
    realloc - 动态内存分配
    首先,让我们看一下代码:

    在此函数中,使用malloc分配11个字符的空间。然后,在通过使用realloc重新分配所述存储器位置以适合21个字符之前,将“ Hello World”复制到新分配的存储器空间中。最后,将“1337 h4x0r @nonymoose”复制到新重新分配的空间中。最后,在输出之后释放内存。
    现在,我们来看看反编译程序集:
    请注意:在整个汇编过程中,您可能会看到“nop”说明。这些说明是我在本文的准备阶段专门添加的,这样我就可以轻松地在整个汇编代码中定位和注释。

    首先,使用malloc分配内存,就像在上面的“ malloc -动态内存分配”部分中一样。然后,在输出新重定位的“Hello World”字符串后,在ptr变量(表示代码中的mem_alloc变量)上调用realloc(_realloc调用系统函数),并传入大小0x15(十进制21)。接下来,“1337 h4x0r @nonymoose”被分解为“1337 h4x”,“0r @nony”,“moos”和“e”因为它被复制到新重新分配的内存空间中。最后,使用_free调用系统函数释放空间。
    套接字编程
    接下来,我们将通过分解一个非常基本的TCP客户端 - 服务器聊天系统来介绍套接字编程。
    在我们开始分解服务器/客户端代码之前,重要的是指出文件顶部的以下代码行:

    此行将PORT变量定义为1337.此变量将在客户端和服务器中用作用于创建连接的网络端口
    服务器
    首先,让我们看一下代码:

    首先,使用AF_INET域,SOCK_STREAM类型和协议代码0创建套接字文件描述符“server” 。接下来,配置套接字选项和地址。然后,套接字绑定到网络地址/端口,服务器开始在所述服务器上监听,最大队列长度为3。收到连接后,服务器将其接受到sock变量中,并将传输的值读入value变量。最后,服务器在函数返回之前通过连接发送serverhello字符串。
    现在,我们将其分解为汇编:

    首先,创建并初始化服务器变量。

    接下来,通过使用分别通过edx,esi和edi寄存器传递的协议,类型和域设置调用_socket系统函数来创建套接字文件描述“server” 。

    然后,调用setsockopt来设置' server '套接字文件描述符上的套接字选项。

    接下来,通过adress.sin_family,address.sin_addr.s_addr和address.sin_port初始化服务器的地址。

    在地址和套接字配置时,服务器使用_bind调用系统函数绑定到网络地址。

    绑定后,服务器通过传入'server'套接字文件描述符并且最大队列长度为3来监听套接字。

    建立连接后,服务器接受套接字连接到sock变量。

    然后,服务器使用_read调用系统函数将传输的消息读入值变量。

    最后,服务器通过s变量(代表代码中的serverhello)发送serverhello消息。
    客户端
    首先,让我们看一下代码:

    首先,套接字文件描述符'sock'是使用AF_INET域,SOCK_STREAM类型和协议代码0创建的。接着,memset的用于填充的存储区域SERVER_ADDR与'0'之前的地址信息是利用设置的server_addr.sin_family和server_addr.sin_port。接下来,在客户端连接到服务器之前,使用inet_pton将地址信息从文本转换为二进制格式。连接后,客户端发送它的helloclient字符串,然后将服务器的响应读入值变量。最后,打印出value变量,函数返回。
    现在,我们将其分解为汇编:

    首先,初始化客户端的局部变量。

    'sock'套接字文件描述符是通过调用_socket系统函数并分别通过edx,esi和edi寄存器传递协议,类型和域信息来创建的。

    接下来,使用_memset系统调用将server_address变量(在程序集中表示为's')填充为'0'(0x30)。

    然后,配置服务器的地址信息。

    接下来,使用_inet_pton调用系统函数将地址从文本转换为二进制格式。请注意,由于代码中未明确定义地址,因此假定为localhost(127.0.0.1)。

    客户端使用_connect调用系统函数连接到服务器。

    连接后,客户端将helloClient字符串发送到服务器。

    最后,客户端使用_read调用系统函数将服务器的回复读入value变量。
    线程
    最后,我们将介绍C语言中的线程基础知识。
    首先,让我们看一下代码:

    如您所见,程序首先输出“This is is the thread”,然后使用pthread_create函数创建一个指向* mythread函数的新线程。完成* mythread函数后(在睡眠1秒后输出“Hello from mythread”),新线程使用pthread_join函数连接回主线程并输出“This is is the thread”。
    现在,我们将其分解为汇编:

    首先,程序输出 “This is before the thread”。

    接下来,使用_pthread_create调用系统函数创建一个新线程。这个线程指向mythread,因为它是启动例程。

    如您所见,mythread函数只需在输出“ Hello from mythread ” 之前休眠一秒钟。
    请注意:在mythread函数中,您将看到两个'nop'。这些内容专门用于在本文准备阶段轻松定位。

    从mythread函数返回后,新线程使用_pthread_join函数与主线程连接。

    最后,输出“This is after the thread”并将函数返回。
    结束陈述
    我希望这篇文章能够阐明一些更高级的编程概念及其底层汇编代码。

关键词:[‘技术文章’, ‘翻译文章’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now