TIPs | 理解IDA中移位指针的使用

admin 2023年9月27日00:20:20评论42 views字数 3307阅读11分1秒阅读模式

移位指针系C中常用的特征,IDA在反编译中借鉴使用了其原理来表示具有相关偏移特征的指针。

假设源码中存在下面结构:

struct mystruct{ char buf[16]; int dummy; int value;            // <- myptr 指向这 double fval;};

那么移位指针定义如下:

int *__shifted(mystruct,20) myptr;

myptr 是指向 int 的指针,如果我们将其递减 20 个字节,则最终得到 mystruct* 。事实上,偏移值不限于结构体中,甚至可以是负数。此外,“父”类型不必是结构体,而是可以是除 void 之外的任何类型。这在某些情况下很有用。

当移位指针用来调整数据结构时候,IDA会使用 ADJ 辅助显示,这是一个伪运算符,它将指针返回到父类型(在我们的例子中为 mystruct )。例如,如下将指向fval参数:

ADJ(myptr)->fval

结构数组中的使用

编译处理结构体数组的代码时,编译器可以优化循环,以便“当前项”指针指向结构的中间而不是开头。当仅访问一小部分字段时,这种情况尤其常见。考虑这个例子:

struct mydata{  int a, b, c;  void *pad[2];  int d, e, f;  char path[260];};
int sum_c_d(struct mydata *arr, int count){ int sum=0; for (int i=0; i< count; i++) { sum+=arr[i].d+arr[i].c*43; } return sum;}

当使用 Microsoft Visual C++ x86 编译时,可以生成以下代码:

?sum_c_d@@YAHPAUmydata@@H@Z proc near
arg_0 = dword ptr 4arg_4 = dword ptr 8
mov edx, [esp+arg_4] ;count push esi xor esi, esi test edx, edx jle short loc_25 mov eax, [esp+4+arg_0] ;*arr add eax, 14h
loc_12: ; CODE XREF: sum_c_d(mydata *,int)+23↓j imul ecx, [eax-0Ch], 2Bh ; '+' add ecx, [eax] lea eax, [eax+124h] add esi, ecx sub edx, 1 jnz short loc_12
loc_25: ; CODE XREF: sum_c_d(mydata *,int)+9↑j mov eax, esi pop esi retn

即使添加并指定了正确的类型,初始反编译看起来也很奇怪:

int __cdecl sum_c_d(struct mydata *arr, int count){  int v2; // edx  int v3; // esi  int *p_d; // eax  int v5; // ecx
v2 = count; v3 = 0; if ( count <= 0 ) return v3; p_d = &arr->d; do { v5 = *p_d + 43 * *(p_d - 3); p_d += 73; v3 += v5; --v2; } while ( v2 ); return v3;}

显然,编译器决定使用指向 d 字段的指针并访问相对于它的 c 。我们怎样才能让这个看起来更好呢?

我们可以通过手动计算、检查反汇编或在伪代码中将鼠标悬停在其上来找出 d 在结构中的偏移量。

TIPs | 理解IDA中移位指针的使用

因此,我们可以将 p_d 的类型更改为 int * __shifted(mydata, 0x14) 以获得改进的伪代码:

int __cdecl sum_c_d(struct mydata *arr, int count){  int v2; // edx  int v3; // esi  int *__shifted(mydata,0x14) p_d; // eax  int v5; // ecx
v2 = count; v3 = 0; if ( count <= 0 ) return v3; p_d = &arr->d; do { v5 = ADJ(p_d)->d + 43 * ADJ(p_d)->c; p_d += 73; v3 += v5; --v2; } while ( v2 ); return v3;}

前置元数据

此技术用于原始内存块需要附加一些管理信息的情况,即堆分配器、托管字符串等。作为一个具体示例,让我们考虑经典的 MFC 4.x CString 类。它使用放置在实际字符数组之前的结构:

struct CStringData{    long  nRefs;    // reference count    int   nDataLength;    // length of data (including terminator)    int   nAllocLength;   // length of allocation    // TCHAR data[nAllocLength]
TCHAR* data() // TCHAR* to managed data { return (TCHAR*)(this+1); }};

也就是逆向中总是会看到CString的字符串前面多了几个结构块。CString类本身只有一个数据成员:

class CString{public:// Constructors[...skipped]private:    LPTSTR   m_pchData;        // pointer to ref counted string data *******
// implementation helpers CStringData* GetData() const;[...skipped]};inlineCStringData*CString::GetData( ) const{ ASSERT(m_pchData != NULL); return ((CStringData*)m_pchData)-1;}

内存中的结构如下:

               ┌───────────────┐               │   nRefs       │               ├───────────────┤ CStringData   │ nDataLength   │               ├───────────────┤               │ nAllocLength  │               ├───────────────┴─────┐           ┌──►│'H','e','l','l','o',0│           │   └─────────────────────┘         ┌─┴────────┐CString  │m_pchData │         └──────────┘

下面是 CString 的析构函数在初始反编译中的样子:

void __thiscall CString::~CString(CString *this){  if ( *(_DWORD *)this - (_DWORD)off_4635E0 != 12 && InterlockedDecrement((volatile LONG *)(*(_DWORD *)this - 12)) <= 0 )    operator delete((void *)(*(_DWORD *)this - 12));}

即使在使用单个成员 *m_pszData 创建 CString 结构之后,它仍然有些令人困惑:

void __thiscall CString::~CString(CString *this){  if ( this->m_pszData - (char *)off_4635E0 != 12 && InterlockedDecrement((volatile LONG *)this->m_pszData - 3) <= 0 )    operator delete(this->m_pszData - 12);}

最后,如果我们如上所述创建 CStringDatastruct 并将 CString 成员的类型更改为:char *__shifted(CStringData,0xC) m_pszData,结构将会比较清晰:

void __thiscall CString::~CString(CString *this){  if ( ADJ(this->m_pszData)->data - (char *)off_4635E0 != 12 && InterlockedDecrement(&ADJ(this->m_pszData)->nRefs) <= 0 )    operator delete(ADJ(this->m_pszData));}

现在代码更容易理解了:如果递减的引用计数变为零,则删除 CStringData 实例。


TIPs | 理解IDA中移位指针的使用


原文始发于微信公众号(TIPFactory情报工厂):TIPs | 理解IDA中移位指针的使用

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年9月27日00:20:20
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   TIPs | 理解IDA中移位指针的使用http://cn-sec.com/archives/2071659.html

发表评论

匿名网友 填写信息