IDA 技巧(76) 快速重命名

admin 2024年11月22日15:38:29评论1 views字数 4484阅读14分56秒阅读模式

在IDA 7.6中新增的功能之一是反编译器中变量的自动重命名。

IDA 技巧(76) 快速重命名

Image

与PIT不同,它不仅限于堆栈变量,还处理存储在寄存器中的变量,不仅是调用,还包括赋值和其他一些表达式。它还尝试解释包含动词(如get、make、fetch、query等)的函数名,并相应地重命名分配的结果。

手动触发重命名

为了应对自动重命名失败或不足的情况,反编译器还支持一个名为“快速重命名”的手动操作,默认快捷键为ShiftN。它可以用于在赋值和其他表达式中传播名称。通常,它只重命名用户未明确命名的虚拟变量(如v1v2等)。以下是该操作使用的一些不完整规则列表:

  • 根据赋值中相对变量的名称:v1 = myvar:重命名v1 -> myvar1
  • 根据比较中相对变量的名称:offset < v1:重命名v1 -> offset1
  • 作为指向命名良好的变量的指针:v1 = &Value:重命名v1 -> p_Value
  • 根据表达式中的结构字段:v1 = x.Next:重命名v1 -> Next
  • 作为指向结构字段的指针:v1 = &x.left:重命名v1 -> p_left
  • 根据调用中形式参数的名称:close(v1):重命名v1 -> fd
  • 根据被调用函数的名称:v1=create_table():重命名v1 -> table
  • 根据被调用函数的返回类型:v1 = strchr(s, '1'):重命名v1 -> str
  • 根据字符串常量:v1 = fopen("/etc/fstab", "r"):重命名v1 -> etc_fstab
  • 根据变量类型:error_t v1:重命名v1 -> error
  • 结果变量的标准名称:return v1:如果当前函数返回布尔值,重命名v1 -> ok

示例:Windows驱动程序

我们将检查Process Hacker使用的驱动程序,以执行需要内核模式访问的操作。打开kprocesshacker.sys时,IDA会自动将已知的函数原型应用于DriverEntry入口点,并加载内核模式类型库,因此默认反编译已经相当不错:

NTSTATUS __stdcall DriverEntry(_DRIVER_OBJECT *DriverObject, PUNICODE_STRING RegistryPath)
{
  NTSTATUS result; // eax
  NTSTATUS v5; // r11d
  PDEVICE_OBJECT v6; // rax
  struct _UNICODE_STRING DestinationString; // [rsp+40h] [rbp-18h] BYREF
  PDEVICE_OBJECT DeviceObject; // [rsp+60h] [rbp+8h] BYREF

  qword_132C0 = (__int64)DriverObject;
  VersionInformation.dwOSVersionInfoSize = 284;
  result = RtlGetVersion(&VersionInformation);
  if ( result >= 0 )
  {
    result = sub_15100(RegistryPath);
    if ( result >= 0 )
    {
      RtlInitUnicodeString(&DestinationString, L"\Device\KProcessHacker3");
      result = IoCreateDevice(DriverObject, 0, &DestinationString, 0x22u, 0x100u, 0, &DeviceObject);
      v5 = result;
      if ( result >= 0 )
      {
        v6 = DeviceObject;
        DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)&sub_11008;
        qword_132D0 = (__int64)v6;
        DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)&sub_1114C;
        DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)&sub_11198;
        DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_150EC;
        v6->Flags &= ~0x80u;
        return v5;
      }
    }
  }
  return result;
}

然而,为了理解它,我们需要进行一些更改。MajorFunction数组的索引是所谓的IRP主要功能代码,它们有以IRP_MJ_开头的符号名称。因此,我们可以应用枚举操作(M快捷键)将数字转换为类型库中可用的相应符号常量。

IDA 技巧(76) 快速重命名

Image

之后,我们可以重命名相应的例程,使伪代码看起来非常类似于标准DriverEntry:

IDA 技巧(76) 快速重命名

Image

为了去除强制转换,使用“设置项目类型”操作(Y快捷键)为调度例程设置正确的原型。我们可以为所有三个例程使用相同的原型字符串:

NTSTATUS Dispatch(PDEVICE_OBJECT Device, PIRP Irp)

