如何简化将自身作为参数引用的函数? (这是啥意思)

Posted

技术标签:

【中文标题】如何简化将自身作为参数引用的函数? (这是啥意思)【英文标题】:How to simplify a function referencing itself as argument ? (and what does this mean)如何简化将自身作为参数引用的函数? (这是什么意思) 【发布时间】:2021-04-28 23:24:35 【问题描述】:

我经常遇到一个函数调用自己(在伪代码中,由 IDA 生成),例如:

result = (**(__int64 (__fastcall ***)(volatile signed __int32 *))lambda)(lambda);
    既然反汇编是call qword ptr [rax],那我把c中的伪代码翻译成result = lambda();不就可以了吗? 为什么在伪代码中函数将自身作为参数? 当有诸如lambda + 8i64(即call qword ptr [rax+8])之类的呼叫时会发生什么?

这里有一个更完整的上下文:

__int64 __fastcall CR_maybeParseWithLambda(_QWORD *a1, __int64 newPtr, __int64 positionOrCounter)

  volatile signed __int32 *lambda; // rdi
  __int64 result; // rax

  lambda = (volatile signed __int32 *)a1[1];
  if ( lambda )
  
    result = (unsigned int)_InterlockedExchangeAdd(lambda + 2, 0xFFFFFFFF);
    if ( (_DWORD)result == 1 )
    
      result = (**(__int64 (__fastcall ***)(volatile signed __int32 *))lambda)(lambda);
      if ( _InterlockedExchangeAdd(lambda + 3, 0xFFFFFFFF) == 1 )
        result = (*(__int64 (__fastcall **)(volatile signed __int32 *))(*(_QWORD *)lambda + 8i64))(lambda);
      a1[1] = positionOrCounter;
      *a1 = newPtr;
    
    else
    
      a1[1] = positionOrCounter;
      *a1 = newPtr;
    
  
  else
  
    a1[1] = positionOrCounter;
    *a1 = newPtr;
  
  return result;

反汇编,同样来自 IDA:

.text:0000000180005F70 ; __int64 __fastcall CR_maybeParseWithLambda(_QWORD *a1, __int64 newPtr, __int64 positionOrCounter)
.text:0000000180005F70 CR_maybeParseWithLambda proc near       ; CODE XREF: sub_180005B10+10F↑p
.text:0000000180005F70                                         ; sub_180005B10+14A↑p ...
.text:0000000180005F70
.text:0000000180005F70 arg_0           = qword ptr  8
.text:0000000180005F70 arg_8           = qword ptr  10h
.text:0000000180005F70 arg_10          = qword ptr  18h
.text:0000000180005F70 arg_18          = qword ptr  20h
.text:0000000180005F70
.text:0000000180005F70                 mov     [rsp+arg_8], rbx
.text:0000000180005F75                 mov     [rsp+arg_10], rbp
.text:0000000180005F7A                 mov     [rsp+arg_18], rsi
.text:0000000180005F7F                 push    rdi
.text:0000000180005F80                 sub     rsp, 20h
.text:0000000180005F84                 mov     rdi, [rcx+8]
.text:0000000180005F88                 mov     rsi, r8
.text:0000000180005F8B                 mov     rbp, rdx
.text:0000000180005F8E                 mov     rbx, rcx
.text:0000000180005F91                 test    rdi, rdi
.text:0000000180005F94                 jz      short loc_180005FF3
.text:0000000180005F96
.text:0000000180005F96 loc_180005F96:                          ; DATA XREF: .rdata:0000000180401E74↓o
.text:0000000180005F96                                         ; .rdata:0000000180401E84↓o ...
.text:0000000180005F96                 mov     [rsp+28h+arg_0], r14
.text:0000000180005F9B                 or      r14d, 0FFFFFFFFh
.text:0000000180005F9F                 mov     eax, r14d
.text:0000000180005FA2                 lock xadd [rdi+8], eax
.text:0000000180005FA7                 cmp     eax, 1
.text:0000000180005FAA                 jnz     short loc_180005FEA
.text:0000000180005FAC                 mov     rax, [rdi]
.text:0000000180005FAF                 mov     rcx, rdi
.text:0000000180005FB2                 call    qword ptr [rax]
.text:0000000180005FB4                 lock xadd [rdi+0Ch], r14d
.text:0000000180005FBA                 cmp     r14d, 1
.text:0000000180005FBE                 jnz     short loc_180005FC9
.text:0000000180005FC0                 mov     rax, [rdi]
.text:0000000180005FC3                 mov     rcx, rdi
.text:0000000180005FC6                 call    qword ptr [rax+8]
.text:0000000180005FC9
.text:0000000180005FC9 loc_180005FC9:                          ; CODE XREF: CR_maybeParseWithLambda+4E↑j
.text:0000000180005FC9                 mov     [rbx+8], rsi
.text:0000000180005FCD                 mov     [rbx], rbp
.text:0000000180005FD0
.text:0000000180005FD0 loc_180005FD0:                          ; CODE XREF: CR_maybeParseWithLambda+81↓j
.text:0000000180005FD0                 mov     r14, [rsp+28h+arg_0]
.text:0000000180005FD5
.text:0000000180005FD5 loc_180005FD5:                          ; CODE XREF: CR_maybeParseWithLambda+8A↓j
.text:0000000180005FD5                                         ; DATA XREF: .pdata:0000000180483888↓o ...
.text:0000000180005FD5                 mov     rbx, [rsp+28h+arg_8]
.text:0000000180005FDA                 mov     rbp, [rsp+28h+arg_10]
.text:0000000180005FDF                 mov     rsi, [rsp+28h+arg_18]
.text:0000000180005FE4                 add     rsp, 20h
.text:0000000180005FE8                 pop     rdi
.text:0000000180005FE9                 retn

