Problem 2: evalme
Overview
Section titled “Overview”Field: LowUIR lifting, concrete evaluation
evalme is a stripped binary that computes the accepted password at runtime,
stores it in a local buffer, and compares the command-line argument against that
generated value.
Use the evalme/ directory from
tutorial.zip.
It has this layout:
evalme/ Makefile src/ evalme.c bin/ evalme evalme-arm evalme-mips strip/ evalme evalme-arm evalme-mipsThe stripped challenge binary is at strip/evalme. The unstripped rebuild is
at bin/evalme. The source file and unstripped binaries are provided for
verification; use the stripped binaries for the exercise.
Recover the password accepted by evalme.
Problem Description
Section titled “Problem Description”evalme expects one argv token. It computes an expected password at runtime,
stores it in a local buffer, and compares argv against that buffer.
The program also contains an anti-debugging check before the comparison. The anti-debugging check exits immediately when it detects tracing. You can still bypass it by patching the binary, skipping the check, or neutralizing it with a debugger. However, the intended goal of this tutorial is to use the B2R2 API to concretely execute the password-generation code.
Suggested Workflow
Section titled “Suggested Workflow”- Locate the function that generates the expected password.
- Identify the first-argument convention. On System V AMD64, the first
argument is passed in
RDI. - Prepare a writable output buffer in the concrete evaluation state.
- Execute the password-generation function until it returns, following its internal helper calls.
- Read the generated C string from the output buffer.
- Verify the recovered password by running the binary normally.
Starter Code
Section titled “Starter Code”Save this as evalme.fsx in the extracted evalme/ directory:
#r "nuget: B2R2.MiddleEnd.ConcEval"
open System.IOopen B2R2open B2R2.FrontEndopen B2R2.MiddleEnd.ConcEval
let binaryPath = Path.Combine(__SOURCE_DIRECTORY__, "strip", "evalme")
let hdl = BinHandle binaryPathlet executor = ConcExecutor hdl
let startAddr =let endAddr =
let state =let accessor =
// TODO: initialize the stack and frame pointer.
let buf =
// TODO: pass buf as argument 0.
let result = executor.Run(startAddr, state, StopAtAddress endAddr)
if result.IsStoppedAtAddress endAddr then let password = printfn "Recovered password: %s" passwordelse printfn "Execution stopped before reaching 0x%x" endAddrRecommended References
Section titled “Recommended References”BinHandlefor loading the target binary.ConcExecutorfor creating concrete states and running fromstartAddr.StopAtAddressandIsStoppedAtAddressfor stopping atendAddr.ConcStateAccessorand shared state accessor members for stack setup,AllocateStackBuffer,SetArgument,SetRegister, andReadCString.ELFBinFile.GlobalPointerfor the optional MIPS GP setup in the solution.
Run the script from the extracted evalme/ directory:
dotnet fsi evalme.fsxOn Unix-like systems, if the extracted binary is not executable, run:
chmod +x strip/evalmeSolution
Section titled “Solution”Show solution
Unified solution script. The x86-64 version is active by default. To run ARM or MIPS, comment out the x86-64 lines and uncomment the matching architecture block.
#r "nuget: B2R2.MiddleEnd.ConcEval"// MIPS: for global pointer set// #r "nuget: B2R2.FrontEnd.MIPS"
open System.IOopen B2R2open B2R2.FrontEnd// ARM: uncomment when using the ARM32 frontend.// open B2R2.FrontEnd.ARM32// MIPS: uncomment for ELFBinFile.GlobalPointer.// open B2R2.FrontEnd.BinFileopen B2R2.MiddleEnd.ConcEval
let stackTop = 0x7fffe000UL
// x86-64let binaryPath = Path.Combine(__SOURCE_DIRECTORY__, "strip", "evalme")let startAddr = 0x40142cULlet endAddr = 0x401507UL
// ARM:// let binaryPath =// Path.Combine(__SOURCE_DIRECTORY__, "strip", "evalme-arm")// let startAddr = 0x00010a00UL// let endAddr = 0x10b38UL
// MIPS:// let binaryPath =// Path.Combine(__SOURCE_DIRECTORY__, "strip", "evalme-mips")// let startAddr = 0x400bfcUL// let endAddr = 0x400d74UL
let hdl = BinHandle binaryPath
let executor = ConcExecutor hdllet state = executor.CreateState()let accessor = ConcStateAccessor(hdl, state)
accessor.InitializeStack stackTopaccessor.InitializeFramePointer()
// MIPS only:// match hdl.File with// | :? ELFBinFile as elf ->// match elf.GlobalPointer with// | Some gp -> accessor.SetRegister("GP", accessor.WordValue gp)// | None -> ()// | _ -> ()
let buf = accessor.AllocateStackBuffer 32
accessor.SetArgument(0, accessor.WordValue buf)
// x86-64: You can use RDI instead.// accessor.SetRegister("RDI", accessor.WordValue buf)
// ARM: You can use R0 instead.// accessor.SetRegister("R0", accessor.WordValue buf)
// MIPS O32 uses the caller stack area above the callee frame for argument// spills, so keep extra scratch space between SP and our output buffer.// accessor.AllocateStackBuffer 128 |> ignore// MIPS: You can use A0 instead.// accessor.SetRegister("A0", accessor.WordValue buf)// accessor.SetRegister("NPC", accessor.WordValue(startAddr + 4UL))
let result = executor.Run(startAddr, state, StopAtAddress endAddr)
if result.IsStoppedAtAddress endAddr then let password = accessor.ReadCString(buf, 32) printfn "Recovered password: %s" passwordScript output:
Recovered password: b2r2-concrete!Binary run:
./strip/evalme b2r2-concrete!accepted