首页 > 解决方案 > 反编译一个 C 函数但得到一个奇怪的代码(参数太多,双重释放)

问题描述

我是这个话题的新手,在尝试反编译 ac 函数时遇到了困难。

这是我的问题:我试图反编译隐藏在预编译项目中的给定函数的代码,所以我看不到代码,只能看到原型。

这是我的功能(我看不到代码):

int  removeFromStack(tStack *p, void *d, unsigned cantBytes);

但是我从那个特定的函数中得到了这个伪代码,它包含更多的参数:

__int64 __fastcall removeFromStack(void *a1, const void *a2, __int64 a3, size_t **a4)
{
  __int64 result; // rax
  size_t *v5; // rbx

  result = 0LL;
  v5 = *a4;
  if ( *a4 )
  {
    *a4 = (size_t *)v5[2];
    memcpy(a1, a2, *v5);
    free(a1);
    free(a1);
    result = 1LL;
  }
  return result;
}

为什么会这样?

我真的不明白为什么


编辑:

public removeFromStack
removeFromStack proc near
push    rbx
sub     rsp, 20h
xor     eax, eax
mov     rbx, [rcx]
test    rbx, rbx
mov     r9, rdx
jz      short loc_525

mov     rax, [rbx+10h]
cmp     [rbx+8], r8d
cmovbe  r8d, [rbx+8]
mov     [rcx], rax
mov     rdx, [rbx]      ; Size
mov     rcx, r9
mov     r8d, r8d
call    memcpy
mov     rcx, [rbx]
call    free
mov     rcx, rbx
call    free
mov     eax, 1

loc_525:
add     rsp, 20h
pop     rbx
retn
removeFromStack endp


编辑#2

图片

我使用了相同的项目,但使用的是 32 位版本而不是 x64 版本,现在我得到了这个:

int __cdecl removeFromStack(int a1, void *a2, int a3)
{
  int result; // eax
  int Size; // edx
  int Block; // ebx

  result = 0;
  Size = a3;
  Block = *(_DWORD *)a1;
  if ( *(_DWORD *)a1 )
  {
    if ( *(_DWORD *)(Block + 4) <= (unsigned int)a3 )
      Size = *(_DWORD *)(Block + 4);
    *(_DWORD *)a1 = *(_DWORD *)(Block + 8);
    memcpy(a2, *(const void **)Block, Size);
    free(*(void **)Block);
    free((void *)Block);
    result = 1;
  }
  return result;
}

; int __cdecl removeFromStack(int, void *, int)
public _removeFromStack
_removeFromStack proc near

Block= dword ptr -1Ch
Src= dword ptr -18h
Size= dword ptr -14h
arg_0= dword ptr  4
arg_4= dword ptr  8
arg_8= dword ptr  0Ch

push    ebx
xor     eax, eax
sub     esp, 18h
mov     ecx, [esp+1Ch+arg_0]
mov     edx, [esp+1Ch+arg_8]
mov     ebx, [ecx]
test    ebx, ebx
jz      short loc_53D

mov     eax, [ebx+8]
cmp     [ebx+4], edx
cmovbe  edx, [ebx+4]
mov     [ecx], eax
mov     eax, [ebx]
mov     [esp+1Ch+Size], edx ; Size
mov     [esp+1Ch+Src], eax ; Src
mov     eax, [esp+1Ch+arg_4]
mov     [esp+1Ch+Block], eax ; void *
call    _memcpy
mov     eax, [ebx]
mov     [esp+1Ch+Block], eax ; Block
call    _free
mov     [esp+1Ch+Block], ebx ; Block
call    _free
mov     eax, 1

loc_53D:
add     esp, 18h
pop     ebx
retn
_removeFromStack endp

标签: cx86-64decompilingcalling-conventionida

解决方案


看起来您正在反编译,就好像这是 x86-64 System V 调用约定(RCX 中的第 4 个参数,a1=RDI,a2=RSI,a3=RDX),
但实际上它是 Windows x64(RCX 中的第一个参数,然后RDX,R8,R9)。
@NateEldredge 在评论中对此的猜测是正确的。

这就解释了为什么将 args 传递给free(实际上是*first_argthen first_arg)是错误的,以及为什么它发明了未使用的虚拟 args a1..3。好吧,实际上它认为 a1 和 a2(RDI 和 RSI)被原封不动地传递给 memcpy。然后释放两次,因为我猜它假设 RDI 没有改变,即使在 memcpy 返回后没有再次设置它。编译器当然不会生成依赖于被破坏寄存器的值的代码,所以这应该是对 IDA 的一个提示,即这段代码没有使用它假设的调用约定。

所以告诉 IDA 你的代码来自哪里(Windows),这样它就知道要假设什么调用约定。(我不知道 IDA,但我可以从 asm 和 C 中看出这是问题所在。)


推荐阅读