反编译一个 C 函数但得到一个奇怪的代码参数太多,双重释放

问题描述

我是这个主题的新手,在尝试反编译 c 函数时遇到了问题。

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

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

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

image

我使用了相同的项目,但使用的是 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

解决方法

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

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

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