c语言的反编译问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c语言的反编译问题相关的知识,希望对你有一定的参考价值。

手上有个EX4文件..
是基于C语言编写的..
听说有个ex42mq4软件可以把它转为MQ4文件..
有人知道么?
或是可以反编译源码出来的高手.
有么?
QQ:52039179

参考技术A 除了 。net 框架以外的 语言 写的程序都不能反编译

c言语写的 除非是特别简单例如 HELLO WOLRD 那样的代码
否则 你放弃把
参考技术B 反编译源码——不可能——因为你的变量名全被舍弃,在编译时变成地址代码了。本回答被提问者采纳 参考技术C 一般编译程序是不可以反编译到源程序的,

有一些写的程序听说可以基本上反编译出源码:java vb
参考技术D 用w32asm可以反编译成汇编语言,如果你汇编好的话就没问题.

了解对象到源代码的反编译

【中文标题】了解对象到源代码的反编译【英文标题】:Understanding the decompilation of an object to source code 【发布时间】:2020-09-20 03:42:54 【问题描述】:

首先,我是一名学生,我对C、C++和汇编程序的知识还不是很全面,所以我非常努力地理解它。

我有一段来自 Intel x86-32 位处理器的汇编代码。

我的目标是将其转换为源代码。

0x80483dc <main>:    push       ebp        
0x80483dd <main+1>:  mov        ebp,esp     
0x80483df <main+3>:  sub        esp,0x10
0x80483e2 <main+6>:  mov        DWORD PTR [ebp-0x8],0x80484d0   
0x80483e9 <main+13>: lea        eax,[ebp-0x8]   
0x80483ec <main+16>: mov        DWORD PTR [ebp-0x4],eax     
0x80483ef <main+19>: mov        eax,DWORD PTR [ebp-0x4]     
0x80483f2 <main+22>: mov        edx,DWORD PTR [eax+0xc]
0x80483f5 <main+25>: mov        eax,DWORD PTR [ebp-0x4]         
0x80483f8 <main+28>: movzx      eax,WORD PTR [eax+0x10]
0x80483fc <main+32>: cwde
0x80483fd <main+33>: add        edx, eax
0x80483ff <main+35>: mov        eax,DWORD PTR [ebp-0x4]         
0x8048402 <main+38>: mov        DWORD PTR [eax+0xc],edx     
0x8048405 <main+41>: mov        eax,DWORD PTR [ebp-0x4]     
0x8048408 <main+44>: movzx      eax,BYTE PTR [eax]
0x804840b <main+47>: cmp        al,0x4f     
0x804840d <main+49>: jne        0x8048419 <main+61> 
0x804840f <main+51>: mov        eax,DWORD PTR [ebp-0x4] 
0x8048412 <main+54>: movzx      eax,BYTE PTR [eax] 
0x8048415 <main+57>: cmp        al,0x4b 
0x8048417 <main+59>: je         0x804842d <main+81> 
0x8048419 <main+61>: mov        eax,DWORD PTR [ebp-0x4] 
0x804841c <main+64>: mov        eax,DWORD PTR [eax+0xc]
0x804841f <main+67>: mov        edx, eax
0x8048421 <main+69>: and        edx,0xf0f0f0f
0x8048427 <main+75>: mov        eax,DWORD PTR [ebp-0x4] 
0x804842a <main+78>: mov        DWORD PTR [eax+0x4],edx
0x804842d <main+81>: mov        eax,0x0
0x8048432 <main+86>: leave
0x8048433 <main+87>: ret

这是我从代码中了解到的:

有4个变量:

a = [ebp-0x8] ebp
b = [ebp-0x4] eax
c = [eax + 0xc] edx
d = [eax + 0x10] eax

价值观:

0x4 = 4
0x8 = 8
0xc = 12
0x10 = 16
0x4b = 75
0x4f = 79

类型:

char (8 bits) = 1 BYTE
short (16 bits) = WORD
int (32 bit) = DWORD
long (32 bits) = DWORD
long long (32 bit) = DWORD

这是我能够创建的:

#include <stdio.h>
int main (void)

   int a = 0x80484d0;
   int b
   short c;
   int d;

   c + b?
