使用免费工具进行逆向和利用:第20部分

admin 2022年4月15日10:42:23使用免费工具进行逆向和利用:第20部分已关闭评论20 views字数 5030阅读16分46秒阅读模式

原作者:Ricardo Narvaja

翻译作者:梦幻的彼岸

更新日期:2022年1月21日


我们继续讨论结构问题。

在上面的第19部分中,我们比较了两个练习:"无结构 "和 "有结构"。

https://drive.google.com/file/d/10p5i8gDHLUgz9Xag_1XadpHBCyNAPisB/view?usp=sharing

password =a

以及 "有结构"(With STRUCTURES)的情况

https://drive.google.com/file/d/1i_PKAY5ustTHjPdoM75ffwfCmXH9cDb9/view?usp=sharing

我们已经看到了STRUCTURE-FREE,它将所有的局部变量分开。

使用免费工具进行逆向和利用:第20部分

我们将其反汇编,然后是有结构体,这两个都有它们的源代码附加。

使用免费工具进行逆向和利用:第20部分

他们也有自己的符号,因此,如果在IDA中加载它,在改变DEMANGLE NAMES -NAMES选项后,它看起来很好。

使用免费工具进行逆向和利用:第20部分

但在现实中,我们并不总是有这些符号,或者几乎没有,所以我们将尝试在没有符号的情况下进行反汇编。

我们进入可执行文件所在的文件夹,删除作为数据库的i64,以便它再次扫描,同时重命名pdb文件。

使用免费工具进行逆向和利用:第20部分

我们注意到,它现在要求提供符号,所以很明显,没有符号它也会分析,我们按下CANCEL,它将继续分析。

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

现在我们几乎没有任何信息,让我们看看这些字符串。

使用免费工具进行逆向和利用:第20部分

让我们去看看这些字符串被使用的地方,例如pepe.dll

使用免费工具进行逆向和利用:第20部分

在那里我按 X 并转到。

使用免费工具进行逆向和利用:第20部分

让我们去那里。

使用免费工具进行逆向和利用:第20部分

在那里,我们看到了他们总是问我的一个问题的答案。

我们怎么知道什么是结构?

您可以看到,如果你把一个值从一个变量传到一个寄存器,然后访问这个值加上一个偏移量,这就是一个结构。

使用免费工具进行逆向和利用:第20部分

在那里,例如,如果我问自己,arg_0会是一个什么样的变量,它把自己的值读到ECX,然后在这个值上加上0x44来访问另一个值?

使用免费工具进行逆向和利用:第20部分

很明显,一个结构是通过一个存储结构起始地址的指针变量来管理的,因为在本例中arg_0是一个指向结构起始地址的指针。

My_struct * arg0;

如果我们看一下源代码,它看起来像这样,但由于我们没有它,我们只知道它是一个指向未知结构的指针。

使用免费工具进行逆向和利用:第20部分

因此,在 IDA 中,我将创建一个新的结构,通过倒退来确定结构的字段,在 STRUCTURES 标签中,我按下 INS 键。

使用免费工具进行逆向和利用:第20部分

我给它起个名字,现在我不知道它叫什么,也不知道它有什么用,我就叫它unknown_struct

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

现在我们将看到大小,是否精确无关紧要,然后我们可以在必要时增加或缩小,即使它更大也不重要,我们要做的是看我看到它访问的最大字段的偏移量。

使用免费工具进行逆向和利用:第20部分

我们看到它在这里访问的最大偏移量是0x54,为了以防万一,我将设置长的0x60,然后我再看看。

使用免费工具进行逆向和利用:第20部分

如果我不介意字段都是1个字节,我可以右击ARRAY,将大小设置为0x60。

使用免费工具进行逆向和利用:第20部分

移除CREATE AS ARRAY的tilde,它将创建一个所有字段都是byte类型的结构,但稍后我将看到每个字段是什么类型并加以纠正。

如果我知道他们中的大多数将是DWORDS,我会这样做,首先添加一个DWORD。

