On Sat, 1 Mar 2025, Johannes Schindelin wrote:

> Note: In the long run, we may very well want to follow the insightful
> suggestion by a helpful Windows kernel engineer who pointed out that it
> may be less fragile to implement kind of a disassembler that has a
> better chance to adapt to the ever-changing code of
> `ntdll!RtlpReferenceCurrentDirectory` by skipping uninteresting
> instructions such as `mov %rsp,%rax`, `mov %rbx,0x20(%rax)`, `push %rsi`
> `sub $0x70,%rsp`, etc, and focuses on finding the `lea`, `call
> ntdll!RtlEnterCriticalSection` and `mov ..., rbx` instructions, much
> like it was prototyped out for ARM64 at
> https://gist.github.com/jeremyd2019/aa167df0a0ae422fa6ebaea5b60c80c9

Since you kind of asked, here's a proof-of-concept that uses udis86 (I
left a whole bunch of pointer<->integer warnings since this is a PoC).
Tested on windows 11 and 8:

LPVOID find_fast_cwd_pointer_on_x64 ()
{
  ud_t ud_obj;
  ud_init (&ud_obj);
  ud_set_mode (&ud_obj, 64);
  LPCVOID proc = GetProcAddress(GetModuleHandle("ntdll"), 
"RtlGetCurrentDirectory_U");
  LPCVOID start = proc;
  printf("%p\n", proc);
  /* no idea for size */
  ud_set_input_buffer (&ud_obj, proc, 80);
  ud_set_pc (&ud_obj, proc);

  /* find the call to RtlpReferenceCurrentDirectory, and get its address */
  while (ud_disassemble (&ud_obj))
    {
      if (ud_insn_mnemonic (&ud_obj) == UD_Icall)
        {
          const ud_operand_t * operand = ud_insn_opr (&ud_obj, 0);
          if (operand->type == UD_OP_JIMM && operand->size == 32)
            {
              proc = ud_insn_off (&ud_obj) + ud_insn_len (&ud_obj) +
                      operand->lval.sdword;
              break;
            }
        }
    }
  printf("%p\n", proc);
  if (proc == start)
    return NULL;

  start = proc;
  /* no idea for size */
  ud_set_input_buffer (&ud_obj, proc, 160);
  ud_set_pc (&ud_obj, proc);

  LPVOID critsec = NULL;

  while (ud_disassemble (&ud_obj))
    {
      if (ud_insn_mnemonic (&ud_obj) == UD_Ilea)
        {
          /* this seems to follow intel syntax, in that operand 0 is the
             register and 1 is the memory refernece */
          const ud_operand_t * operand = ud_insn_opr (&ud_obj, 1);
          if (operand->type == UD_OP_MEM && operand->base == UD_R_RIP &&
              operand->index == UD_NONE && operand->scale == 0 &&
              operand->offset == 32)
            {
              critsec = ud_insn_off (&ud_obj) + ud_insn_len (&ud_obj) +
                operand->lval.sdword;
              break;
            }
        }
    }
  if (critsec != NtCurrentTeb ()->Peb->FastPebLock)
    return NULL;

  /* find the call to RtlEnterCriticalSection */
  proc = GetProcAddress(GetModuleHandle("ntdll"), "RtlEnterCriticalSection");
  while (ud_disassemble (&ud_obj))
    {
      enum ud_mnemonic_code insn = ud_insn_mnemonic (&ud_obj);
      if (insn == UD_Icall)
        {
          const ud_operand_t * operand = ud_insn_opr (&ud_obj, 0);
          if (operand->type == UD_OP_JIMM && operand->size == 32)
            {
              if (proc != ud_insn_off (&ud_obj) + ud_insn_len (&ud_obj) +
                            operand->lval.sdword)
                return NULL;
              break;
            }
        }
      else if (insn == UD_Ibtr && ud_obj.pfx_lock)
        {
          /* for Windows 8 */
          const ud_operand_t * operand = ud_insn_opr (&ud_obj, 0);
          if (operand->type == UD_OP_MEM && operand->base == UD_R_RIP &&
              operand->index == UD_NONE && operand->scale == 0 &&
              operand->offset == 32 && critsec == ud_insn_off (&ud_obj) +
                ud_insn_len (&ud_obj) + operand->lval.sdword -
                offsetof (RTL_CRITICAL_SECTION, LockCount))
            break;
        }
    }
  LPVOID RtlpCurDirRef = NULL;
  /* probably the next instruction is the mov qword ptr */
  while (ud_disassemble (&ud_obj))
    {
      if (ud_insn_mnemonic (&ud_obj) == UD_Imov)
        {
          const ud_operand_t * operand = ud_insn_opr (&ud_obj, 1);
          if (operand->type == UD_OP_MEM && operand->base == UD_R_RIP &&
              operand->index == UD_NONE && operand->scale == 0 &&
              operand->offset == 32 && operand->size == 64)
            {
              RtlpCurDirRef = ud_insn_off (&ud_obj) + ud_insn_len (&ud_obj) +
                              operand->lval.sdword;
              break;
            }
        }
    }

  printf("%p -> %p\n", ud_insn_off (&ud_obj), RtlpCurDirRef);

  return RtlpCurDirRef;
}

Reply via email to