1

Тема: JavaScript: дамп ApiSet v6 (расширение WinDbg)

Пригодится всем интересующимся внутренним устройством Windows.

'use strict';

const is_kd = () => host.currentSession.Attributes.Target.IsKernelTarget;
const chunk = ([a1, a2, ...tail]) => tail.length // Windows - это LE система
           ? [[a1, a2].reverse(), ...chunk(tail)] : [[a1, a2].reverse()];
const read_struct = (arr, adr) => {
  return ((fields, values) => { // размерность полей одинакова, поэтому
    return fields.reduce((hash, key, index) => { // не используем синтетики
      hash[key] = values[index];
      return hash;
    }, {});
  })(arr, host.memory.readMemoryValues(adr, arr.length, 4));
}
const read_string = (adr, len) => {
  return String.fromCharCode(...chunk( // разбор символов Unicode
    host.memory.readMemoryValues(adr, len, 1)
  ).map(c => c.join('').padStart(4, '0')));
}

class ApiSetEnrty {
  constructor(entry, sealed, points) {
    this.Entry  = entry;
    this.Sealed = sealed;
    this.Points = points.join(', ');
  }
}

/*
 * .scriptload apiset.js
 * dx -g @$apiset()
 * [...] <- кликабельно в WinDbg
 * dx -c 100 -g @$apiset()
 * [...]
 * dx -c 200 -g @$apiset()
 * [...]
 * и так далее
 */
function *EnumerateApiSetEntries() {
  let peb = is_kd() ? host.createPointerObject(
        host.currentProcess.KernelObject.Peb.address, 'nt', '_PEB *'
      ) : host.namespace.Debugger.State.PseudoRegisters.General.peb,
      pnt = peb.ApiSetMap.address,
      api_set_namespace = read_struct([
        'Version', 'Size', 'Flags', 'Count', 'EntryOffset', 'HashOffset', 'HashFactor'
      ], pnt);

  for (let pasne = pnt.add(api_set_namespace.EntryOffset),
           ofs_v = 0, i = 0; i < api_set_namespace.Count; ofs_v += 24, i++) {
    let entry = read_struct([ // API_SET_NAMESPACE_ENTRY
      'Flags', 'NameOffset', 'NameLength', 'HashedLength', 'ValueOffset', 'ValueCount'
    ], pasne.add(ofs_v));

    let name = read_string(pnt.add(entry.NameOffset), entry.NameLength),
        stat = 0 !== (entry.Flags & 1) ? 'true' : 'false',
        vals = [];
    for (let pasve = pnt.add(entry.ValueOffset),
             ofs_e = 0, j = 0; j < entry.ValueCount; ofs_e += 20, j++) {
      let value = read_struct([
        'Flags', 'NameOffset', 'NameLength', 'ValueOffset', 'ValueLength'
      ], pasve.add(ofs_e));
      vals.push(read_string(pnt.add(value.ValueOffset), value.ValueLength));
    }

    yield new ApiSetEnrty(name, stat, vals);
  }
}

function initializeScript() {
  return [new host.functionAlias(EnumerateApiSetEntries, 'apiset')]
}

Хотя в многострочном комментарии кода описано что к чему, добавлю: может потребоваться ввести полный путь до сценария (слэши
должны экранироваться). Например:

C:\dbg> cdb notepad
...
0:000> .scriptload E:\\dbg\\extensions\\apiset.js
JavaScript script successfully loaded from 'E:\dbg\extensions\apiset.js'
0:000> dx -g @$apiset()
============================================================================================================================
=                          = Entry                                             = Sealed  = Points                          =
============================================================================================================================
= [0x0] : [object Object]  - api-ms-onecoreuap-print-render-l1-1-0             - true    - printrenderapihost.dll          =
= [0x1] : [object Object]  - api-ms-win-appmodel-identity-l1-2-0               - true    - kernel.appcore.dll              =
= [0x2] : [object Object]  - api-ms-win-appmodel-runtime-internal-l1-1-7       - true    - kernel.appcore.dll              =
...