使用免费工具进行逆向和利用:第20部分

我在结构结束的地方停下来,按了几次d键都没有动,直到类型发生变化,剩下的是一个DWORD。

使用免费工具进行逆向和利用:第20部分

现在如果我像以前一样在那里做一个数组,我必须去掉CREATE AS ARRAY的复选标记。

使用免费工具进行逆向和利用:第20部分

它告诉我ELEMENT SIZE将是4或DWORD,所以如果我想让它有0x60的长度,我将不得不输入长度0x60/4。

使用免费工具进行逆向和利用:第20部分

而现在看起来不错。

使用免费工具进行逆向和利用:第20部分

嗯,我已经有了基本的结构。

我们需要将arg_0的类型改为指向该结构的指针。

使用免费工具进行逆向和利用:第20部分

进入堆栈的静态表示,右击--SET TYPE,然后进入。

使用免费工具进行逆向和利用:第20部分

然后通过重命名,我们可以给这个变量起个名字,比如store

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

我们可以检查类型,看看它是否看起来不错。

我可以在函数中执行 SET TYPE 来传播类型。

使用免费工具进行逆向和利用:第20部分

我们看到,通过更改指向我已经定义的结构类型的指针并在main函数中传播,它现在可以检测字段,这很有帮助。

使用免费工具进行逆向和利用:第20部分

我们看到它传播到main函数,但在我们自己的函数中没有。

使用免费工具进行逆向和利用:第20部分

我们在每个偏移处按T,并选择创建的结构。

使用免费工具进行逆向和利用:第20部分

当我完成时,它看起来是这样的。

使用免费工具进行逆向和利用:第20部分

现在好了,现在让我们离开这个函数,回到main函数。

使用免费工具进行逆向和利用:第20部分

因此,当pushea ECX的时候,它有结构所在的地址,当然,正如我们所看到的,接收这个的变量是arg_0,因为它不是真正的存储,而是一个指向存储的指针,我们可以把它改为p_store

使用免费工具进行逆向和利用:第20部分

现在,如果arg_0是指向结构的指针,则最好将其命名为p_store,因为它是一个指针变量,我们知道它存储变量或参数的地址,在本例中,它存储store的地址。

使用免费工具进行逆向和利用:第20部分

现在看起来很完美,但真正定义的store在哪里,让我们看看。

使用免费工具进行逆向和利用:第20部分

在这里,我们看到它是main函数的局部变量,长度正好是0x5c,比我分配给它的0x60小,当然是一个变量,因为它在返回地址的顶部。

所以在低层上,这个结构是在堆栈上创建的,作为堆栈本身的一个缓冲区

如果我们用源代码检查,我们会发现这是真的,因为它也可以通过在堆中分配内存来创建,但情况并非如此。

使用免费工具进行逆向和利用:第20部分

现在我们有了这个,这几乎是小菜一碟,嘿嘿,好吧,不是那么多,我们还没有符号,但我们已经迈出了一大步。

使用免费工具进行逆向和利用:第20部分

我们看到结构的构造函数或初始化函数,它也通过LEA传递给p_store。

参数会依记录传递,并储存在var_4中。

使用免费工具进行逆向和利用:第20部分

如果要定义一个按注册表传递参数的函数,可以使用usercall,它是IDA用来表示非标准调用约定的方法。

使用ecx和堆栈中的参数并返回eax中的结果的函数示例。

int __usercall NOMBRE_FUNCION@<eax>(int pepe, int pipo@<ecx>)

对我们来说是严重的。

int __usercall NOMBRE_FUNCION@<eax>(unknown_struct * p_store@<ecx>)

虽然在eax中没有返回任何内容,但我可以删除返回值并将其设置为void而不是int。

void __usercall sub_401000(unknown_struct *p_store@<ecx>);

使用免费工具进行逆向和利用:第20部分

我们可以将函数重命名为结构的构造函数,就像它是一个类一样,因为它们在水平上看起来非常相似。

使用免费工具进行逆向和利用:第20部分

将双冒号":: "改为下划线,没有问题。

