Hex

您所在的位置:网站首页 hex反编译ino Hex

Hex

2024-06-06 03:12| 来源: 网络整理| 查看: 265

反编译与反汇编简介

反编译器以可读形式表示可执行二进制文件 span>。 更准确地说,它将二进制代码转换为软件开发人员可以读取和修改的文本。 软件安全行业依靠这种转换来分析和验证程序。 该分析是对二进制代码执行的,因为传统上源代码(软件的文本形式)不可用,因为它被认为是商业秘密。

将二进制代码转化为文本形式的程序一直存在。将处理器指令代码简单地一对一映射成指令口令是由反汇编器完成的。市场上有许多反汇编程序,包括免费的和商业的。最强大的反汇编程序是我们自己的IDA Pro。它可以处理大量处理器的二进制代码,并且具有开放的架构,允许开发人员编写附加的分析模块。

反编译器在一个非常重要的方面与反编译器不同。 两者都生成人类可读的文本,而反编译器生成更高级的文本 span>,它更简洁 span>和更容易阅读 span>。

与低级汇编语言相比,高级语言表示具有以下优点:

它很简洁 它是结构化的 它并不要求开发人员了解汇编语言 它能识别低级成语并将其转换为高级概念 它的混乱程度较低,因此比较容易理解 它的重复性较低,不易分散注意力 它使用了数据流分析

我们来详细考虑一下这几点。

通常反编译器的输出比反汇编器的输出短五到十倍。例如,一个典型的现代程序包含400KB到5MB的二进制代码。反汇编器对这样一个程序的输出将包括大约5-100MB的文本,这可能需要几周到几个月的时间才能完全分析。由于经济原因,分析人员不可能在一个程序上花费这么多时间。

典型程序的反编译器输出为400KB至10MB。 尽管这仍然是很大的阅读和理解量(大约是一本厚书的大小),但分析时间所需的时间除以10或更多。

第二个大区别是反编译器输出是结构化的 span>。 代替使每行与所有其他行都相似的线性指令流,对文本进行缩进以使程序逻辑明确。 控制流构造 span>(例如条件语句,循环和开关)都标记有适当的关键字。

由于反编译器的输出是高级别的,因此它比反汇编程序的输出更易于理解。 为了能够使用反汇编程序,分析人员必须知道目标处理器的汇编语言。 主流程序员并不将汇编语言用于日常任务,但如今几乎每个人都使用高级语言。 反编译器消除了典型的编程语言和输出语言之间的差距。 更多的分析师可以使用反编译器而不是反汇编程序。

反编译器将汇编级习语转换为高级抽象。有些习语的分析时间可能相当长,而且很耗时。以下是一行代码

x = y / 2;

可以被编译器转化为一系列20-30条处理器指令。一个有经验的分析人员至少需要15-30秒的时间才能识别出这种模式,并在心里将其替换成原来的行。如果代码中包含许多这样的成语,分析师就不得不做笔记,并将每个模式用它的简短表示法标记出来。所有这些都极大地减慢了分析速度。反编译器消除了分析人员的这种负担。

要分析的汇编器指令数量巨大。它们看起来彼此非常相似,并且它们的模式非常重复。读取反汇编程序输出与读取引人入胜的故事完全不同。在编译器生成的程序中,95%的代码将非常无聊,无法阅读和分析。对于分析师来说,将两个看起来相似的代码片段混淆起来非常容易,而在输出中就完全迷路了。这两个因素(文本的大小和无聊的性质)导致以下现象:从未对二进制程序进行全面分析。分析人员尝试使用一些启发式方法和一些自动化工具来定位可疑部分。当程序很小或分析人员花费不成比例的大量时间进行分析时,就会发生异常。反编译器可以缓解这两个问题:它们的输出较短且重复性较低。输出仍然包含一些重复,但是可以由人管理。此外,这种重复可以通过自动化分析来解决。

二进制代码中的重复模式需要一个解决方案。一个显而易见的解决方案是利用计算机来寻找模式,并以某种方式将其缩减为更短的、更容易为人类分析人员所掌握的东西。一些拆解器(包括IDA Pro)提供了一种自动分析的手段。然而,可用的分析模块数量一直不多,所以重复的代码仍然是一个问题。主要原因是,识别二进制模式是一项出人意料的困难任务。任何 "简单 "的动作,包括基本的算术运算,如加法和减法,都可以用无穷无尽的方式用二进制形式表示。编译器可能用加法运算符来做减法,反之亦然。它可以在内存中的某个地方存储常数,并在需要时加载它们。它可以利用经过一些操作后,可以证明寄存器的值是一个已知的常数,只需使用寄存器而不重新初始化它。使用方法的多样性解释了可用的分析模块数量少的原因。