if (79 <= al) 
instructions
 else 
instructions


   return 0

但我被困住了。我也无法理解“cmp al ..”这个句子与什么相比,“al”是什么?

这些说明如何工作?

编辑1:

也就是说,正如你评论的那样,大会似乎是错误的,或者正如某些 cmets 所说,这太疯狂了!

代码和练习来自以下书籍:“逆向,逆向工程”第 140 页(3.8 建议练习)。我永远不会想到这是错误的,如果是这样,这显然让我难以学习......

所以不能逆向获取源代码,因为它不是一个好的汇编?也许我没有被压迫?可以优化吗?

编辑2:

嗨!

我确实问过,最后她说这应该是 c 代码:

inf foo(void)
    char *string;//ebp-0x8
    unsigned int *pointerstring//[ebp-0x4]
    unsigned int *position;
    *position = *(pointerstring+0xc);
    unsigned char character;
    character=(unsigned char) string[*position];
    if ((character != 0x4)||(character != 0x4b))
    
     *(position+0x4)=(unsigned int)(*position & 0x0f0f0f0f);
    
    return(0);

这对你有任何意义吗?有人可以向我解释一下吗? 真的有人这样编程吗?

非常感谢!

【问题讨论】:

long long (32 bit) = DWORD 不正确。 C++ 标准要求 long long 为 64 位。 (嗯,不完全是,但它必须支持的最大值是它需要 64 位来存储它) 如果您需要询问al 是什么,那么您需要对处理器进行更深入的研究,然后才能理解反汇编或汇编列表。 al 寄存器是eax 寄存器的最低有效 8 位,ax 是 l.s. eax 的 16 位。 blcldlbxcxdx 类似。 ah 也是 eax 的下 8 位,因此 ahal 一起构成 ax Here's a link to the documentation。查找标题为“基本程序执行寄存器”的部分 @PeterCordes 我不认为你在放屁。我认为那个大会有一些非常奇怪的地方。特别是在0x80483f2 &lt;main+22&gt;: mov edx,DWORD PTR [eax+0xc] 之后,在我看来,返回地址最终会出现在edx 中,考虑到随后的代码如何处理它,这似乎是不可取的。 除非章节是关于混淆代码的,否则我会写信给作者。代码中有一个微不足道的逻辑错误,而且这本书没有在 Google 上弹出(你给我们的名字是对的吗?)所以我倾向于相信这本书要么有一个关于控制流混淆的精彩章节,要么有是由不应该写的人写的。 【参考方案1】:

你的程序集完全疯了。这大致相当于C:

int main() 
    int i = 0x80484d0; // in ebp-8
    int *p = &i; // in ebp-4
    p[3] += (short)p[4]; // add argc to the return address(!)
    if((char)*p != 0x4f || (char)*p != 0x4b) // always true because of || instead of &&
        p[1] = p[3] & 0xf0f0f0f; // note that p[1] is p
    return 0;

很明显,这是非常糟糕的代码,几乎肯定不会达到程序员的预期。

【讨论】:

0x80484d0 可能是 .rodata 部分中的地址,所以我在想char *arr[1] = "something" ;。但是,是的,int *p = (int*)arr; 和 UB 无法访问数组边界之外。我没看剩下的有多疯狂。 我问了我的老师,他说是因为他没有优化……老实说,如果你知道的不明白,我怀疑我也能做到.. @conjim “未优化”意味着代码做了正确的事情,但比必要的慢。这段代码做错了。 感谢您提供的信息,我想在这种情况下,没有什么可以做的了......我想也许我没有做对,但似乎每个人都同意这是错误的代码。【参考方案2】:

x86 汇编语言有着悠久的传统,并且大部分都保持了兼容性。我们需要回到故事开始的 8086/8088 芯片。这些是 16 位处理器,这意味着它们的寄存器的字长为 16 位。通用寄存器被命名为 AX、BX、CX 和 DX。 8086 具有操作这些寄存器的高 8 位和低 8 位部分的指令,这些寄存器随后被命名为 AH、AL、BH、BL、CH、CL、DH 和 DL。 This Wikipedia page 描述了这个,请看一下。

