├── .gitignore ├── Dockerfile ├── FunSeeker.sln ├── LICENSE.md ├── README.md └── src └── FunSeeker ├── BinUtil.fs ├── Cache.fs ├── Disasm.fs ├── EndbrFP.fs ├── FunSeeker.fsproj ├── Program.fs ├── Report.fs └── Tailcall.fs /.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | RUN apt-get update && apt-get upgrade -y && \ 4 | apt-get install -y wget git 5 | 6 | RUN wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb && \ 7 | dpkg -i packages-microsoft-prod.deb && \ 8 | rm packages-microsoft-prod.deb 9 | 10 | RUN apt-get update; \ 11 | apt-get install -y apt-transport-https && \ 12 | apt-get update && \ 13 | DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get install -y dotnet-sdk-6.0 14 | 15 | RUN git clone https://github.com/B2R2-org/FunSeeker.git;\ 16 | cd FunSeeker && \ 17 | dotnet build -c Release 18 | -------------------------------------------------------------------------------- /FunSeeker.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.0.32014.148 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{52511B34-7C3A-4BD6-808D-74A7AAE20801}" 6 | EndProject 7 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FunSeeker", "src\FunSeeker\FunSeeker.fsproj", "{8FC3C561-C3F1-4097-A6B9-AE05B6DE6AB3}" 8 | EndProject 9 | Global 10 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 11 | Debug|Any CPU = Debug|Any CPU 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|Any CPU = Release|Any CPU 15 | Release|x64 = Release|x64 16 | Release|x86 = Release|x86 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {8FC3C561-C3F1-4097-A6B9-AE05B6DE6AB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {8FC3C561-C3F1-4097-A6B9-AE05B6DE6AB3}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {8FC3C561-C3F1-4097-A6B9-AE05B6DE6AB3}.Debug|x64.ActiveCfg = Debug|Any CPU 22 | {8FC3C561-C3F1-4097-A6B9-AE05B6DE6AB3}.Debug|x64.Build.0 = Debug|Any CPU 23 | {8FC3C561-C3F1-4097-A6B9-AE05B6DE6AB3}.Debug|x86.ActiveCfg = Debug|Any CPU 24 | {8FC3C561-C3F1-4097-A6B9-AE05B6DE6AB3}.Debug|x86.Build.0 = Debug|Any CPU 25 | {8FC3C561-C3F1-4097-A6B9-AE05B6DE6AB3}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {8FC3C561-C3F1-4097-A6B9-AE05B6DE6AB3}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {8FC3C561-C3F1-4097-A6B9-AE05B6DE6AB3}.Release|x64.ActiveCfg = Release|Any CPU 28 | {8FC3C561-C3F1-4097-A6B9-AE05B6DE6AB3}.Release|x64.Build.0 = Release|Any CPU 29 | {8FC3C561-C3F1-4097-A6B9-AE05B6DE6AB3}.Release|x86.ActiveCfg = Release|Any CPU 30 | {8FC3C561-C3F1-4097-A6B9-AE05B6DE6AB3}.Release|x86.Build.0 = Release|Any CPU 31 | {A4E39F7B-96C2-46E2-96C3-192DC56FBD0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {A4E39F7B-96C2-46E2-96C3-192DC56FBD0D}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {A4E39F7B-96C2-46E2-96C3-192DC56FBD0D}.Debug|x64.ActiveCfg = Debug|Any CPU 34 | {A4E39F7B-96C2-46E2-96C3-192DC56FBD0D}.Debug|x64.Build.0 = Debug|Any CPU 35 | {A4E39F7B-96C2-46E2-96C3-192DC56FBD0D}.Debug|x86.ActiveCfg = Debug|Any CPU 36 | {A4E39F7B-96C2-46E2-96C3-192DC56FBD0D}.Debug|x86.Build.0 = Debug|Any CPU 37 | {A4E39F7B-96C2-46E2-96C3-192DC56FBD0D}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {A4E39F7B-96C2-46E2-96C3-192DC56FBD0D}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {A4E39F7B-96C2-46E2-96C3-192DC56FBD0D}.Release|x64.ActiveCfg = Release|Any CPU 40 | {A4E39F7B-96C2-46E2-96C3-192DC56FBD0D}.Release|x64.Build.0 = Release|Any CPU 41 | {A4E39F7B-96C2-46E2-96C3-192DC56FBD0D}.Release|x86.ActiveCfg = Release|Any CPU 42 | {A4E39F7B-96C2-46E2-96C3-192DC56FBD0D}.Release|x86.Build.0 = Release|Any CPU 43 | EndGlobalSection 44 | GlobalSection(SolutionProperties) = preSolution 45 | HideSolutionNode = FALSE 46 | EndGlobalSection 47 | GlobalSection(NestedProjects) = preSolution 48 | {8FC3C561-C3F1-4097-A6B9-AE05B6DE6AB3} = {52511B34-7C3A-4BD6-808D-74A7AAE20801} 49 | EndGlobalSection 50 | GlobalSection(ExtensibilityGlobals) = postSolution 51 | SolutionGuid = {59F91353-74FB-437A-A24E-0F66EE0074CB} 52 | EndGlobalSection 53 | EndGlobal 54 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) SoftSec Lab. @ KAIST, since 2016 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FunSeeker 2 | FunSeeker is function identification tool for Intel CET-enabled binaries. 3 | FunSeeker leverages patterns of CET-relevant instructions to find function 4 | entries. The details of the algorithm is in our [paper](https://softsec.kaist.ac.kr/~sangkilc/papers/kim-dsn2022.pdf), 5 | "How'd Security Benefit Reverse Engineers? The Implication of Intel CET on 6 | Function Identification," which will appear in DSN 2022. 7 | 8 | ### Build & Run 9 | 10 | FunSeeker is written in F#, so you need to install [.NET SDK 11 | 6.0](https://dotnet.microsoft.com/en-us/download). Also, FunSeeker includes 12 | following NuGet packages: [FSharp.Core 13 | ](https://www.nuget.org/packages/FSharp.Core/6.0.1) and 14 | [B2R2.FrontEnd.BinInterface 15 | ](https://www.nuget.org/packages/B2R2.FrontEnd.BinInterface/0.6.0-alpha) 16 | 17 | Next, you should download and build FunSeeker as follows. 18 | ``` 19 | $ git clone https://github.com/B2R2-org/FunSeeker.git 20 | $ cd FunSeeker/ 21 | $ dotnet build -c Release 22 | ``` 23 | 24 | Now, you are ready to run FunSeeker. You can run it with following command 25 | ``` 26 | $ src/FunSeeker/bin/Release/net6.0/FunSeeker [binary_path] 27 | ``` 28 | 29 | ### Docker 30 | You can use Docker image to try out FunSeeker quickly. 31 | ``` 32 | docker build --tag funseeker . 33 | docker run --rm funseeker /FunSeeker/src/FunSeeker/bin/Release/net6.0/FunSeeker [binary_path] 34 | ``` 35 | 36 | ### Dataset 37 | You can download our [dataset](https://drive.google.com/file/d/1tHv-Mws-rxy_3ErjSR6VS-0-xAKd0R0C/view?usp=sharing) which contains non-stripped binaries, stripped binaries and function list files. 38 | 39 | ### Authors 40 | This research project has been conducted by [SoftSec Lab](https://softsec.kais.ac.kr) and [CSRC](https://csrc.kaist.ac.kr) at KAIST. 41 | - Hyungseok Kim 42 | - Junoh Lee 43 | - [Soomin Kim](https://softsec.kaist.ac.kr/~soomink/) 44 | - Seungil Jung 45 | - [Sang Kil Cha](https://softsec.kaist.ac.kr/~sangkilc/) 46 | 47 | ### Citation 48 | If you plan to use FunSeeker in your own research, please consider citing our [paper](https://softsec.kaist.ac.kr/~sangkilc/papers/kim-dsn2022.pdf): 49 | ``` 50 | @INPROCEEDINGS{kim:dsn:2022, 51 | author = {Hyungseok Kim and Junoh Lee and Soomin Kim and Seungil Jung and Sang Kil Cha}, 52 | title = {How'd Security Benefit Reverse Engineers? The Implication of Intel CET on Function Identification}, 53 | booktitle = dsn, 54 | year = 2022 55 | } 56 | ``` 57 | -------------------------------------------------------------------------------- /src/FunSeeker/BinUtil.fs: -------------------------------------------------------------------------------- 1 | module FunSeeker.BinUtil 2 | 3 | open B2R2 4 | open B2R2.FrontEnd.BinInterface 5 | 6 | let createBinHandleFromPath filepath = 7 | BinHandle.Init ( 8 | ISA.DefaultISA, 9 | ArchOperationMode.NoMode, 10 | true, /// AutoDetect 11 | None, /// BaseAddress 12 | fileName = filepath) 13 | 14 | let textSectionOf (hdl: BinHandle) = 15 | hdl.FileInfo.GetTextSections () 16 | |> Seq.tryFind (fun s -> 17 | if s.Name = ".text" then true else false) 18 | 19 | let textBoundaryOf (hdl: BinHandle) = 20 | match textSectionOf hdl with 21 | | Some s -> 22 | (s.Address, s.Address + s.Size) 23 | | _ -> (0UL, 0UL) 24 | 25 | let isTextAddr (hdl: BinHandle) addr = 26 | let stext, etext = textBoundaryOf hdl 27 | (addr >= stext) && (addr < etext) 28 | 29 | let isX86 (hdl: BinHandle) = 30 | hdl.FileInfo.ISA.Arch = Arch.IntelX86 31 | -------------------------------------------------------------------------------- /src/FunSeeker/Cache.fs: -------------------------------------------------------------------------------- 1 | module FunSeeker.Cache 2 | 3 | open System.Collections.Generic 4 | open B2R2 5 | open B2R2.FrontEnd.BinInterface 6 | open B2R2.FrontEnd.BinLifter 7 | 8 | type Cache = { 9 | /// B2R2 BinHandle 10 | Handle: BinHandle 11 | 12 | /// Type (Addr, Instruction) SortedList 13 | /// Store B2R2 Instruction for superset disassembly 14 | SupersetCache: SortedList 15 | 16 | /// Type (Addr, Instruction) SortedList 17 | /// Store B2R2 Instruction for linear disassembly 18 | LinearCache: SortedList 19 | 20 | /// Type Addr set 21 | /// Set of call target address 22 | CallTargetCache: HashSet 23 | 24 | /// Type Addr set 25 | /// Set of jump instruction address 26 | mutable JumpCache: SortedList 27 | 28 | JumpTargetRefCache: SortedList> 29 | 30 | /// Type Addr set 31 | /// Set of function entry address 32 | FunctionCache: HashSet 33 | 34 | /// Type Addr set 35 | /// Set of endbr instruction address 36 | EndbrCache: HashSet 37 | 38 | /// Type Addr set 39 | /// Set of endbr-func address 40 | ReturnTwiceCache: HashSet 41 | 42 | /// Type (Addr, int) SortedList 43 | /// Store function boundary in exception table 44 | mutable ExceptionCache: SortedList 45 | 46 | mutable NumException: int 47 | mutable NumReturnTwice: int 48 | } 49 | 50 | module Cache = 51 | let initCache hdl = { 52 | Handle = hdl 53 | SupersetCache = SortedList () 54 | LinearCache = SortedList () 55 | CallTargetCache = HashSet () 56 | JumpCache = SortedList () 57 | JumpTargetRefCache = SortedList> () 58 | FunctionCache = HashSet () 59 | EndbrCache = HashSet () 60 | ReturnTwiceCache = HashSet () 61 | ExceptionCache = SortedList () 62 | NumException = 0 63 | NumReturnTwice = 0 64 | } 65 | 66 | let setSupersetCache cache (addr: Addr) (inst: Instruction) = 67 | if cache.SupersetCache.ContainsKey (addr) then () 68 | else cache.SupersetCache.[addr] <- inst 69 | 70 | let setLinearCache cache (addr: Addr) (inst: Instruction) = 71 | if cache.LinearCache.ContainsKey (addr) then () 72 | else cache.LinearCache.[addr] <- inst 73 | 74 | let initFunctionCache cache = 75 | cache.FunctionCache = HashSet () 76 | 77 | let setFunctionCache cache (addr: Addr) = 78 | cache.FunctionCache.Add(addr) |> ignore 79 | 80 | let setEndbrCache cache (addr: Addr) = 81 | cache.EndbrCache.Add(addr) |> ignore 82 | 83 | let setCallTargetCache cache (addr: Addr) = 84 | cache.CallTargetCache.Add(addr) |> ignore 85 | 86 | let setJumpCache cache (addr: Addr) (target: Addr) = 87 | cache.JumpCache.[addr] <- target 88 | 89 | let setReturnTwiceCache cache (addr: Addr) = 90 | cache.ReturnTwiceCache.Add(addr) |> ignore 91 | 92 | let setExceptionCache cache (addr: Addr) (size: int) = 93 | cache.ExceptionCache.[addr] <- size 94 | 95 | let setJumpTargetRefCache cache (target: Addr) (source: Addr) = 96 | if cache.JumpTargetRefCache.ContainsKey(target) |> not then 97 | cache.JumpTargetRefCache.[target] <- HashSet () 98 | cache.JumpTargetRefCache.[target].Add(source) |> ignore 99 | else 100 | cache.JumpTargetRefCache.[target].Add(source) |> ignore 101 | -------------------------------------------------------------------------------- /src/FunSeeker/Disasm.fs: -------------------------------------------------------------------------------- 1 | module FunSeeker.Disasm 2 | 3 | open B2R2 4 | open B2R2.FrontEnd.BinFile 5 | open B2R2.FrontEnd.BinInterface 6 | open B2R2.FrontEnd.BinLifter 7 | open B2R2.FrontEnd.BinLifter.Intel 8 | open FunSeeker.Cache 9 | open FunSeeker.BinUtil 10 | 11 | 12 | let getCallTarget (ins: Instruction) = 13 | match ins.DirectBranchTarget () |> Utils.tupleToOpt with 14 | | None -> 0UL 15 | | Some target -> target 16 | 17 | let isEndbr (ins: Instruction) = 18 | let intelInst = ins :?> IntelInstruction 19 | match intelInst.Opcode with 20 | | Opcode.ENDBR32 | Opcode.ENDBR64 -> true 21 | | _ -> false 22 | 23 | let addCallTarget cache (ins: Instruction) = 24 | let entry = Option.get cache.Handle.FileInfo.EntryPoint 25 | match ins.DirectBranchTarget () |> Utils.tupleToOpt with 26 | | None -> () 27 | | Some target -> 28 | if isTextAddr cache.Handle target && target <> (ins.Address + uint64 ins.Length) then 29 | Cache.setCallTargetCache cache target 30 | 31 | let addJumpTarget cache (ins: Instruction) = 32 | let intelInst = ins :?> IntelInstruction 33 | match intelInst.Opcode with 34 | | Opcode.JMPFar | Opcode.JMPNear -> 35 | match ins.DirectBranchTarget () |> Utils.tupleToOpt with 36 | | None -> () 37 | | Some target -> 38 | if isTextAddr cache.Handle target then 39 | Cache.setJumpCache cache ins.Address target 40 | Cache.setJumpTargetRefCache cache target ins.Address 41 | | _ -> () 42 | 43 | let parse cache = 44 | let rec disasm hdl bp = 45 | if BinaryPointer.IsValid bp then 46 | match BinHandle.TryParseInstr (hdl, bp=bp) with 47 | | Ok (ins) -> 48 | let bp' = BinaryPointer.Advance bp (int ins.Length) 49 | if isEndbr ins && isTextAddr cache.Handle bp.Addr then 50 | Cache.setEndbrCache cache bp.Addr 51 | Cache.setLinearCache cache bp.Addr ins 52 | if ins.IsCall () then 53 | addCallTarget cache ins 54 | if ins.IsBranch () then 55 | addJumpTarget cache ins 56 | disasm hdl bp' 57 | | Error _ -> 58 | let bp' = BinaryPointer.Advance bp 1 59 | disasm hdl bp' 60 | else () 61 | 62 | let hdl = cache.Handle 63 | 64 | cache.Handle.FileInfo.ExceptionTable 65 | |> ARMap.iter (fun funcRange _ -> 66 | Cache.setExceptionCache cache funcRange.Min (int (funcRange.Max - funcRange.Min + 1UL)) 67 | ) 68 | 69 | hdl.FileInfo.GetExecutableSections () 70 | |> Seq.iter (fun s -> 71 | if s.Size > 0UL then 72 | let bp = BinaryPointer.OfSection s 73 | disasm hdl bp) 74 | -------------------------------------------------------------------------------- /src/FunSeeker/EndbrFP.fs: -------------------------------------------------------------------------------- 1 | module FunSeeker.EndbrFP 2 | 3 | open B2R2 4 | open B2R2.FrontEnd.BinLifter 5 | 6 | open FunSeeker.Cache 7 | open FunSeeker.Disasm 8 | 9 | let returnTwiceDict cache = 10 | (* 11 | setjmp, sigsetjmp, vfork, savectx, getcontext 12 | *) 13 | let returnTwiceFuncNames = [ 14 | "vfork" ; 15 | "setjmp" ; 16 | "sigsetjmp" ; 17 | "savectx" ; 18 | "getcontext" ; 19 | ] 20 | 21 | let stripUnderScore (s: string) = 22 | let rec loop curr = 23 | if String.length curr < 2 then curr 24 | else 25 | if curr.Chars(0) = '_' then 26 | loop (curr.Substring (1)) 27 | else curr 28 | loop s 29 | 30 | cache.Handle.FileInfo.GetLinkageTableEntries () 31 | |> Seq.iter (fun sym -> 32 | let name = sym.FuncName 33 | let isReturnTwice = List.fold (fun state (str: string) -> 34 | if stripUnderScore name = str then true else state) false returnTwiceFuncNames 35 | if isReturnTwice then 36 | Cache.setReturnTwiceCache cache sym.TrampolineAddress 37 | ) 38 | 39 | let inExceptionRange cache (addr: Addr) = 40 | match SortedList.findGreatestLowerBoundKey addr cache.ExceptionCache with 41 | | Some funcAddr -> 42 | let funcSize = cache.ExceptionCache.[funcAddr] 43 | (addr > funcAddr) && (addr < funcAddr + uint64 funcSize) 44 | | _ -> false 45 | 46 | let eliminateExceptions cache = 47 | cache.EndbrCache.RemoveWhere (fun addr -> 48 | if inExceptionRange cache addr then 49 | true 50 | else 51 | cache.NumException <- cache.NumException + 1 52 | false 53 | ) 54 | |> ignore 55 | 56 | let isReturnTwice cache (inst: Instruction) = 57 | if inst.IsCall () then 58 | let target = getCallTarget inst 59 | if cache.ReturnTwiceCache.Contains target then true 60 | else false 61 | else false 62 | 63 | let isFallThrough cache (inst: Instruction) = 64 | if inst.IsCondBranch () then true 65 | else false 66 | 67 | let lookPrevInst cache = 68 | returnTwiceDict cache 69 | let prevInstAddr addr = 70 | match SortedList.findGreatestLowerBoundKey (addr - 1UL) cache.LinearCache with 71 | | Some prevAddr -> prevAddr 72 | | _ -> 0UL 73 | cache.EndbrCache.RemoveWhere (fun addr -> 74 | let prevAddr = prevInstAddr addr 75 | let prevInst = cache.LinearCache.[prevAddr] 76 | if isReturnTwice cache prevInst then 77 | cache.NumReturnTwice <- cache.NumReturnTwice + 1 78 | true 79 | /// elif isFallThrough cache prevInst then false 80 | else false 81 | ) 82 | |> ignore 83 | 84 | let eliminateEndbrFP cache = 85 | lookPrevInst cache 86 | eliminateExceptions cache 87 | -------------------------------------------------------------------------------- /src/FunSeeker/FunSeeker.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net6.0 6 | 3390;$(WarnOn) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/FunSeeker/Program.fs: -------------------------------------------------------------------------------- 1 | open FunSeeker.Cache 2 | open FunSeeker.BinUtil 3 | open FunSeeker.Disasm 4 | open FunSeeker.Report 5 | 6 | [] 7 | let main argv = 8 | if Array.length argv <> 2 then 9 | let hdl = createBinHandleFromPath argv.[0] 10 | let cache = Cache.initCache hdl 11 | parse cache 12 | reportType4 cache 13 | printFunctions cache 14 | else 15 | let ty = argv.[1] 16 | let hdl = createBinHandleFromPath argv.[0] 17 | let cache = Cache.initCache hdl 18 | parse cache 19 | if ty = "type1" then 20 | reportType1 cache 21 | printFunctions cache 22 | elif ty = "type2" then 23 | reportType2 cache 24 | printFunctions cache 25 | elif ty = "type3" then 26 | reportType3 cache 27 | printFunctions cache 28 | elif ty = "type4" then 29 | reportType4 cache 30 | printFunctions cache 31 | elif ty = "type5" then 32 | reportType5 cache 33 | printFunctions cache 34 | elif ty = "fp" then 35 | reportFP cache 36 | elif ty = "exception" then 37 | reportException cache 38 | elif ty = "endbr" then 39 | reportEndbr cache 40 | printFunctions cache 41 | elif ty = "jump" then 42 | reportJumpTarget cache 43 | else 0 44 | -------------------------------------------------------------------------------- /src/FunSeeker/Report.fs: -------------------------------------------------------------------------------- 1 | module FunSeeker.Report 2 | 3 | open B2R2 4 | open FunSeeker.Cache 5 | open FunSeeker.EndbrFP 6 | open FunSeeker.Tailcall 7 | 8 | let printFunctions cache = 9 | cache.FunctionCache 10 | |> Seq.iter (fun addr -> 11 | printf "%x\n" addr 12 | ) 13 | 0 14 | 15 | /// Direct Branch Target 16 | let reportType1 cache = 17 | cache.FunctionCache.UnionWith(cache.CallTargetCache) 18 | 19 | /// Endbr 20 | let reportType2 cache = 21 | cache.FunctionCache.UnionWith(cache.EndbrCache) 22 | 23 | /// DBT + Endbr 24 | let reportType3 cache = 25 | cache.FunctionCache.UnionWith(cache.CallTargetCache) |> ignore 26 | cache.FunctionCache.UnionWith(cache.EndbrCache) |> ignore 27 | 28 | /// DBT + Endbr + ReturnTwice + Exception 29 | let reportType4 cache = 30 | eliminateEndbrFP cache 31 | cache.FunctionCache.UnionWith(cache.CallTargetCache) |> ignore 32 | cache.FunctionCache.UnionWith(cache.EndbrCache) |> ignore 33 | 34 | /// DBT + Endbr + ReturnTwice + Exception + TailCall analysis 35 | let reportType5 cache = 36 | eliminateEndbrFP cache 37 | cache.FunctionCache.UnionWith(cache.CallTargetCache) |> ignore 38 | cache.FunctionCache.UnionWith(cache.EndbrCache) |> ignore 39 | tailCallAnalysis cache 40 | 41 | let reportFP cache = 42 | eliminateEndbrFP cache 43 | printf "ReturnTwice : %d\n" cache.NumReturnTwice 44 | printf "Exception : %d\n" cache.NumException 45 | 0 46 | 47 | let reportException cache = 48 | let getExceptionRange cache (addr: Addr) = 49 | match SortedList.findGreatestLowerBoundKey addr cache.ExceptionCache with 50 | | Some funcAddr -> 51 | let funcSize = cache.ExceptionCache.[funcAddr] 52 | (funcAddr, funcAddr + uint64 funcSize) 53 | | _ -> (0UL, 0UL) 54 | 55 | cache.EndbrCache 56 | |> Seq.iter (fun addr -> 57 | if inExceptionRange cache addr |> not then 58 | () 59 | else 60 | let fstart, fend = getExceptionRange cache addr 61 | printf "%x %x %x\n" fstart fend addr 62 | ) 63 | 0 64 | 65 | let reportJumpTarget cache = 66 | Seq.iter (fun addr -> 67 | printf "%x\n" addr 68 | ) cache.JumpTargetRefCache.Keys 69 | 0 70 | 71 | let reportEndbr cache = 72 | eliminateEndbrFP cache 73 | cache.FunctionCache.UnionWith(cache.EndbrCache) |> ignore 74 | -------------------------------------------------------------------------------- /src/FunSeeker/Tailcall.fs: -------------------------------------------------------------------------------- 1 | module FunSeeker.Tailcall 2 | 3 | open FunSeeker.Cache 4 | open FunSeeker.BinUtil 5 | 6 | let blockEndOf cache addr = 7 | let rec loop addr = 8 | if cache.LinearCache.ContainsKey(addr) then 9 | let inst = cache.LinearCache.[addr] 10 | if inst.IsBBLEnd () && (inst.IsCall () |> not) then 11 | addr 12 | else loop (addr + uint64 inst.Length) 13 | else addr 14 | loop addr 15 | 16 | let isColdFunc cache addr fboundary = 17 | let fstart, fend = fboundary 18 | cache.JumpTargetRefCache.[addr] 19 | |> Seq.fold (fun state addr -> 20 | if addr >= fstart && addr < fend then 21 | true && state 22 | else false 23 | ) true 24 | 25 | let tailCallAnalysis cache = 26 | let functionList = cache.FunctionCache |> Seq.toList |> List.sort 27 | let functionBoundaryOf cache addr = 28 | let fIdx' = 29 | List.tryFindIndexBack (fun funcAddr -> 30 | funcAddr <= addr 31 | ) functionList 32 | match fIdx' with 33 | | Some idx -> 34 | if idx >= functionList.Length then 35 | (0UL, 0UL) 36 | else 37 | let fstart = functionList.[idx] 38 | let fend = functionList.[idx + 1] 39 | (fstart, fend) 40 | | _ -> (0UL, 0UL) 41 | cache.JumpCache.Keys 42 | |> Seq.cast 43 | |> Seq.iter (fun addr -> 44 | let fstart, fend = functionBoundaryOf cache addr 45 | let target = cache.JumpCache.[addr] 46 | if (fstart <> 0UL && fend <> 0UL) && (target < fstart || target >= fend) then 47 | if isColdFunc cache target (fstart, fend) then () 48 | else 49 | Cache.setFunctionCache cache target 50 | else () 51 | ) 52 | 53 | let tailCallAnalysis2 cache = 54 | let functionBoundaryOf cache addr = 55 | let min, max = textBoundaryOf cache.Handle 56 | let rec before addr go = 57 | if addr < min then 0UL 58 | elif cache.FunctionCache.Contains(addr) then 59 | if not go then addr 60 | else before (addr - 1UL) false 61 | else before (addr - 1UL) go 62 | and after addr = 63 | if addr > max then 0UL 64 | elif cache.FunctionCache.Contains(addr) then addr 65 | else after (addr + 1UL) 66 | (before addr true, after addr) 67 | cache.JumpCache.Keys 68 | |> Seq.cast 69 | |> Seq.iter (fun addr -> 70 | let fstart, fend = functionBoundaryOf cache addr 71 | let target = cache.JumpCache.[addr] 72 | if (fstart <> 0UL && fend <> 0UL) && (target < fstart || target >= fend) then 73 | Cache.setFunctionCache cache target 74 | ) 75 | --------------------------------------------------------------------------------