【问题讨论】:

【参考方案1】:
    既然反汇编是call qword ptr [rax],那我把c中的伪代码翻译成result = lambda();不就可以了吗?

没有。反编译器检测到传入的变量很可能是被调用函数的参数。

例如,void f()void f(int) 函数都使用单个 call 汇编命令调用,除了在后一种情况下,调用者在调用函数之前将 int 值移动到适当的寄存器。

您可以更改lambda 的类型来避免这种情况。

    为什么在伪代码中函数将自身作为参数?

非常仔细阅读汇编代码和反编译代码。 lambda 不是函数指针,要从中获取函数指针,必须取消引用两次。所以它可能是这样的(伪C++代码)

using FunctionType=int(int);
struct B
    FunctionType* functionPointer;
;
struct A
    B* b;
;
A* lambda; // the variable name is a little misleading, given this interpretation.
auto functionPointer=(*(*lambda).b);
functionPointer(lambda);

鉴于双重取消引用,B 很可能实际上是 vftable(尽管在这些情况下,该函数通常以 __thiscall 约定调用)——因此代码可以这样编写:

struct Base
    virtual void someFunction()
    virtual void otherFunction()
;
struct Base_vftableType // compiler-generated
    void (*someFunction)(Base*); // explicit (this) argument shown
    void (*otherFunction)(Base*); // explicit (this) argument shown
;
struct Derived: Base
    Base_vftableType *vftable; // compiler-generated
    void someFunction() /* ... */ 
;
Base_vftableType derived_vftable /* ... */ ; // compiler-generated vftable

Derived *a;

// the function call is something like this in pseudo-C
// (and probably how it will be displayed in IDA):
a->vftable->someFunction(a);
    当有诸如lambda + 8i64(即call qword ptr [rax+8])之类的呼叫时会发生什么?

同样,vftable 中可能有多个函数,+ 只是获取其他函数的地址。

假设 64 位函数指针,+8 将是表中的第二个函数。

另见:c++ - How to organize vtables in IDA Pro? - Reverse Engineering Stack Exchange

【讨论】:

更多参见:可能重复***.com/questions/11000067/…

以上是关于如何简化将自身作为参数引用的函数? (这是啥意思)的主要内容,如果未能解决你的问题,请参考以下文章

将 C 函数和参数作为右值引用传递

C++中,int&&表示啥意思?和int&有啥不同?

utility 在计算机中是啥意思?

“类型 T 必须是引用类型才能将其用作参数”是啥意思?

如何使用子字符串函数,这是啥意思? [复制]

在word中,表格的计算里,那些函数分别代表啥意思?