跳转至

遍历 IAT

一旦你知道 IAT 仍然完好,就可以开始遍历它,这就是 IAT 钩子开始变得难看的地方。IAT 是称为导入描述符的结构数组。每个依赖有一个导入描述符,每个导入描述符指向称为 thunk 的结构数组,每个 thunk 表示从依赖导入的一个函数。

幸运的是,Windows API 通过 IMAGE_IMPORT_DESCRIP TOR 和 IMAGE_THUNK_DATA 结构分别暴露了导入描述符和 thunk。有预定义的结构省去了自己创建,但并不会让遍历 IAT 的代码更好看。

要明白我的意思,看看清单 8-7,它建立在清单 8-4 到 8-6 之上。

auto impDesc =
    pointMemory<IMAGE_IMPORT_DESCRIPTOR>( 
baseAddr + IAT.VirtualAddress);
 while (impDesc->FirstThunk) {
  auto thunkData =
        pointMemory<IMAGE_THUNK_DATA>(baseAddr + 
    int n = 0;
  while (thunkData->u1.Function) {
        // 钩子发生在这里
        n++;
        thunkData++;
    }
    impDesc++;
}

清单 8-7:遍历 IAT 以找到函数

记住导入描述符相对于 PE 头起始处存储,这段代码把模块基址加到 IAT 目录头中找到的虚拟地址①,创建一个指向模块第一个导入描述符的指针 impDesc。

导入描述符存储在顺序数组中,FirstThunk 成员设为 NULL 的描述符表示数组结尾。知道这一点,代码使用 while 循环②,一直持续到 impDesc->FirstThunk 为 NULL,每次迭代执行 impDesc++ 递增描述符。

对每个导入描述符,代码创建一个指向描述符内第一个 thunk 的指针 thunkData③。使用熟悉的循环,代码遍历 thunk④,直到找到 Function 成员设为 NULL 的 thunk。循环还用整数 n 跟踪当前 thunk 索引,因为该索引在放置钩子时很重要。