这些寄存器的 32 位版本前面有一个E:EAX、EBX、ECX 等。

您提到的特定指令,例如cmp al,0x4f 是将 AX 寄存器的低字节与 0x4f 进行比较。比较实际上与减法相同,但不保存结果,仅设置标志。

对于 8086 指令集,there is a nice reference here。您的程序是 32 位代码,因此您至少需要 80386 指令参考。

【讨论】:

【参考方案3】:

您已经分析了变量,这是一个很好的起点。您应该尝试在开始时为它们添加类型注释,大小,当用作指针时(如b),指向什么类型/大小的指针。

知道[ebp-4]b,我可能会更新您的变量图表如下:

c = [b + 0xc]
d = [b + 0x10]
e = [b + 0], size = byte

要分析的另一件事是控制流。对于大多数指令,控制流是顺序的,但某些指令会故意改变它。从广义上讲,当 pc 向前移动时,它会跳过一些代码,而当 pc 向后移动时,它会重复一些它已经运行过的代码。跳过代码用于构造 if-then、if-then-else 和跳出循环的语句。后退用于继续循环。

某些指令称为条件分支,在某些动态条件为真时:向前(或向后)跳过,在为假时执行简单的顺序前进到下一条指令(有时称为条件分支失败)。

这里的控制序列:

...
0x8048405 <main+41>: mov        eax,DWORD PTR [ebp-0x4]    b
0x8048408 <main+44>: movzx      eax,BYTE PTR [eax]         b->e

0x804840b <main+47>: cmp        al,0x4f                    b->e <=> 'O'
0x804840d <main+49>: jne        0x8048419 <main+61>        b->e != 'O'  skip to 61

** we know that the letter, a->e, must be 'O' here

0x804840f <main+51>: mov        eax,DWORD PTR [ebp-0x4]    b      
0x8048412 <main+54>: movzx      eax,BYTE PTR [eax]         b->e

0x8048415 <main+57>: cmp        al,0x4b                    b->e <=> 'K'
0x8048417 <main+59>: je         0x804842d <main+81>        b->e == 'K' skip to 81

** we know that the letter, a->e must not be 'K' here if we fall thru the above je 

** this line can be reached by taken branch jne or by fall thru je
0x8048419 <main+61>: mov        eax,DWORD PTR [ebp-0x4]    ******
...

控制流到达最后一行标记为我们知道字母不是“O”或者不是“K”。

jne 指令用于跳过另一个测试的构造是短路|| 运算符。因此控制结构是:

if ( a->e != 'O' || a->e != 'K' ) 
    then-part

由于这两个条件分支是函数中唯一的流控修改,所以if没有else部分,也没有循环或其他if。


这段代码似乎有一点问题。

如果值不是 'O',则 then 部分将从第一个测试中触发。但是,如果我们进行第二次测试,我们已经知道字母是 'O',所以测试 'K' 是愚蠢的,并且会是真的('O' 不是 'K')。

因此,这个 if-then 将始终触发。

要么效率非常低,要么存在一个错误,可能是(可能)字符串中的下一个字母应该针对“K”而不是完全相同的字母进行测试。

【讨论】:

[ebp-4]b - b 不是指针变量,持有地址 ebp-8?因此,与[eax + 0xc] 的偏移是索引到函数自己的堆栈帧,mov edx, [eax + 0xc] 加载函数的返回地址。 (Joseph Sible 和我 discussed this in comments;这对我们来说看起来很疯狂。我想知道原始代码是否在像 char *arr[1] = "hello" ; 这样的本地数组的范围之外进行索引?) 我仍然很困惑......但非常感谢您提供的信息。有没有办法自动将目标代码转换为源代码,即使它远程相似......但不完全是?谢谢

以上是关于c语言的反编译问题的主要内容,如果未能解决你的问题,请参考以下文章

谁能帮我把一个hex文件反编译为c语言文件?

C#开源项目-二进制文件的反编译器

如何关闭 Rider 中文件的反编译?

java 是啥语言写的

C 和 C++ 编译器的问题

iOS中的反编译可能性以及如何防止它们