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; }