有了反编译器,情况就不同了。自动化变得更加容易,因为反编译器为分析人员提供了高层次的概念。许多模式会被自动识别并被抽象的概念所取代。由于反编译器引入了形式化的概念,剩余的模式可以很容易地被检测出来。例如,函数参数和调用约定的概念被严格地形式化。反编译器使我们极易找到任何函数调用的参数,即使这些参数的初始化距离调用指令很远。如果使用反汇编器,这是一项艰巨的任务,它需要单独处理每种情况。

反编译器,与反汇编器相反,对输入进行广泛的数据流分析。这意味着,诸如 "变量在哪里初始化?"和 "这个变量被使用了吗?"这样的问题可以立即得到回答,而不需要对函数进行任何广泛的搜索。分析师经常提出并回答这些问题,有了答案就能立即提高他们的工作效率。

反汇编和反编译的并列比较

下面你会发现反汇编和反编译输出的并列比较。以下是一些例子:

此页面上显示以下示例:

一分为二 li> 够简单吗? li> 我的变量在哪里? li> 算术不是火箭科学 li> 示例窗口过程 li> 短路评估 li> 内联字符串操作 li> 一分为二

只要注意大小上的差异! 尽管反汇编输出不仅需要您知道编译器会为有符号除法和模运算生成此类复杂的代码,而且还必须花费时间来识别模式。 不用说,反编译器使事情变得非常简单。

汇编代码 ; =============== S U B R O U T I N E ======================================= ; Attributes: bp-based frame ; mod_ll(long long) public __Z6mod_llx __Z6mod_llx proc near var_10 = dword ptr -10h var_C = dword ptr -0Ch arg_0 = qword ptr 8 push ebp mov ebp, esp push ebx sub esp, 0Ch mov ecx, dword ptr [ebp+arg_0] mov ebx, dword ptr [ebp+arg_0+4] mov eax, ecx mov edx, ebx mov eax, edx mov edx, eax sar edx, 1Fh sar eax, 1Fh mov eax, edx mov edx, 0 shr eax, 1Fh add eax, ecx adc edx, ebx shrd eax, edx, 1 sar edx, 1 mov [ebp+var_10], eax mov [ebp+var_C], edx mov eax, [ebp+var_10] mov edx, [ebp+var_C] shld edx, eax, 1 add eax, eax sub ecx, eax sbb ebx, edx mov [ebp+var_10], ecx mov [ebp+var_C], ebx mov eax, [ebp+var_10] mov edx, [ebp+var_C] add esp, 0Ch pop ebx pop ebp retn __Z6mod_llx endp 伪代码 __int64 __cdecl mod_ll(__int64 a1) { return a1 % 2; } 够简单吗?

类似的问题

函数可能的返回值有哪些? 该函数是否使用任何字符串? 该函数的作用是什么?

几乎可以在看反编译器输出的瞬间得到答案。不用说,它看起来更好,因为我重命名了局部变量。在反汇编器中,很少重命名寄存器,因为它隐藏了寄存器的用途,会导致混乱。