这之所以有效,是因为函数名不被视为函数原型的一部分,并且被IDA忽略。对于卸载函数,原型不同:

void Unload(PDRIVER_OBJECT Driver)

设置原型后,不再有强制转换:

IDA 技巧(76) 快速重命名

Image

现在我们可以进入KhDispatchDeviceControl来研究它是如何工作的。由于预设的原型,初始伪代码乍一看是合理的:

NTSTATUS __stdcall KhDispatchDeviceControl(PDEVICE_OBJECT Device, PIRP Irp)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  v13 = Irp;
  CurrentStackLocation = Irp->Tail.Overlay.CurrentStackLocation;
  FsContext = CurrentStackLocation->FileObject->FsContext;
  Parameters = CurrentStackLocation->Parameters.CreatePipe.Parameters;
  Options = CurrentStackLocation->Parameters.Create.Options;
  LowPart = CurrentStackLocation->Parameters.Read.ByteOffset.LowPart;
  AccessMode = Irp->RequestorMode;
  if ( !FsContext )
  {
    v9 = -1073741595;
    goto LABEL_105;
  }
  if ( LowPart != -1718018045
    && LowPart != -1718018041
    && (dword_132CC == 2 || dword_132CC == 3)
    && (*FsContext & 2) == 0 )

但仔细检查后,一些奇怪之处变得明显。_IO_STACK_LOCATION结构的Parameters成员是一个联合体,包含请求特定的参数。由于信息不足,反编译器选择了第一个匹配的成员,但它们对于我们正在处理的请求没有意义。对于IRP_MJ_DEVICE_CONTROL,应使用DeviceIoControl结构。

IDA 技巧(76) 快速重命名

Image

因此,我们可以使用“选择联合字段”操作(AltY快捷键)在对CurrentStackLocation->Parameters的三个引用上选择DeviceIoControl,以查看实际使用了哪些参数。

IDA 技巧(76) 快速重命名

Image

引用已更改,但变量名称和类型保持不变。在这种情况下,我们可以通过在赋值上使用快速重命名(ShiftN)来更新名称。

IDA 技巧(76) 快速重命名

Image

为了去除强制转换,我们可以手动将Type3InputBuffer变量类型更改为void*,或者简单地刷新反编译(F5)。这会导致反编译器重新运行类型推导算法并更新自动类型化变量的类型。

现在伪代码更接近于实际情况。特别是,我们可以看到第一个比较正在检查IoControlCode是否与一些预期值匹配,这比原始的LowPart更有意义。

IDA 技巧(76) 快速重命名

Image

其他用途

当由于名称冲突导致自动重命名失败时,快速重命名可能会很有用。例如,如果我们回到DriverEntry,可以看到DeviceObject被复制到一个临时变量v6

v6 = DeviceObject;
DriverObject->MajorFunction[IRP_MJ_CREATE] = KhDispatchCreate;
qword_132D0 = (__int64)v6;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = KhDispatchClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KhDispatchDeviceControl;
DriverObject->DriverUnload = KhUnload;
v6->Flags &= ~0x80u;

我们可以手动重命名v6,或者简单地在赋值上按ShiftN,反编译器将重用名称并添加数字后缀以解决冲突:

DeviceObject1 = DeviceObject;
DriverObject->MajorFunction[IRP_MJ_CREATE] = KhDispatchCreate;
qword_132D0 = (__int64)DeviceObject1;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = KhDispatchClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KhDispatchDeviceControl;
DriverObject->DriverUnload = KhUnload;
DeviceObject1->Flags &= ~0x80u;

更多文章

立即关注【二进制磨剑】公众号

👉👉👉【IDA 技巧合集】👈👈👈
👉👉👉【Github 安全项目合集】👈👈👈
学习零基础学习 IDA 逆向
【课程完结!内容揭秘!】7 天打造 IDA 9.0 大师:从零基础到逆向精英
知识星球🪐

IDA 技巧(76) 快速重命名

原文始发于微信公众号(二进制磨剑):IDA 技巧(76) 快速重命名

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月22日15:38:29
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   IDA 技巧(76) 快速重命名https://cn-sec.com/archives/3424899.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息