翻译自: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”并将函数返回。
    结束陈述
    我希望这篇文章能够阐明一些更高级的编程概念及其底层汇编代码。

源链接

Hacking more

...