使用免费工具进行逆向和利用:第20部分

我将var_4重命名为p_store,并将指针类型设为结构。

使用免费工具进行逆向和利用:第20部分

但首先我们看到有一个问题,字段0x48是字节类型的,我们必须改变它。

使用免费工具进行逆向和利用:第20部分

在Structures(结构)选项卡中,我按下d,直到它是字节类型的。

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

它停留在那里,在三个仍未定义的地方,我也按下了d,以便它们被定义。

使用免费工具进行逆向和利用:第20部分

现在,如果我在每次选择结构类型 unknown_struct 时按 T。

使用免费工具进行逆向和利用:第20部分

我们看到它将除了 field_50 之外的所有内容都初始化为零,我可以将其重命名为 const_0x32。

使用免费工具进行逆向和利用:第20部分

好吧,我们在这里看到它从 IAT 中读取 LoadLibraryA 的地址,并将其保存在我们重命名的 0x54 字段中。

使用免费工具进行逆向和利用:第20部分

当然,当您在此处更改时,您会看到在我们开始的函数中也发生了更改,并且调用loadlibrarya在此处加载pepe.dll。

使用免费工具进行逆向和利用:第20部分

从参数中我们意识到您想以二进制读取模式(rb)打开文件

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

我们可以看到fopen与这些参数相匹配,它们是两个字符串,一个是名称,另一个是模式(在本例中为“rb”),在这些包含嵌入dll的文件中,从经验上来说,了解哪个api通常不使用包含嵌入dll的文件,只在非常特殊的情况下使用,您应该在此看到对dll的导入函数的调用,并向我们显示名称

使用免费工具进行逆向和利用:第20部分

返回的值是一个指向FILE类型的指针。

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

当然,如果文件file.dat存在,它将跟随箭头移动,否则它必须与可执行文件在同一文件夹中创建。

使用免费工具进行逆向和利用:第20部分

好吧,我们看到有一个函数,我认为它将是用于fread参数。

使用免费工具进行逆向和利用:第20部分

一个缓冲区,然后是两个整数,然后是一个指向FILE的指针,完全匹配fread。

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

那么第二个参数是项目的大小,第三个参数是要读取的项目数量。

通过重命名为_fread,IDA检测到了类型,实际情况是它读取了4个字节,所以EDX中的缓冲区是一个指向field_8的指针,告诉我们field_8将是一个DWORD,因为它将在那里存储4个字节。

使用免费工具进行逆向和利用:第20部分

我把结构中的字段重命名为field_8。

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

然后它又读了一个DWORD,第三次读了一个BYTE,重命名。

我们看到,它读取BYTE_LEIDO并与0x32进行比较,如果它小于0x32,它就会跟随,记住,它可能是负数,这就是想法,因为JL是一个考虑符号的跳转,所以0xff是负数,MOVSX用前面的FF完成它,它将是0xFFFFFFFFxx,当与0x32进行比较时,它也是负数,它将小于0x32。

使用免费工具进行逆向和利用:第20部分

我们看到了对printf的调用,通过参数,我们意识到它在控制台中打印了cookie、cookie2和size的值,所以我们可以用printf显示的名称来重命名要打印的值。

使用免费工具进行逆向和利用:第20部分

现在它看起来更漂亮了。

然后我们看到一个以数字为参数的调用

使用免费工具进行逆向和利用:第20部分

如果我们看看里面。

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

我们看到,它是malloc,它只有一个数字参数。

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

我可以把它重命名为p_chunk_32。

使用免费工具进行逆向和利用:第20部分

我们看到它在field_44和field_40中存储了一个指向cookie的指针和一个指向size的指针,重新命名。

我们已经完成了这个工作。,所有这些都被反汇编了。

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

将函数重命名为load _ dll,因为如果cookie + cookie2的和等于0x 585523343,则会发生这种情况。

我们看到它找到属于cookie的p_cookie的内容,并将它与cookie2相加并进行比较。