汇编代码 ; =============== S U B R O U T I N E ======================================= ; int __cdecl sub_4061C0(char *Str, char *Dest) sub_4061C0 proc near ; CODE XREF: sub_4062F0+15p ; sub_4063D4+21p ... Str = dword ptr 4 Dest = dword ptr 8 push esi push offset aSmtp_ ; "smtp." push [esp+8+Dest] ; Dest call _strcpy mov esi, [esp+0Ch+Str] push esi ; Str call _strlen add esp, 0Ch xor ecx, ecx test eax, eax jle short loc_4061ED loc_4061E2: ; CODE XREF: sub_4061C0+2Bj cmp byte ptr [ecx+esi], 40h jz short loc_4061ED inc ecx cmp ecx, eax jl short loc_4061E2 loc_4061ED: ; CODE XREF: sub_4061C0+20j ; sub_4061C0+26j dec eax cmp ecx, eax jl short loc_4061F6 xor eax, eax pop esi retn ; --------------------------------------------------------------------------- loc_4061F6: ; CODE XREF: sub_4061C0+30j lea eax, [ecx+esi+1] push eax ; Source push [esp+8+Dest] ; Dest call _strcat pop ecx pop ecx push 1 pop eax pop esi retn sub_4061C0 endp 伪代码 signed int __cdecl sub_4061C0(char *Str, char *Dest) { int len; // int i; // char *str2; // signed int result; // strcpy(Dest, "smtp."); str2 = Str; len = strlen(Str); for ( i = 0; i < len; ++i ) { if ( str2[i] == 64 ) break; } if ( i < len - 1 ) { strcat(Dest, &str2[i + 1]); result = 1; } else { result = 0; } return result; } 我的变量在哪里?

IDA高亮了当前的标识符。事实证明,这个功能在高层次的输出中更加有用。在这个示例中,我试图追踪检索到的函数指针是如何被函数使用的。 在反汇编输出中,许多错误的eax出现被高亮显示,而反编译器却完全按照我的要求做了。

汇编代码 ; =============== S U B R O U T I N E ======================================= ; int __cdecl myfunc(wchar_t *Str, int) myfunc proc near ; CODE XREF: sub_4060+76p ; .text:42E4p Str = dword ptr 4 arg_4 = dword ptr 8 mov eax, dword_1001F608 cmp eax, 0FFFFFFFFh jnz short loc_10003AB6 push offset aGetsystemwindo ; "GetSystemWindowsDirectoryW" push offset aKernel32_dll ; "KERNEL32.DLL" call ds:GetModuleHandleW push eax ; hModule call ds:GetProcAddress mov dword_1001F608, eax loc_10003AB6: ; CODE XREF: myfunc+8j test eax, eax push esi mov esi, [esp+4+arg_4] push edi mov edi, [esp+8+Str] push esi push edi jz short loc_10003ACA call eax ; dword_1001F608 jmp short loc_10003AD0 ; --------------------------------------------------------------------------- loc_10003ACA: ; CODE XREF: myfunc+34j call ds:GetWindowsDirectoryW loc_10003AD0: ; CODE XREF: myfunc+38j sub esi, eax cmp esi, 5 jnb short loc_10003ADD pop edi add eax, 5 pop esi retn ; --------------------------------------------------------------------------- loc_10003ADD: ; CODE XREF: myfunc+45j push offset aInf_0 ; "\\inf" push edi ; Dest call _wcscat push edi ; Str call _wcslen add esp, 0Ch pop edi pop esi retn myfunc endp 伪代码 size_t __cdecl myfunc(wchar_t *buf, int bufsize) { int (__stdcall *func)(_DWORD, _DWORD); // [email protected] wchar_t *buf2; // [email protected] int bufsize; // [email protected] UINT dirlen; // [email protected] size_t outlen; // [email protected] HMODULE h; // [email protected] func = g_fptr; if ( g_fptr == (int (__stdcall *)(_DWORD, _DWORD))-1 ) { h = GetModuleHandleW(L"KERNEL32.DLL"); func = (int (__stdcall *)(_DWORD, _DWORD)) GetProcAddress(h, "GetSystemWindowsDirectoryW"); g_fptr = func; } bufsize = bufsize; buf2 = buf; if ( func ) dirlen = func(buf, bufsize); else dirlen = GetWindowsDirectoryW(buf, bufsize); if ( bufsize - dirlen >= 5 ) { wcscat(buf2, L"\\inf"); outlen = wcslen(buf2); } else { outlen = dirlen + 5; } return outlen; } 算术不是火箭科学

算术不是一门火箭科学,但是如果有人为您处理算术,它总是更好。 您还有更多重要的事情要关注。

汇编代码 ; =============== S U B R O U T I N E ======================================= ; Attributes: bp-based frame ; sgell(__int64, __int64) public @sgell$qjj @sgell$qjj proc near arg_0 = dword ptr 8 arg_4 = dword ptr 0Ch arg_8 = dword ptr 10h arg_C = dword ptr 14h push ebp mov ebp, esp mov eax, [ebp+arg_0] mov edx, [ebp+arg_4] cmp edx, [ebp+arg_C] jnz short loc_10226 cmp eax, [ebp+arg_8] setnb al jmp short loc_10229 ; --------------------------------------------------------------------------- loc_10226: ; CODE XREF: sgell(__int64,__int64)+Cj setnl al loc_10229: ; CODE XREF: sgell(__int64,__int64)+14j and eax, 1 pop ebp retn @sgell$qjj endp 伪代码 bool __cdecl sgell(__int64 a1, __int64 a2) { return a1 >= a2; } 样本窗口程序

反编译器识别出switch语句,很好地表示了窗口过程。 没有这些帮助,用户将不得不自己计算消息号。 没什么特别困难的,只是费时又无聊。 如果她犯错了怎么办?...

汇编代码 ; =============== S U B R O U T I N E ======================================= wndproc proc near ; DATA XREF: sub_4010E0+21o Paint = tagPAINTSTRUCT ptr -0A4h Buffer = byte ptr -64h hWnd = dword ptr 4 Msg = dword ptr 8 wParam = dword ptr 0Ch lParam = dword ptr 10h mov ecx, hInstance sub esp, 0A4h lea eax, [esp+0A4h+Buffer] push 64h ; nBufferMax push eax ; lpBuffer push 6Ah ; uID push ecx ; hInstance call ds:LoadStringA mov ecx, [esp+0A4h+Msg] mov eax, ecx sub eax, 2 jz loc_4013E8 sub eax, 0Dh jz loc_4013B2 sub eax, 102h jz short loc_401336 mov edx, [esp+0A4h+lParam] mov eax, [esp+0A4h+wParam] push edx ; lParam push eax ; wParam push ecx ; Msg mov ecx, [esp+0B0h+hWnd] push ecx ; hWnd call ds:DefWindowProcA add esp, 0A4h retn 10h ; --------------------------------------------------------------------------- loc_401336: ; CODE XREF: wndproc+3Cj mov ecx, [esp+0A4h+wParam] mov eax, ecx and eax, 0FFFFh sub eax, 68h jz short loc_40138A dec eax jz short loc_401371 mov edx, [esp+0A4h+lParam] mov eax, [esp+0A4h+hWnd] push edx ; lParam push ecx ; wParam push 111h ; Msg push eax ; hWnd call ds:DefWindowProcA add esp, 0A4h retn 10h ; --------------------------------------------------------------------------- loc_401371: ; CODE XREF: wndproc+7Aj mov ecx, [esp+0A4h+hWnd] push ecx ; hWnd call ds:DestroyWindow xor eax, eax add esp, 0A4h retn 10h ; --------------------------------------------------------------------------- loc_40138A: ; CODE XREF: wndproc+77j mov edx, [esp+0A4h+hWnd] mov eax, hInstance push 0 ; dwInitParam push offset DialogFunc ; lpDialogFunc push edx ; hWndParent push 67h ; lpTemplateName push eax ; hInstance call ds:DialogBoxParamA xor eax, eax add esp, 0A4h retn 10h ; --------------------------------------------------------------------------- loc_4013B2: ; CODE XREF: wndproc+31j push esi mov esi, [esp+0A8h+hWnd] lea ecx, [esp+0A8h+Paint] push ecx ; lpPaint push esi ; hWnd call ds:BeginPaint push eax ; HDC push esi ; hWnd call my_paint add esp, 8 lea edx, [esp+0A8h+Paint] push edx ; lpPaint push esi ; hWnd call ds:EndPaint pop esi xor eax, eax add esp, 0A4h retn 10h ; --------------------------------------------------------------------------- loc_4013E8: ; CODE XREF: wndproc+28j push 0 ; nExitCode call ds:PostQuitMessage xor eax, eax add esp, 0A4h retn 10h wndproc endp 伪代码 LRESULT __stdcall wndproc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { LRESULT result; // HWND h; // HDC dc; // CHAR Buffer; // [sp+40h] [bp-64h]@1 struct tagPAINTSTRUCT Paint; // [sp+0h] [bp-A4h]@10 LoadStringA(hInstance, 0x6Au, &Buffer, 100); switch ( Msg ) { case 2u: PostQuitMessage(0); result = 0; break; case 15u: h = hWnd; dc = BeginPaint(hWnd, &Paint); my_paint(h, dc); EndPaint(h, &Paint); result = 0; break; case 273u: if ( (_WORD)wParam == 104 ) { DialogBoxParamA(hInstance, (LPCSTR)0x67, hWnd, DialogFunc, 0); result = 0; } else { if ( (_WORD)wParam == 105 ) { DestroyWindow(hWnd); result = 0; } else { result = DefWindowProcA(hWnd, 0x111u, wParam, lParam); } } break; default: result = DefWindowProcA(hWnd, Msg, wParam, lParam); break; } return result; } 短路评估

这是一个大函数的节选,用来说明短路评估。复杂的事情发生在长函数中,有反编译器以人性化的方式来表示事情是非常方便的。请注意,散落在地址空间的代码是如何在两个if语句中简洁地显示出来的。

汇编代码 loc_804BCC7: ; CODE XREF: sub_804BB10+A42j mov [esp+28h+var_24], offset aUnzip ; "unzip" xor eax, eax test esi, esi setnz al mov edx, 1 mov ds:dword_804FBAC, edx lea eax, [eax+eax+1] mov ds:dword_804F780, eax mov eax, ds:dword_804FFD4 mov [esp+28h+var_28], eax call _strstr test eax, eax jz loc_804C4F1 loc_804BCFF: ; CODE XREF: sub_804BB10+9F8j mov eax, 2 mov ds:dword_804FBAC, eax loc_804BD09: ; CODE XREF: sub_804BB10+9FEj mov [esp+28h+var_24], offset aZ2cat ; "z2cat" mov eax, ds:dword_804FFD4 mov [esp+28h+var_28], eax call _strstr test eax, eax jz loc_804C495 loc_804BD26: ; CODE XREF: sub_804BB10+99Cj ; sub_804BB10+9B9j ... mov eax, 2 mov ds:dword_804FBAC, eax xor eax, eax test esi, esi setnz al inc eax mov ds:dword_804F780, eax .............................. SKIP ............................ loc_804C495: ; CODE XREF: sub_804BB10+210j mov [esp+28h+var_24], offset aZ2cat_0 ; "Z2CAT" mov eax, ds:dword_804FFD4 mov [esp+28h+var_28], eax call _strstr test eax, eax jnz loc_804BD26 mov [esp+28h+var_24], offset aZcat ; "zcat" mov eax, ds:dword_804FFD4 mov [esp+28h+var_28], eax call _strstr test eax, eax jnz loc_804BD26 mov [esp+28h+var_24], offset aZcat_0 ; "ZCAT" mov eax, ds:dword_804FFD4 mov [esp+28h+var_28], eax call _strstr test eax, eax jnz loc_804BD26 jmp loc_804BD3D ; --------------------------------------------------------------------------- loc_804C4F1: ; CODE XREF: sub_804BB10+1E9j mov [esp+28h+var_24], offset aUnzip_0 ; "UNZIP" mov eax, ds:dword_804FFD4 mov [esp+28h+var_28], eax call _strstr test eax, eax jnz loc_804BCFF jmp loc_804BD09 伪代码 dword_804F780 = 2 * (v9 != 0) + 1; if ( strstr(dword_804FFD4, "unzip") || strstr(dword_804FFD4, "UNZIP") ) dword_804FBAC = 2; if ( strstr(dword_804FFD4, "z2cat") || strstr(dword_804FFD4, "Z2CAT") || strstr(dword_804FFD4, "zcat") || strstr(dword_804FFD4, "ZCAT") ) { dword_804FBAC = 2; dword_804F780 = (v9 != 0) + 1; } 内联字符串操作

反编译器尝试识别经常内联的字符串函数,例如strcmp,strchr,strlen等。在此代码段中,已识别出对 strlen code>函数的调用。

汇编代码 mov eax, [esp+argc] sub esp, 8 push ebx push ebp push esi lea ecx, ds:0Ch[eax*4] push edi push ecx ; unsigned int call @Z ; operator new(uint) mov edx, [esp+1Ch+argv] mov ebp, eax or ecx, 0FFFFFFFFh xor eax, eax mov esi, [edx] add esp, 4 mov edi, esi repne scasb not ecx dec ecx cmp ecx, 4 jl short loc_401064 cmp byte ptr [ecx+esi-4], '.' jnz short loc_401064 mov al, [ecx+esi-3] cmp al, 'e' jz short loc_401047 cmp al, 'E' jnz short loc_401064 loc_401047: ; CODE XREF: _main+41j mov al, [ecx+esi-2] cmp al, 'x' jz short loc_401053 cmp al, 'X' jnz short loc_401064 loc_401053: ; CODE XREF: _main+4Dj mov al, [ecx+esi-1] cmp al, 'e' jz short loc_40105F cmp al, 'E' jnz short loc_401064 loc_40105F: ; CODE XREF: _main+59j mov byte ptr [ecx+esi-4], 0 loc_401064: ; CODE XREF: _main+32j _main+39j ... mov edi, esi or ecx, 0FFFFFFFFh xor eax, eax repne scasb not ecx add ecx, 3 push ecx ; unsigned int call @Z ; operator new(uint) mov edx, eax 伪代码 v4 = operator new(4 * argc + 12); v5 = *argv; v77 = strlen(*argv); v3 = v77 - 1; if ( (signed int)(v77 - 1) >= 4 ) { if ( v5[v3 - 4] == '.' ) { chr = v5[v3 - 3]; if ( chr == 'e' || chr == 'E' ) { v7 = v5[v3 - 2]; if ( v7 == 'x' || v7 == 'X' ) { v8 = v5[v3 - 1]; if ( v8 == 'e' || v8 == 'E' ) v5[v3 - 4] = 0; } } } } v9 = operator new(strlen(v5) + 3);


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3