所以我们已经知道,要加载dll,文件的前4个字节加上4秒的字节必须等于0x 585523343,第9个字节必须大于0x7f才能为负数。

最后,您将使用使用movsx填充FFs并溢出field_C缓冲区的负大小调用fread,让我们来看看该缓冲区的长度。

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

我们在结构中看到,标记区域将是缓冲区,因为随后将出现p_size和p_cookie。

使用免费工具进行逆向和利用:第20部分

通过按field_C中的d键将其更改为字节,然后创建数组缓冲区,该缓冲区长度为52(十进制长度)。

使用免费工具进行逆向和利用:第20部分

回顾一下,它检查了要复制的大小是否大于0x32。

使用免费工具进行逆向和利用:第20部分

问题是,我在比较中考虑符号,因为程序使用signed,但它说unsigned编译器默认使用signed。

使用免费工具进行逆向和利用:第20部分

问题是,0xffffffxx将是要从文件中读取的大小,因为fread认为大小是无符号的,因为类型size t是无符号的。

使用免费工具进行逆向和利用:第20部分

因此,我们可以读取我们想要的数量,它将结束在它不能再读取的地方,允许我们进入返回地址。

使用免费工具进行逆向和利用:第20部分

因为结构下面是返回地址。

使用免费工具进行逆向和利用:第20部分

所以我们必须填写整个结构并输入返回地址。

不管怎样,我们必须小心,直到我们到达返回地址,然后我们看到有一个memcpy

使用免费工具进行逆向和利用:第20部分

它有三个参数,一个缓冲区和一个大小。

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

但是用malloc创建的缓冲区的目的地没有被它的指针占用,所以不会有问题,大小被硬编码为0x32。

使用免费工具进行逆向和利用:第20部分

pepe.dll还具有内置的system函数,可以直接调用该函数,因为它没有随机化,因此使用该函数可以轻松运行计算器。

如果我错了,那就是原始文件没有随机化,而我们下载的这个文件如果,我们以后会把它删除,我们会处理的,在ollydbg是最快的,我们切换到special -pe header并搜索dll特征。

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

设置为零并保存

使用免费工具进行逆向和利用:第20部分

然后复制到可执行文件,再保存文件。

使用免费工具进行逆向和利用:第20部分

我们通过一个合适的file.dat看到了这一点

使用免费工具进行逆向和利用:第20部分

我们看到,在这个版本中,它还删除了EAX中保留的指向我们想要的字符串的指针,因为这是我们的缓冲区,我使用memcpy将其复制到我相信的malloc缓冲区中,这使我们变得复杂。

使用免费工具进行逆向和利用:第20部分

我的意思是,我们在这里,但是哪里都没有指向缓冲区的指针。

使用免费工具进行逆向和利用:第20部分

使用免费工具进行逆向和利用:第20部分

幸运的是,在pepe.dll上有一个Push-ret。

使用免费工具进行逆向和利用:第20部分

System在那里

使用免费工具进行逆向和利用:第20部分

像这样的代码

0019FF30 8BC4 MOV EAX,ESP

0019FF32 83E8 64 SUB EAX,64

0019FF35 8B00 MOV EAX,DWORD PTR DS:[EAX]

0019FF37 BE 24980178 MOV ESI,pepe.system

0019FF3C 50 PUSH EAX

0019FF3D FFD6 CALL ESI

8B C4 83 E8 64 8B 00 BE 24 98 01 78 50 FF D6

我们将它放在 push esp-ret 之后

使用免费工具进行逆向和利用:第20部分

并且已经在运行记事本

使用免费工具进行逆向和利用:第20部分

直到第 21 部分,我们将在 Radare 和下一个 GHIDRA 中进行相同的练习。

PS:没有必要去掉随机化,这个方案在原版中的效果是一样的,但是我们学到了一些别的东西,呵呵,如果你没有启用dep,效果也是一样的,但是我们学会了如何去掉它来练习。

祝您好运

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月15日10:42:23
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   使用免费工具进行逆向和利用:第20部分http://cn-sec.com/archives/913073.html