From 2dcd92e9eccbcd5f99e9d0618e45a248f4f037a3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 24 Feb 2026 13:42:28 +0000 Subject: [PATCH] archive: add 5 repo prompt(s) [skip ci] --- archive/0vercl0k/rp.txt | 374894 +++++++++++++++ archive/DarthTon/Blackbone.txt | 134428 ++++++ archive/HoShiMin/Kernel-Bridge.txt | 27333 ++ archive/bromoket/access_updated.txt | 4392 + .../juniorjacob/readwrite-kernel-stable.txt | 483 + archive/waryas/EUPMAccess.txt | 1050 + 6 files changed, 542580 insertions(+) create mode 100644 archive/0vercl0k/rp.txt create mode 100644 archive/DarthTon/Blackbone.txt create mode 100644 archive/HoShiMin/Kernel-Bridge.txt create mode 100644 archive/bromoket/access_updated.txt create mode 100644 archive/juniorjacob/readwrite-kernel-stable.txt create mode 100644 archive/waryas/EUPMAccess.txt diff --git a/archive/0vercl0k/rp.txt b/archive/0vercl0k/rp.txt new file mode 100644 index 00000000..b5b43b98 --- /dev/null +++ b/archive/0vercl0k/rp.txt @@ -0,0 +1,374894 @@ +Project Path: arc_0vercl0k_rp__kot9zmi + +Source Tree: + +```txt +arc_0vercl0k_rp__kot9zmi +├── LICENSE +├── README.md +├── pics +│ ├── rp-r.gif +│ ├── rp-shexa.gif +│ ├── rp-sint.gif +│ └── rp.gif +└── src + ├── CMakeLists.txt + ├── rp + │ ├── arm.hpp + │ ├── arm64.hpp + │ ├── coloshell.hpp + │ ├── cpu.hpp + │ ├── disassenginewrapper.hpp + │ ├── elf.hpp + │ ├── elf_struct.hpp + │ ├── executable_format.hpp + │ ├── gadget.hpp + │ ├── instruction.hpp + │ ├── intelbeaengine.hpp + │ ├── macho.hpp + │ ├── macho_struct.hpp + │ ├── main.cpp + │ ├── options.hpp + │ ├── pe.hpp + │ ├── pe_struct.hpp + │ ├── platform.h + │ ├── program.cpp + │ ├── program.hpp + │ ├── raw.hpp + │ ├── ropsearch_algorithm.cpp + │ ├── ropsearch_algorithm.hpp + │ ├── rpexception.hpp + │ ├── safeint.hpp + │ ├── section.hpp + │ ├── toolbox.cpp + │ ├── toolbox.hpp + │ ├── x64.hpp + │ └── x86.hpp + └── third_party + ├── CLI11 + │ └── CLI11.hpp + ├── beaengine + │ ├── CMakeLists.txt + │ ├── README.md + │ ├── changelog + │ ├── headers + │ │ ├── BeaEngine.h + │ │ ├── BeaEngineDelphi32.pas + │ │ ├── BeaEngineDelphi64.pas + │ │ ├── BeaEngineFasm32.inc + │ │ ├── BeaEngineFasm64.inc + │ │ ├── BeaEngineGoAsm32.inc + │ │ ├── BeaEngineGoAsm64.inc + │ │ ├── BeaEngineMasm32.inc + │ │ ├── BeaEngineMasm64.inc + │ │ ├── BeaEngineNasm32.inc + │ │ ├── BeaEngineNasm64.inc + │ │ ├── BeaEnginePureBasic32.pb + │ │ ├── BeaEnginePureBasic64.pb + │ │ ├── BeaEnginePython.py + │ │ ├── C# headers + │ │ │ ├── Constants.cs + │ │ │ ├── Engine.cs + │ │ │ └── Structs.cs + │ │ ├── Includes + │ │ │ ├── basic_types.h + │ │ │ └── export.h + │ │ └── __init__.py + │ ├── include + │ │ └── beaengine + │ │ ├── BeaEngine.h + │ │ ├── basic_types.h + │ │ ├── export.h + │ │ └── macros.h + │ ├── src + │ │ ├── BeaEngine.c + │ │ ├── CMakeLists.txt + │ │ ├── COPYING.LESSER.txt + │ │ ├── COPYING.txt + │ │ └── Includes + │ │ ├── BeaEngine.h + │ │ ├── BeaEngineVersion.c + │ │ ├── Routines_Disasm.c + │ │ ├── Routines_ModRM.c + │ │ ├── instr_set + │ │ │ ├── Data_opcode.h + │ │ │ ├── instructions_list.c + │ │ │ ├── opcodes_Grp1.c + │ │ │ ├── opcodes_Grp12.c + │ │ │ ├── opcodes_Grp13.c + │ │ │ ├── opcodes_Grp14.c + │ │ │ ├── opcodes_Grp15.c + │ │ │ ├── opcodes_Grp16.c + │ │ │ ├── opcodes_Grp17.c + │ │ │ ├── opcodes_Grp2.c + │ │ │ ├── opcodes_Grp3.c + │ │ │ ├── opcodes_Grp4.c + │ │ │ ├── opcodes_Grp5.c + │ │ │ ├── opcodes_Grp6.c + │ │ │ ├── opcodes_Grp7.c + │ │ │ ├── opcodes_Grp8.c + │ │ │ ├── opcodes_Grp9.c + │ │ │ └── opcodes_prefixes.c + │ │ ├── internal_datas.h + │ │ └── protos.h + │ └── tests + │ ├── 0f01.py + │ ├── 0f02.py + │ ├── 0f03.py + │ ├── 0f08.py + │ ├── 0f10.py + │ ├── 0f11.py + │ ├── 0f12.py + │ ├── 0f13.py + │ ├── 0f14.py + │ ├── 0f15.py + │ ├── 0f16.py + │ ├── 0f17.py + │ ├── 0f1e.py + │ ├── 0f28.py + │ ├── 0f29.py + │ ├── 0f2a.py + │ ├── 0f2b.py + │ ├── 0f2c.py + │ ├── 0f2d.py + │ ├── 0f2e.py + │ ├── 0f2f.py + │ ├── 0f37.py + │ ├── 0f3800.py + │ ├── 0f3801.py + │ ├── 0f3802.py + │ ├── 0f3803.py + │ ├── 0f3804.py + │ ├── 0f3805.py + │ ├── 0f3806.py + │ ├── 0f3807.py + │ ├── 0f3808.py + │ ├── 0f3809.py + │ ├── 0f380a.py + │ ├── 0f380b.py + │ ├── 0f380c.py + │ ├── 0f380d.py + │ ├── 0f380e.py + │ ├── 0f380f.py + │ ├── 0f3810.py + │ ├── 0f3811.py + │ ├── 0f3812.py + │ ├── 0f3813.py + │ ├── 0f3814.py + │ ├── 0f3815.py + │ ├── 0f3816.py + │ ├── 0f3817.py + │ ├── 0f3818.py + │ ├── 0f3819.py + │ ├── 0f381a.py + │ ├── 0f381b.py + │ ├── 0f381c.py + │ ├── 0f381d.py + │ ├── 0f381e.py + │ ├── 0f381f.py + │ ├── 0f3820.py + │ ├── 0f3821.py + │ ├── 0f3822.py + │ ├── 0f3823.py + │ ├── 0f3824.py + │ ├── 0f3825.py + │ ├── 0f3826.py + │ ├── 0f3827.py + │ ├── 0f3828.py + │ ├── 0f3829.py + │ ├── 0f382a.py + │ ├── 0f382b.py + │ ├── 0f382c.py + │ ├── 0f382d.py + │ ├── 0f382e.py + │ ├── 0f382f.py + │ ├── 0f3830.py + │ ├── 0f3831.py + │ ├── 0f3832.py + │ ├── 0f3833.py + │ ├── 0f3834.py + │ ├── 0f3835.py + │ ├── 0f3836.py + │ ├── 0f3837.py + │ ├── 0f3838.py + │ ├── 0f3839.py + │ ├── 0f383a.py + │ ├── 0f383b.py + │ ├── 0f383c.py + │ ├── 0f383d.py + │ ├── 0f383e.py + │ ├── 0f383f.py + │ ├── 0f3840.py + │ ├── 0f3841.py + │ ├── 0f3842.py + │ ├── 0f3843.py + │ ├── 0f3844.py + │ ├── 0f3845.py + │ ├── 0f3846.py + │ ├── 0f3847.py + │ ├── 0f3849.py + │ ├── 0f384b.py + │ ├── 0f384c.py + │ ├── 0f384d.py + │ ├── 0f384e.py + │ ├── 0f384f.py + │ ├── 0f3850.py + │ ├── 0f3851.py + │ ├── 0f3852.py + │ ├── 0f3853.py + │ ├── 0f3854.py + │ ├── 0f3855.py + │ ├── 0f3858.py + │ ├── 0f3859.py + │ ├── 0f385a.py + │ ├── 0f385b.py + │ ├── 0f385c.py + │ ├── 0f385e.py + │ ├── 0f3862.py + │ ├── 0f3863.py + │ ├── 0f3864.py + │ ├── 0f3865.py + │ ├── 0f3866.py + │ ├── 0f3870.py + │ ├── 0f3871.py + │ ├── 0f3872.py + │ ├── 0f3873.py + │ ├── 0f3875.py + │ ├── 0f3876.py + │ ├── 0f3877.py + │ ├── 0f3878.py + │ ├── 0f3879.py + │ ├── 0f387a.py + │ ├── 0f387b.py + │ ├── 0f387c.py + │ ├── 0f387d.py + │ ├── 0f387e.py + │ ├── 0f387f.py + │ ├── 0f3880.py + │ ├── 0f3881.py + │ ├── 0f3882.py + │ ├── 0f3883.py + │ ├── 0f3888.py + │ ├── 0f3889.py + │ ├── 0f388a.py + │ ├── 0f388b.py + │ ├── 0f388c.py + │ ├── 0f388d.py + │ ├── 0f388e.py + │ ├── 0f388f.py + │ ├── 0f3890.py + │ ├── 0f3891.py + │ ├── 0f3892.py + │ ├── 0f3893.py + │ ├── 0f3896.py + │ ├── 0f3897.py + │ ├── 0f3898.py + │ ├── 0f3899.py + │ ├── 0f389a.py + │ ├── 0f389b.py + │ ├── 0f389c.py + │ ├── 0f389d.py + │ ├── 0f389e.py + │ ├── 0f389f.py + │ ├── 0f38a0.py + │ ├── 0f38a1.py + │ ├── 0f38a2.py + │ ├── 0f38a3.py + │ ├── 0f38a6.py + │ ├── 0f38a7.py + │ ├── 0f38a8.py + │ ├── 0f38a9.py + │ ├── 0f38aa.py + │ ├── 0f38ab.py + │ ├── 0f38ac.py + │ ├── 0f38ad.py + │ ├── 0f38ae.py + │ ├── 0f38af.py + │ ├── 0f38b4.py + │ ├── 0f38b5.py + │ ├── 0f38b6.py + │ ├── 0f38b7.py + │ ├── 0f38b8.py + │ ├── 0f38b9.py + │ ├── 0f38ba.py + │ ├── 0f38bb.py + │ ├── 0f38bc.py + │ ├── 0f38bd.py + │ ├── 0f38be.py + │ ├── 0f38bf.py + │ ├── 0f38c4.py + │ ├── 0f38c8.py + │ ├── 0f38c9.py + │ ├── 0f38ca.py + │ ├── 0f38cb.py + │ ├── 0f38cc.py + │ ├── 0f38cd.py + │ ├── 0f38d8.py + │ ├── 0f38db.py + │ ├── 0f38dc.py + │ ├── 0f38dd.py + │ ├── 0f38de.py + │ ├── 0f38df.py + │ ├── 0f38f0.py + │ ├── 0f38f1.py + │ ├── 0f38f5.py + │ ├── 0f38f6.py + │ ├── 0f38f7.py + │ ├── 0f38fa.py + │ ├── 0f38fb.py + │ ├── 0f3a00.py + │ ├── 0f3a01.py + │ ├── 0f3a02.py + │ ├── 0f3a03.py + │ ├── 0f3a04.py + │ ├── 0f3a05.py + │ ├── 0f3a06.py + │ ├── 0f3a08.py + │ ├── 0f3a09.py + │ ├── 0f3a0a.py + │ ├── 0f3a0b.py + │ ├── 0f3a0c.py + │ ├── 0f3a0d.py + │ ├── 0f3a0e.py + │ ├── 0f3a0f.py + │ ├── 0f3a14.py + │ ├── 0f3a15.py + │ ├── 0f3a16.py + │ ├── 0f3a17.py + │ ├── 0f3a18.py + │ ├── 0f3a19.py + │ ├── 0f3a1a.py + │ ├── 0f3a1b.py + │ ├── 0f3a1d.py + │ ├── 0f3a1e.py + │ ├── 0f3a1f.py + │ ├── 0f3a20.py + │ ├── 0f3a21.py + │ ├── 0f3a22.py + │ ├── 0f3a23.py + │ ├── 0f3a25.py + │ ├── 0f3a26.py + │ ├── 0f3a27.py + │ ├── 0f3a30.py + │ ├── 0f3a31.py + │ ├── 0f3a32.py + │ ├── 0f3a33.py + │ ├── 0f3a38.py + │ ├── 0f3a39.py + │ ├── 0f3a3a.py + │ ├── 0f3a3b.py + │ ├── 0f3a3e.py + │ ├── 0f3a3f.py + │ ├── 0f3a40.py + │ ├── 0f3a41.py + │ ├── 0f3a42.py + │ ├── 0f3a43.py + │ ├── 0f3a44.py + │ ├── 0f3a46.py + │ ├── 0f3a4a.py + │ ├── 0f3a4b.py + │ ├── 0f3a4c.py + │ ├── 0f3a50.py + │ ├── 0f3a51.py + │ ├── 0f3a54.py + │ ├── 0f3a55.py + │ ├── 0f3a56.py + │ ├── 0f3a57.py + │ ├── 0f3a60.py + │ ├── 0f3a61.py + │ ├── 0f3a62.py + │ ├── 0f3a63.py + │ ├── 0f3a66.py + │ ├── 0f3a67.py + │ ├── 0f3a70.py + │ ├── 0f3a71.py + │ ├── 0f3a72.py + │ ├── 0f3acc.py + │ ├── 0f3adf.py + │ ├── 0f3af0.py + │ ├── 0f41.py + │ ├── 0f42.py + │ ├── 0f44.py + │ ├── 0f45.py + │ ├── 0f46.py + │ ├── 0f47.py + │ ├── 0f4a.py + │ ├── 0f4b.py + │ ├── 0f50.py + │ ├── 0f51.py + │ ├── 0f52.py + │ ├── 0f53.py + │ ├── 0f54.py + │ ├── 0f55.py + │ ├── 0f56.py + │ ├── 0f57.py + │ ├── 0f58.py + │ ├── 0f59.py + │ ├── 0f5a.py + │ ├── 0f5b.py + │ ├── 0f5c.py + │ ├── 0f5d.py + │ ├── 0f5e.py + │ ├── 0f5f.py + │ ├── 0f60.py + │ ├── 0f61.py + │ ├── 0f62.py + │ ├── 0f63.py + │ ├── 0f64.py + │ ├── 0f65.py + │ ├── 0f66.py + │ ├── 0f67.py + │ ├── 0f68.py + │ ├── 0f69.py + │ ├── 0f6a.py + │ ├── 0f6b.py + │ ├── 0f6c.py + │ ├── 0f6d.py + │ ├── 0f6e.py + │ ├── 0f6f.py + │ ├── 0f70.py + │ ├── 0f71.py + │ ├── 0f72.py + │ ├── 0f73.py + │ ├── 0f74.py + │ ├── 0f75.py + │ ├── 0f76.py + │ ├── 0f77.py + │ ├── 0f78.py + │ ├── 0f79.py + │ ├── 0f7a.py + │ ├── 0f7b.py + │ ├── 0f7c.py + │ ├── 0f7d.py + │ ├── 0f7f.py + │ ├── 0f90.py + │ ├── 0f91.py + │ ├── 0f92.py + │ ├── 0f93.py + │ ├── 0f98.py + │ ├── 0f99.py + │ ├── 0fae.py + │ ├── 0fc0.py + │ ├── 0fc1.py + │ ├── 0fc2.py + │ ├── 0fc4.py + │ ├── 0fc5.py + │ ├── 0fc6.py + │ ├── 0fc7.py + │ ├── 0fd0.py + │ ├── 0fd1.py + │ ├── 0fd2.py + │ ├── 0fd3.py + │ ├── 0fd4.py + │ ├── 0fd5.py + │ ├── 0fd6.py + │ ├── 0fd7.py + │ ├── 0fd8.py + │ ├── 0fd9.py + │ ├── 0fda.py + │ ├── 0fdb.py + │ ├── 0fdc.py + │ ├── 0fdd.py + │ ├── 0fde.py + │ ├── 0fdf.py + │ ├── 0fe0.py + │ ├── 0fe1.py + │ ├── 0fe2.py + │ ├── 0fe3.py + │ ├── 0fe4.py + │ ├── 0fe5.py + │ ├── 0fe6.py + │ ├── 0fe7.py + │ ├── 0fe8.py + │ ├── 0fe9.py + │ ├── 0fea.py + │ ├── 0feb.py + │ ├── 0fec.py + │ ├── 0fed.py + │ ├── 0fee.py + │ ├── 0fef.py + │ ├── 0ff0.py + │ ├── 0ff1.py + │ ├── 0ff2.py + │ ├── 0ff3.py + │ ├── 0ff4.py + │ ├── 0ff5.py + │ ├── 0ff6.py + │ ├── 0ff7.py + │ ├── 0ff8.py + │ ├── 0ff9.py + │ ├── 0ffa.py + │ ├── 0ffb.py + │ ├── 0ffc.py + │ ├── 0ffd.py + │ ├── 0ffe.py + │ ├── 0fff.py + │ ├── README.md + │ ├── REXPrefix.py + │ ├── VEXPrefix.py + │ ├── examples.py + │ ├── issue#2.py + │ ├── issue#3.py + │ ├── modrm.py + │ ├── opcode1byte.yml + │ ├── regopcode.py + │ ├── securityblock.py + │ ├── test.py + │ ├── test_prefixes.py + │ └── test_python_wrapper.py + ├── capstone + │ ├── CMakeLists.txt + │ ├── COMPILE.TXT + │ ├── COMPILE_CMAKE.TXT + │ ├── COMPILE_MSVC.TXT + │ ├── CREDITS.TXT + │ ├── ChangeLog + │ ├── HACK.TXT + │ ├── LEB128.h + │ ├── LICENSE.TXT + │ ├── LICENSE_LLVM.TXT + │ ├── MCDisassembler.h + │ ├── MCFixedLenDisassembler.h + │ ├── MCInst.c + │ ├── MCInst.h + │ ├── MCInstrDesc.c + │ ├── MCInstrDesc.h + │ ├── MCRegisterInfo.c + │ ├── MCRegisterInfo.h + │ ├── Makefile + │ ├── MathExtras.h + │ ├── README.md + │ ├── RELEASE_NOTES + │ ├── SPONSORS.TXT + │ ├── SStream.c + │ ├── SStream.h + │ ├── TODO + │ ├── arch + │ │ ├── AArch64 + │ │ │ ├── AArch64AddressingModes.h + │ │ │ ├── AArch64BaseInfo.c + │ │ │ ├── AArch64BaseInfo.h + │ │ │ ├── AArch64Disassembler.c + │ │ │ ├── AArch64Disassembler.h + │ │ │ ├── AArch64GenAsmWriter.inc + │ │ │ ├── AArch64GenDisassemblerTables.inc + │ │ │ ├── AArch64GenInstrInfo.inc + │ │ │ ├── AArch64GenRegisterInfo.inc + │ │ │ ├── AArch64GenSubtargetInfo.inc + │ │ │ ├── AArch64InstPrinter.c + │ │ │ ├── AArch64InstPrinter.h + │ │ │ ├── AArch64Mapping.c + │ │ │ ├── AArch64Mapping.h + │ │ │ ├── AArch64MappingInsn.inc + │ │ │ ├── AArch64MappingInsnOp.inc + │ │ │ ├── AArch64Module.c + │ │ │ └── AArch64Module.h + │ │ ├── ARM + │ │ │ ├── ARMAddressingModes.h + │ │ │ ├── ARMBaseInfo.h + │ │ │ ├── ARMDisassembler.c + │ │ │ ├── ARMDisassembler.h + │ │ │ ├── ARMGenAsmWriter.inc + │ │ │ ├── ARMGenDisassemblerTables.inc + │ │ │ ├── ARMGenInstrInfo.inc + │ │ │ ├── ARMGenRegisterInfo.inc + │ │ │ ├── ARMGenSubtargetInfo.inc + │ │ │ ├── ARMInstPrinter.c + │ │ │ ├── ARMInstPrinter.h + │ │ │ ├── ARMMapping.c + │ │ │ ├── ARMMapping.h + │ │ │ ├── ARMMappingInsn.inc + │ │ │ ├── ARMMappingInsnOp.inc + │ │ │ ├── ARMModule.c + │ │ │ └── ARMModule.h + │ │ ├── EVM + │ │ │ ├── EVMDisassembler.c + │ │ │ ├── EVMDisassembler.h + │ │ │ ├── EVMInstPrinter.c + │ │ │ ├── EVMInstPrinter.h + │ │ │ ├── EVMMapping.c + │ │ │ ├── EVMMapping.h + │ │ │ ├── EVMMappingInsn.inc + │ │ │ ├── EVMModule.c + │ │ │ └── EVMModule.h + │ │ ├── M680X + │ │ │ ├── M680XDisassembler.c + │ │ │ ├── M680XDisassembler.h + │ │ │ ├── M680XDisassemblerInternals.h + │ │ │ ├── M680XInstPrinter.c + │ │ │ ├── M680XInstPrinter.h + │ │ │ ├── M680XModule.c + │ │ │ ├── M680XModule.h + │ │ │ ├── cpu12.inc + │ │ │ ├── hcs08.inc + │ │ │ ├── hd6301.inc + │ │ │ ├── hd6309.inc + │ │ │ ├── insn_props.inc + │ │ │ ├── m6800.inc + │ │ │ ├── m6801.inc + │ │ │ ├── m6805.inc + │ │ │ ├── m6808.inc + │ │ │ ├── m6809.inc + │ │ │ └── m6811.inc + │ │ ├── M68K + │ │ │ ├── M68KDisassembler.c + │ │ │ ├── M68KDisassembler.h + │ │ │ ├── M68KInstPrinter.c + │ │ │ ├── M68KInstPrinter.h + │ │ │ ├── M68KInstructionTable.inc + │ │ │ ├── M68KModule.c + │ │ │ └── M68KModule.h + │ │ ├── Mips + │ │ │ ├── MipsDisassembler.c + │ │ │ ├── MipsDisassembler.h + │ │ │ ├── MipsGenAsmWriter.inc + │ │ │ ├── MipsGenDisassemblerTables.inc + │ │ │ ├── MipsGenInstrInfo.inc + │ │ │ ├── MipsGenRegisterInfo.inc + │ │ │ ├── MipsGenSubtargetInfo.inc + │ │ │ ├── MipsInstPrinter.c + │ │ │ ├── MipsInstPrinter.h + │ │ │ ├── MipsMapping.c + │ │ │ ├── MipsMapping.h + │ │ │ ├── MipsMappingInsn.inc + │ │ │ ├── MipsModule.c + │ │ │ └── MipsModule.h + │ │ ├── PowerPC + │ │ │ ├── PPCDisassembler.c + │ │ │ ├── PPCDisassembler.h + │ │ │ ├── PPCGenAsmWriter.inc + │ │ │ ├── PPCGenDisassemblerTables.inc + │ │ │ ├── PPCGenInstrInfo.inc + │ │ │ ├── PPCGenRegisterInfo.inc + │ │ │ ├── PPCGenSubtargetInfo.inc + │ │ │ ├── PPCInstPrinter.c + │ │ │ ├── PPCInstPrinter.h + │ │ │ ├── PPCMapping.c + │ │ │ ├── PPCMapping.h + │ │ │ ├── PPCMappingInsn.inc + │ │ │ ├── PPCModule.c + │ │ │ ├── PPCModule.h + │ │ │ └── PPCPredicates.h + │ │ ├── Sparc + │ │ │ ├── Sparc.h + │ │ │ ├── SparcDisassembler.c + │ │ │ ├── SparcDisassembler.h + │ │ │ ├── SparcGenAsmWriter.inc + │ │ │ ├── SparcGenDisassemblerTables.inc + │ │ │ ├── SparcGenInstrInfo.inc + │ │ │ ├── SparcGenRegisterInfo.inc + │ │ │ ├── SparcGenSubtargetInfo.inc + │ │ │ ├── SparcInstPrinter.c + │ │ │ ├── SparcInstPrinter.h + │ │ │ ├── SparcMapping.c + │ │ │ ├── SparcMapping.h + │ │ │ ├── SparcMappingInsn.inc + │ │ │ ├── SparcModule.c + │ │ │ └── SparcModule.h + │ │ ├── SystemZ + │ │ │ ├── SystemZDisassembler.c + │ │ │ ├── SystemZDisassembler.h + │ │ │ ├── SystemZGenAsmWriter.inc + │ │ │ ├── SystemZGenDisassemblerTables.inc + │ │ │ ├── SystemZGenInstrInfo.inc + │ │ │ ├── SystemZGenRegisterInfo.inc + │ │ │ ├── SystemZGenSubtargetInfo.inc + │ │ │ ├── SystemZInstPrinter.c + │ │ │ ├── SystemZInstPrinter.h + │ │ │ ├── SystemZMCTargetDesc.c + │ │ │ ├── SystemZMCTargetDesc.h + │ │ │ ├── SystemZMapping.c + │ │ │ ├── SystemZMapping.h + │ │ │ ├── SystemZMappingInsn.inc + │ │ │ ├── SystemZModule.c + │ │ │ └── SystemZModule.h + │ │ ├── TMS320C64x + │ │ │ ├── TMS320C64xDisassembler.c + │ │ │ ├── TMS320C64xDisassembler.h + │ │ │ ├── TMS320C64xGenAsmWriter.inc + │ │ │ ├── TMS320C64xGenDisassemblerTables.inc + │ │ │ ├── TMS320C64xGenInstrInfo.inc + │ │ │ ├── TMS320C64xGenRegisterInfo.inc + │ │ │ ├── TMS320C64xInstPrinter.c + │ │ │ ├── TMS320C64xInstPrinter.h + │ │ │ ├── TMS320C64xMapping.c + │ │ │ ├── TMS320C64xMapping.h + │ │ │ ├── TMS320C64xModule.c + │ │ │ └── TMS320C64xModule.h + │ │ ├── X86 + │ │ │ ├── X86ATTInstPrinter.c + │ │ │ ├── X86BaseInfo.h + │ │ │ ├── X86Disassembler.c + │ │ │ ├── X86Disassembler.h + │ │ │ ├── X86DisassemblerDecoder.c + │ │ │ ├── X86DisassemblerDecoder.h + │ │ │ ├── X86DisassemblerDecoderCommon.h + │ │ │ ├── X86GenAsmWriter.inc + │ │ │ ├── X86GenAsmWriter1.inc + │ │ │ ├── X86GenAsmWriter1_reduce.inc + │ │ │ ├── X86GenAsmWriter_reduce.inc + │ │ │ ├── X86GenDisassemblerTables.inc + │ │ │ ├── X86GenDisassemblerTables_reduce.inc + │ │ │ ├── X86GenInstrInfo.inc + │ │ │ ├── X86GenInstrInfo_reduce.inc + │ │ │ ├── X86GenRegisterInfo.inc + │ │ │ ├── X86ImmSize.inc + │ │ │ ├── X86InstPrinter.h + │ │ │ ├── X86IntelInstPrinter.c + │ │ │ ├── X86Mapping.c + │ │ │ ├── X86Mapping.h + │ │ │ ├── X86MappingInsn.inc + │ │ │ ├── X86MappingInsnOp.inc + │ │ │ ├── X86MappingInsnOp_reduce.inc + │ │ │ ├── X86MappingInsn_reduce.inc + │ │ │ ├── X86Module.c + │ │ │ ├── X86Module.h + │ │ │ └── qsort.h + │ │ └── XCore + │ │ ├── XCoreDisassembler.c + │ │ ├── XCoreDisassembler.h + │ │ ├── XCoreGenAsmWriter.inc + │ │ ├── XCoreGenDisassemblerTables.inc + │ │ ├── XCoreGenInstrInfo.inc + │ │ ├── XCoreGenRegisterInfo.inc + │ │ ├── XCoreInstPrinter.c + │ │ ├── XCoreInstPrinter.h + │ │ ├── XCoreMapping.c + │ │ ├── XCoreMapping.h + │ │ ├── XCoreMappingInsn.inc + │ │ ├── XCoreModule.c + │ │ └── XCoreModule.h + │ ├── bindings + │ │ ├── Makefile + │ │ ├── README + │ │ ├── const_generator.py + │ │ ├── java + │ │ │ ├── Makefile + │ │ │ ├── README + │ │ │ ├── TestArm.java + │ │ │ ├── TestArm64.java + │ │ │ ├── TestBasic.java + │ │ │ ├── TestM680x.java + │ │ │ ├── TestMips.java + │ │ │ ├── TestPpc.java + │ │ │ ├── TestSparc.java + │ │ │ ├── TestSystemz.java + │ │ │ ├── TestX86.java + │ │ │ ├── TestXcore.java + │ │ │ ├── capstone + │ │ │ │ ├── Arm.java + │ │ │ │ ├── Arm64.java + │ │ │ │ ├── Arm64_const.java + │ │ │ │ ├── Arm_const.java + │ │ │ │ ├── Capstone.java + │ │ │ │ ├── Evm_const.java + │ │ │ │ ├── M680x.java + │ │ │ │ ├── M680x_const.java + │ │ │ │ ├── M68k_const.java + │ │ │ │ ├── Mips.java + │ │ │ │ ├── Mips_const.java + │ │ │ │ ├── Ppc.java + │ │ │ │ ├── Ppc_const.java + │ │ │ │ ├── Sparc.java + │ │ │ │ ├── Sparc_const.java + │ │ │ │ ├── Systemz.java + │ │ │ │ ├── Sysz_const.java + │ │ │ │ ├── TMS320C64x_const.java + │ │ │ │ ├── X86.java + │ │ │ │ ├── X86_const.java + │ │ │ │ ├── Xcore.java + │ │ │ │ └── Xcore_const.java + │ │ │ └── run.sh + │ │ ├── ocaml + │ │ │ ├── Makefile + │ │ │ ├── README + │ │ │ ├── arm.ml + │ │ │ ├── arm64.ml + │ │ │ ├── arm64_const.ml + │ │ │ ├── arm_const.ml + │ │ │ ├── capstone.ml + │ │ │ ├── evm_const.ml + │ │ │ ├── m680x.ml + │ │ │ ├── m680x_const.ml + │ │ │ ├── m68k_const.ml + │ │ │ ├── mips.ml + │ │ │ ├── mips_const.ml + │ │ │ ├── ocaml.c + │ │ │ ├── ppc.ml + │ │ │ ├── ppc_const.ml + │ │ │ ├── sparc.ml + │ │ │ ├── sparc_const.ml + │ │ │ ├── systemz.ml + │ │ │ ├── sysz_const.ml + │ │ │ ├── test_arm.ml + │ │ │ ├── test_arm64.ml + │ │ │ ├── test_basic.ml + │ │ │ ├── test_detail.ml + │ │ │ ├── test_m680x.ml + │ │ │ ├── test_mips.ml + │ │ │ ├── test_ppc.ml + │ │ │ ├── test_sparc.ml + │ │ │ ├── test_systemz.ml + │ │ │ ├── test_x86.ml + │ │ │ ├── test_xcore.ml + │ │ │ ├── tms320c64x_const.ml + │ │ │ ├── x86.ml + │ │ │ ├── x86_const.ml + │ │ │ ├── xcore.ml + │ │ │ └── xcore_const.ml + │ │ ├── powershell + │ │ │ ├── Capstone + │ │ │ │ ├── Capstone.Format.ps1xml + │ │ │ │ ├── Capstone.psd1 + │ │ │ │ ├── Capstone.psm1 + │ │ │ │ └── Lib + │ │ │ │ └── Capstone + │ │ │ └── README.md + │ │ ├── python + │ │ │ ├── BUILDING.txt + │ │ │ ├── LICENSE.TXT + │ │ │ ├── MANIFEST.in + │ │ │ ├── Makefile + │ │ │ ├── README.txt + │ │ │ ├── capstone + │ │ │ │ ├── arm.py + │ │ │ │ ├── arm64.py + │ │ │ │ ├── arm64_const.py + │ │ │ │ ├── arm_const.py + │ │ │ │ ├── evm.py + │ │ │ │ ├── evm_const.py + │ │ │ │ ├── m680x.py + │ │ │ │ ├── m680x_const.py + │ │ │ │ ├── m68k.py + │ │ │ │ ├── m68k_const.py + │ │ │ │ ├── mips.py + │ │ │ │ ├── mips_const.py + │ │ │ │ ├── ppc.py + │ │ │ │ ├── ppc_const.py + │ │ │ │ ├── sparc.py + │ │ │ │ ├── sparc_const.py + │ │ │ │ ├── systemz.py + │ │ │ │ ├── sysz_const.py + │ │ │ │ ├── tms320c64x.py + │ │ │ │ ├── tms320c64x_const.py + │ │ │ │ ├── x86.py + │ │ │ │ ├── x86_const.py + │ │ │ │ ├── xcore.py + │ │ │ │ └── xcore_const.py + │ │ │ ├── prebuilt + │ │ │ ├── pyx + │ │ │ │ ├── README + │ │ │ │ └── ccapstone.pxd + │ │ │ ├── setup.py + │ │ │ ├── setup_cython.py + │ │ │ ├── test_all.py + │ │ │ ├── test_arm.py + │ │ │ ├── test_arm64.py + │ │ │ ├── test_basic.py + │ │ │ ├── test_customized_mnem.py + │ │ │ ├── test_detail.py + │ │ │ ├── test_evm.py + │ │ │ ├── test_lite.py + │ │ │ ├── test_m680x.py + │ │ │ ├── test_m68k.py + │ │ │ ├── test_mips.py + │ │ │ ├── test_ppc.py + │ │ │ ├── test_skipdata.py + │ │ │ ├── test_sparc.py + │ │ │ ├── test_systemz.py + │ │ │ ├── test_tms320c64x.py + │ │ │ ├── test_x86.py + │ │ │ ├── test_xcore.py + │ │ │ └── xprint.py + │ │ └── vb6 + │ │ ├── CDisassembler.cls + │ │ ├── CInstDetails.cls + │ │ ├── CInstruction.cls + │ │ ├── CX86Inst.cls + │ │ ├── CX86OpMem.cls + │ │ ├── CX86Operand.cls + │ │ ├── Form1.frm + │ │ ├── Form1.frx + │ │ ├── Module1.bas + │ │ ├── Project1.vbp + │ │ ├── Project1.vbw + │ │ ├── README.txt + │ │ ├── mMisc.bas + │ │ ├── mx86.bas + │ │ ├── screenshot.png + │ │ ├── vbCapstone.cpp + │ │ ├── vbCapstone.sln + │ │ └── vbCapstone.vcproj + │ ├── capstone.pc.in + │ ├── cmake.sh + │ ├── config.mk + │ ├── contrib + │ │ ├── README + │ │ ├── cs_driver + │ │ │ ├── README + │ │ │ ├── cs_driver + │ │ │ │ ├── cs_driver.c + │ │ │ │ ├── cs_driver.vcxproj + │ │ │ │ └── cs_driver.vcxproj.filters + │ │ │ └── cs_driver.sln + │ │ ├── m68k_instruction_tbl_gen + │ │ │ ├── M68KInstructionTblGen.c + │ │ │ └── Makefile + │ │ ├── objdump + │ │ │ └── objdump-m68k.py + │ │ └── windows_kernel + │ │ ├── README + │ │ ├── libc.cpp + │ │ └── libc.h + │ ├── cs.c + │ ├── cs_priv.h + │ ├── cstool + │ │ ├── Makefile + │ │ ├── README + │ │ ├── cstool.c + │ │ ├── cstool_arm.c + │ │ ├── cstool_arm64.c + │ │ ├── cstool_evm.c + │ │ ├── cstool_m680x.c + │ │ ├── cstool_m68k.c + │ │ ├── cstool_mips.c + │ │ ├── cstool_ppc.c + │ │ ├── cstool_sparc.c + │ │ ├── cstool_systemz.c + │ │ ├── cstool_tms320c64x.c + │ │ ├── cstool_x86.c + │ │ ├── cstool_xcore.c + │ │ └── getopt.h + │ ├── docs + │ │ ├── BHUSA2014-capstone.pdf + │ │ ├── README + │ │ └── capstone-logo.png + │ ├── functions.mk + │ ├── include + │ │ ├── capstone + │ │ │ ├── arm.h + │ │ │ ├── arm64.h + │ │ │ ├── capstone.h + │ │ │ ├── evm.h + │ │ │ ├── m680x.h + │ │ │ ├── m68k.h + │ │ │ ├── mips.h + │ │ │ ├── platform.h + │ │ │ ├── ppc.h + │ │ │ ├── sparc.h + │ │ │ ├── systemz.h + │ │ │ ├── tms320c64x.h + │ │ │ ├── x86.h + │ │ │ └── xcore.h + │ │ ├── platform.h + │ │ └── windowsce + │ │ ├── intrin.h + │ │ └── stdint.h + │ ├── make.sh + │ ├── msvc + │ │ ├── README + │ │ ├── capstone.sln + │ │ ├── capstone_dll + │ │ │ └── capstone_dll.vcxproj + │ │ ├── capstone_static + │ │ │ └── capstone_static.vcxproj + │ │ ├── capstone_static_winkernel + │ │ │ └── capstone_static_winkernel.vcxproj + │ │ ├── cstool + │ │ │ └── cstool.vcxproj + │ │ ├── test_arm + │ │ │ └── test_arm.vcxproj + │ │ ├── test_arm64 + │ │ │ └── test_arm64.vcxproj + │ │ ├── test_basic + │ │ │ └── test_basic.vcxproj + │ │ ├── test_customized_mnem + │ │ │ └── test_customized_mnem.vcxproj + │ │ ├── test_detail + │ │ │ └── test_detail.vcxproj + │ │ ├── test_evm + │ │ │ └── test_evm.vcxproj + │ │ ├── test_iter + │ │ │ └── test_iter.vcxproj + │ │ ├── test_m680x + │ │ │ └── test_m680x.vcxproj + │ │ ├── test_m68k + │ │ │ └── test_m68k.vcxproj + │ │ ├── test_mips + │ │ │ └── test_mips.vcxproj + │ │ ├── test_ppc + │ │ │ └── test_ppc.vcxproj + │ │ ├── test_skipdata + │ │ │ └── test_skipdata.vcxproj + │ │ ├── test_sparc + │ │ │ └── test_sparc.vcxproj + │ │ ├── test_systemz + │ │ │ └── test_systemz.vcxproj + │ │ ├── test_tms320c64x + │ │ │ └── test_tms320c64x.vcxproj + │ │ ├── test_winkernel + │ │ │ └── test_winkernel.vcxproj + │ │ ├── test_x86 + │ │ │ └── test_x86.vcxproj + │ │ └── test_xcore + │ │ └── test_xcore.vcxproj + │ ├── nmake.bat + │ ├── packages + │ │ ├── freebsd + │ │ │ └── ports + │ │ │ └── devel + │ │ │ └── capstone + │ │ │ ├── Makefile + │ │ │ ├── pkg-descr + │ │ │ └── pkg-plist + │ │ ├── macports + │ │ │ └── devel + │ │ │ └── capstone + │ │ │ ├── Portfile + │ │ │ └── files + │ │ │ └── patch-Makefile.diff + │ │ └── rpm + │ │ └── capstone.spec + │ ├── pkgconfig.mk + │ ├── suite + │ │ ├── MC + │ │ │ ├── AArch64 + │ │ │ │ ├── basic-a64-instructions.s.cs + │ │ │ │ ├── gicv3-regs.s.cs + │ │ │ │ ├── neon-2velem.s.cs + │ │ │ │ ├── neon-3vdiff.s.cs + │ │ │ │ ├── neon-aba-abd.s.cs + │ │ │ │ ├── neon-across.s.cs + │ │ │ │ ├── neon-add-pairwise.s.cs + │ │ │ │ ├── neon-add-sub-instructions.s.cs + │ │ │ │ ├── neon-bitwise-instructions.s.cs + │ │ │ │ ├── neon-compare-instructions.s.cs + │ │ │ │ ├── neon-crypto.s.cs + │ │ │ │ ├── neon-extract.s.cs + │ │ │ │ ├── neon-facge-facgt.s.cs + │ │ │ │ ├── neon-frsqrt-frecp.s.cs + │ │ │ │ ├── neon-halving-add-sub.s.cs + │ │ │ │ ├── neon-max-min-pairwise.s.cs + │ │ │ │ ├── neon-max-min.s.cs + │ │ │ │ ├── neon-mla-mls-instructions.s.cs + │ │ │ │ ├── neon-mov.s.cs + │ │ │ │ ├── neon-mul-div-instructions.s.cs + │ │ │ │ ├── neon-perm.s.cs + │ │ │ │ ├── neon-rounding-halving-add.s.cs + │ │ │ │ ├── neon-rounding-shift.s.cs + │ │ │ │ ├── neon-saturating-add-sub.s.cs + │ │ │ │ ├── neon-saturating-rounding-shift.s.cs + │ │ │ │ ├── neon-saturating-shift.s.cs + │ │ │ │ ├── neon-scalar-abs.s.cs + │ │ │ │ ├── neon-scalar-add-sub.s.cs + │ │ │ │ ├── neon-scalar-by-elem-mla.s.cs + │ │ │ │ ├── neon-scalar-by-elem-mul.s.cs + │ │ │ │ ├── neon-scalar-by-elem-saturating-mla.s.cs + │ │ │ │ ├── neon-scalar-by-elem-saturating-mul.s.cs + │ │ │ │ ├── neon-scalar-compare.s.cs + │ │ │ │ ├── neon-scalar-cvt.s.cs + │ │ │ │ ├── neon-scalar-dup.s.cs + │ │ │ │ ├── neon-scalar-extract-narrow.s.cs + │ │ │ │ ├── neon-scalar-fp-compare.s.cs + │ │ │ │ ├── neon-scalar-mul.s.cs + │ │ │ │ ├── neon-scalar-neg.s.cs + │ │ │ │ ├── neon-scalar-recip.s.cs + │ │ │ │ ├── neon-scalar-reduce-pairwise.s.cs + │ │ │ │ ├── neon-scalar-rounding-shift.s.cs + │ │ │ │ ├── neon-scalar-saturating-add-sub.s.cs + │ │ │ │ ├── neon-scalar-saturating-rounding-shift.s.cs + │ │ │ │ ├── neon-scalar-saturating-shift.s.cs + │ │ │ │ ├── neon-scalar-shift-imm.s.cs + │ │ │ │ ├── neon-scalar-shift.s.cs + │ │ │ │ ├── neon-shift-left-long.s.cs + │ │ │ │ ├── neon-shift.s.cs + │ │ │ │ ├── neon-simd-copy.s.cs + │ │ │ │ ├── neon-simd-ldst-multi-elem.s.cs + │ │ │ │ ├── neon-simd-ldst-one-elem.s.cs + │ │ │ │ ├── neon-simd-misc.s.cs + │ │ │ │ ├── neon-simd-post-ldst-multi-elem.s.cs + │ │ │ │ ├── neon-simd-shift.s.cs + │ │ │ │ ├── neon-tbl.s.cs + │ │ │ │ └── trace-regs.s.cs + │ │ │ ├── ARM + │ │ │ │ ├── arm-aliases.s.cs + │ │ │ │ ├── arm-arithmetic-aliases.s.cs + │ │ │ │ ├── arm-it-block.s.cs + │ │ │ │ ├── arm-memory-instructions.s.cs + │ │ │ │ ├── arm-shift-encoding.s.cs + │ │ │ │ ├── arm-thumb-trustzone.s.cs + │ │ │ │ ├── arm-trustzone.s.cs + │ │ │ │ ├── arm_addrmode2.s.cs + │ │ │ │ ├── arm_addrmode3.s.cs + │ │ │ │ ├── arm_instructions.s.cs + │ │ │ │ ├── basic-arm-instructions-v8.s.cs + │ │ │ │ ├── basic-arm-instructions.s.cs + │ │ │ │ ├── basic-thumb-instructions.s.cs + │ │ │ │ ├── basic-thumb2-instructions-v8.s.cs + │ │ │ │ ├── basic-thumb2-instructions.s.cs + │ │ │ │ ├── crc32-thumb.s.cs + │ │ │ │ ├── crc32.s.cs + │ │ │ │ ├── dot-req.s.cs + │ │ │ │ ├── fp-armv8.s.cs + │ │ │ │ ├── idiv-thumb.s.cs + │ │ │ │ ├── idiv.s.cs + │ │ │ │ ├── load-store-acquire-release-v8-thumb.s.cs + │ │ │ │ ├── load-store-acquire-release-v8.s.cs + │ │ │ │ ├── mode-switch.s.cs + │ │ │ │ ├── neon-abs-encoding.s.cs + │ │ │ │ ├── neon-absdiff-encoding.s.cs + │ │ │ │ ├── neon-add-encoding.s.cs + │ │ │ │ ├── neon-bitcount-encoding.s.cs + │ │ │ │ ├── neon-bitwise-encoding.s.cs + │ │ │ │ ├── neon-cmp-encoding.s.cs + │ │ │ │ ├── neon-convert-encoding.s.cs + │ │ │ │ ├── neon-crypto.s.cs + │ │ │ │ ├── neon-dup-encoding.s.cs + │ │ │ │ ├── neon-minmax-encoding.s.cs + │ │ │ │ ├── neon-mov-encoding.s.cs + │ │ │ │ ├── neon-mul-accum-encoding.s.cs + │ │ │ │ ├── neon-mul-encoding.s.cs + │ │ │ │ ├── neon-neg-encoding.s.cs + │ │ │ │ ├── neon-pairwise-encoding.s.cs + │ │ │ │ ├── neon-reciprocal-encoding.s.cs + │ │ │ │ ├── neon-reverse-encoding.s.cs + │ │ │ │ ├── neon-satshift-encoding.s.cs + │ │ │ │ ├── neon-shift-encoding.s.cs + │ │ │ │ ├── neon-shiftaccum-encoding.s.cs + │ │ │ │ ├── neon-shuffle-encoding.s.cs + │ │ │ │ ├── neon-sub-encoding.s.cs + │ │ │ │ ├── neon-table-encoding.s.cs + │ │ │ │ ├── neon-v8.s.cs + │ │ │ │ ├── neon-vld-encoding.s.cs + │ │ │ │ ├── neon-vst-encoding.s.cs + │ │ │ │ ├── neon-vswp.s.cs + │ │ │ │ ├── neont2-abs-encoding.s.cs + │ │ │ │ ├── neont2-absdiff-encoding.s.cs + │ │ │ │ ├── neont2-add-encoding.s.cs + │ │ │ │ ├── neont2-bitcount-encoding.s.cs + │ │ │ │ ├── neont2-bitwise-encoding.s.cs + │ │ │ │ ├── neont2-cmp-encoding.s.cs + │ │ │ │ ├── neont2-convert-encoding.s.cs + │ │ │ │ ├── neont2-dup-encoding.s.cs + │ │ │ │ ├── neont2-minmax-encoding.s.cs + │ │ │ │ ├── neont2-mov-encoding.s.cs + │ │ │ │ ├── neont2-mul-accum-encoding.s.cs + │ │ │ │ ├── neont2-mul-encoding.s.cs + │ │ │ │ ├── neont2-neg-encoding.s.cs + │ │ │ │ ├── neont2-pairwise-encoding.s.cs + │ │ │ │ ├── neont2-reciprocal-encoding.s.cs + │ │ │ │ ├── neont2-reverse-encoding.s.cs + │ │ │ │ ├── neont2-satshift-encoding.s.cs + │ │ │ │ ├── neont2-shift-encoding.s.cs + │ │ │ │ ├── neont2-shiftaccum-encoding.s.cs + │ │ │ │ ├── neont2-shuffle-encoding.s.cs + │ │ │ │ ├── neont2-sub-encoding.s.cs + │ │ │ │ ├── neont2-table-encoding.s.cs + │ │ │ │ ├── neont2-vld-encoding.s.cs + │ │ │ │ ├── neont2-vst-encoding.s.cs + │ │ │ │ ├── simple-fp-encoding.s.cs + │ │ │ │ ├── thumb-fp-armv8.s.cs + │ │ │ │ ├── thumb-hints.s.cs + │ │ │ │ ├── thumb-neon-crypto.s.cs + │ │ │ │ ├── thumb-neon-v8.s.cs + │ │ │ │ ├── thumb-shift-encoding.s.cs + │ │ │ │ ├── thumb.s.cs + │ │ │ │ ├── thumb2-b.w-encodingT4.s.cs + │ │ │ │ ├── thumb2-branches.s.cs + │ │ │ │ ├── thumb2-mclass.s.cs + │ │ │ │ ├── thumb2-narrow-dp.ll.cs + │ │ │ │ ├── thumb2-pldw.s.cs + │ │ │ │ ├── vfp4-thumb.s.cs + │ │ │ │ ├── vfp4.s.cs + │ │ │ │ ├── vpush-vpop-thumb.s.cs + │ │ │ │ └── vpush-vpop.s.cs + │ │ │ ├── Mips + │ │ │ │ ├── hilo-addressing.s.cs + │ │ │ │ ├── micromips-alu-instructions-EB.s.cs + │ │ │ │ ├── micromips-alu-instructions.s.cs + │ │ │ │ ├── micromips-branch-instructions-EB.s.cs + │ │ │ │ ├── micromips-branch-instructions.s.cs + │ │ │ │ ├── micromips-expansions.s.cs + │ │ │ │ ├── micromips-jump-instructions-EB.s.cs + │ │ │ │ ├── micromips-jump-instructions.s.cs + │ │ │ │ ├── micromips-loadstore-instructions-EB.s.cs + │ │ │ │ ├── micromips-loadstore-instructions.s.cs + │ │ │ │ ├── micromips-loadstore-unaligned-EB.s.cs + │ │ │ │ ├── micromips-loadstore-unaligned.s.cs + │ │ │ │ ├── micromips-movcond-instructions-EB.s.cs + │ │ │ │ ├── micromips-movcond-instructions.s.cs + │ │ │ │ ├── micromips-multiply-instructions-EB.s.cs + │ │ │ │ ├── micromips-multiply-instructions.s.cs + │ │ │ │ ├── micromips-shift-instructions-EB.s.cs + │ │ │ │ ├── micromips-shift-instructions.s.cs + │ │ │ │ ├── micromips-trap-instructions-EB.s.cs + │ │ │ │ ├── micromips-trap-instructions.s.cs + │ │ │ │ ├── mips-alu-instructions.s.cs + │ │ │ │ ├── mips-control-instructions-64.s.cs + │ │ │ │ ├── mips-control-instructions.s.cs + │ │ │ │ ├── mips-coprocessor-encodings.s.cs + │ │ │ │ ├── mips-dsp-instructions.s.cs + │ │ │ │ ├── mips-expansions.s.cs + │ │ │ │ ├── mips-fpu-instructions.s.cs + │ │ │ │ ├── mips-jump-instructions.s.cs + │ │ │ │ ├── mips-memory-instructions.s.cs + │ │ │ │ ├── mips-register-names.s.cs + │ │ │ │ ├── mips64-alu-instructions.s.cs + │ │ │ │ ├── mips64-instructions.s.cs + │ │ │ │ ├── mips64-register-names.s.cs + │ │ │ │ ├── mips_directives.s.cs + │ │ │ │ ├── nabi-regs.s.cs + │ │ │ │ ├── set-at-directive.s.cs + │ │ │ │ ├── test_2r.s.cs + │ │ │ │ ├── test_2rf.s.cs + │ │ │ │ ├── test_3r.s.cs + │ │ │ │ ├── test_3rf.s.cs + │ │ │ │ ├── test_bit.s.cs + │ │ │ │ ├── test_cbranch.s.cs + │ │ │ │ ├── test_ctrlregs.s.cs + │ │ │ │ ├── test_elm.s.cs + │ │ │ │ ├── test_elm_insert.s.cs + │ │ │ │ ├── test_elm_insve.s.cs + │ │ │ │ ├── test_i10.s.cs + │ │ │ │ ├── test_i5.s.cs + │ │ │ │ ├── test_i8.s.cs + │ │ │ │ ├── test_lsa.s.cs + │ │ │ │ ├── test_mi10.s.cs + │ │ │ │ └── test_vec.s.cs + │ │ │ ├── PowerPC + │ │ │ │ ├── ppc64-encoding-bookII.s.cs + │ │ │ │ ├── ppc64-encoding-bookIII.s.cs + │ │ │ │ ├── ppc64-encoding-ext.s.cs + │ │ │ │ ├── ppc64-encoding-fp.s.cs + │ │ │ │ ├── ppc64-encoding-vmx.s.cs + │ │ │ │ ├── ppc64-encoding.s.cs + │ │ │ │ └── ppc64-operands.s.cs + │ │ │ ├── README + │ │ │ ├── Sparc + │ │ │ │ ├── sparc-alu-instructions.s.cs + │ │ │ │ ├── sparc-atomic-instructions.s.cs + │ │ │ │ ├── sparc-ctrl-instructions.s.cs + │ │ │ │ ├── sparc-fp-instructions.s.cs + │ │ │ │ ├── sparc-mem-instructions.s.cs + │ │ │ │ ├── sparc-vis.s.cs + │ │ │ │ ├── sparc64-alu-instructions.s.cs + │ │ │ │ ├── sparc64-ctrl-instructions.s.cs + │ │ │ │ ├── sparcv8-instructions.s.cs + │ │ │ │ └── sparcv9-instructions.s.cs + │ │ │ ├── SystemZ + │ │ │ │ ├── insn-good-z196.s.cs + │ │ │ │ ├── insn-good.s.cs + │ │ │ │ └── regs-good.s.cs + │ │ │ └── X86 + │ │ │ ├── 3DNow.s.cs + │ │ │ ├── address-size.s.cs + │ │ │ ├── avx512-encodings.s.cs + │ │ │ ├── intel-syntax-encoding.s.cs + │ │ │ ├── x86-32-avx.s.cs + │ │ │ ├── x86-32-fma3.s.cs + │ │ │ ├── x86-32-ms-inline-asm.s.cs + │ │ │ ├── x86_64-avx-clmul-encoding.s.cs + │ │ │ ├── x86_64-avx-encoding.s.cs + │ │ │ ├── x86_64-bmi-encoding.s.cs + │ │ │ ├── x86_64-encoding.s.cs + │ │ │ ├── x86_64-fma3-encoding.s.cs + │ │ │ ├── x86_64-fma4-encoding.s.cs + │ │ │ ├── x86_64-hle-encoding.s.cs + │ │ │ ├── x86_64-imm-widths.s.cs + │ │ │ ├── x86_64-rand-encoding.s.cs + │ │ │ ├── x86_64-rtm-encoding.s.cs + │ │ │ ├── x86_64-sse4a.s.cs + │ │ │ ├── x86_64-tbm-encoding.s.cs + │ │ │ └── x86_64-xop-encoding.s.cs + │ │ ├── README + │ │ ├── arm + │ │ │ ├── Makefile + │ │ │ └── test_arm_regression.c + │ │ ├── autogen_x86imm.py + │ │ ├── benchmark + │ │ │ ├── Makefile + │ │ │ └── test_iter_benchmark.c + │ │ ├── benchmark.py + │ │ ├── capstone_get_setup.c + │ │ ├── compile_all.sh + │ │ ├── disasm_mc.py + │ │ ├── disasm_mc.sh + │ │ ├── fuzz + │ │ │ ├── Makefile + │ │ │ ├── README + │ │ │ ├── driverbin.c + │ │ │ ├── drivermc.c + │ │ │ ├── fuzz_diff.c + │ │ │ ├── fuzz_disasm.c + │ │ │ ├── fuzz_disasm.options + │ │ │ ├── fuzz_harness.c + │ │ │ ├── fuzz_llvm.cpp + │ │ │ └── onefile.c + │ │ ├── fuzz.py + │ │ ├── patch_major_os_version.py + │ │ ├── ppcbranch.py + │ │ ├── python_capstone_setup.py + │ │ ├── regress + │ │ │ ├── LICENSE + │ │ │ ├── Makefile + │ │ │ ├── invalid_read_in_print_operand.c + │ │ │ └── regress.py + │ │ ├── regress.py + │ │ ├── test_all.sh + │ │ ├── test_c.sh + │ │ ├── test_corpus.py + │ │ ├── test_group_name.py + │ │ ├── test_mc.py + │ │ ├── test_mc.sh + │ │ ├── test_python.sh + │ │ ├── x86 + │ │ │ ├── README + │ │ │ └── verify + │ │ │ └── README + │ │ └── x86odd.py + │ ├── tests + │ │ ├── Makefile + │ │ ├── README + │ │ ├── test_arm.c + │ │ ├── test_arm64.c + │ │ ├── test_basic.c + │ │ ├── test_customized_mnem.c + │ │ ├── test_detail.c + │ │ ├── test_evm.c + │ │ ├── test_iter.c + │ │ ├── test_m680x.c + │ │ ├── test_m68k.c + │ │ ├── test_mips.c + │ │ ├── test_ppc.c + │ │ ├── test_skipdata.c + │ │ ├── test_sparc.c + │ │ ├── test_systemz.c + │ │ ├── test_tms320c64x.c + │ │ ├── test_winkernel.cpp + │ │ ├── test_x86.c + │ │ └── test_xcore.c + │ ├── utils.c + │ ├── utils.h + │ ├── windows + │ │ ├── README + │ │ ├── winkernel_mm.c + │ │ └── winkernel_mm.h + │ ├── windowsce + │ │ ├── COMPILE.md + │ │ ├── make_windowsce7-armv7.bat + │ │ └── make_windowsce8-armv7.bat + │ └── xcode + │ ├── Capstone.xcodeproj + │ │ ├── project.pbxproj + │ │ ├── project.xcworkspace + │ │ │ └── contents.xcworkspacedata + │ │ └── xcshareddata + │ │ └── xcschemes + │ │ ├── Dynamic Library.xcscheme + │ │ ├── Framework.xcscheme + │ │ ├── Static Library.xcscheme + │ │ └── Tests.xcscheme + │ ├── CapstoneFramework + │ │ ├── Info.plist + │ │ └── module.modulemap + │ └── README.md + └── fmt + ├── CMakeLists.txt + ├── CONTRIBUTING.md + ├── ChangeLog.md + ├── LICENSE + ├── README.md + ├── include + │ └── fmt + │ ├── args.h + │ ├── base.h + │ ├── chrono.h + │ ├── color.h + │ ├── compile.h + │ ├── core.h + │ ├── format-inl.h + │ ├── format.h + │ ├── os.h + │ ├── ostream.h + │ ├── printf.h + │ ├── ranges.h + │ ├── std.h + │ └── xchar.h + └── src + ├── fmt.cc + ├── format.cc + └── os.cc + +``` + +`LICENSE`: + +``` +MIT License + +Copyright (c) 2021 Axel Souchet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +`README.md`: + +```md +# rp++: a fast ROP gadget finder for PE/ELF/Mach-O x86/x64/ARM/ARM64 binaries + +![Builds](https://github.com/0vercl0k/rp/workflows/Builds/badge.svg) + +## Overview + +**rp++** or **rp** is a C++ [ROP](https://en.wikipedia.org/wiki/Return-oriented_programming) gadget finder for [PE](https://docs.microsoft.com/en-us/windows/win32/debug/pe-format)/[ELF](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format)/[Mach-O](https://en.wikipedia.org/wiki/Mach-O) executables and x86/x64/ARM/ARM64 architectures. + +

+ +

+ +## Finding ROP gadgets + +To find ROP gadget you need to specify a file with the `--file` / `-f` option and use the `--rop` / `-r` option specifying the maximum the number of instructions in the gadget: + +

+ +

+ +You can customize the base address of the module with the `--va` option (if you pass a base of `0`, then you get relative offsets) and you can also use the `--raw` option to analyze raw code dumps. + +## Finding pointers + +Oftentimes when building ROP chains, you might need to find pointers to integers with specific values. To look for those, you can use the `--search-int` option like in the below: + +

+ +

+ +Other times, you might need to find pointers to specific strings. To look for those, you can use the `--search-hexa` option like in the below: + +

+ +

+ +You can also use the `--va` option to specify your own base address. + +## Build + +You can find shell scripts in [src/build](src/build) for every supported platforms; below is the Linux example: + +``` +src/build$ chmod u+x ./build-release.sh && ./build-release.sh +-- The C compiler identification is GNU 9.3.0 +-- The CXX compiler identification is GNU 9.3.0 +[...] +[16/16] Linking CXX executable rp-lin-x64 +``` + +## Authors + +* Axel '[0vercl0k](https://twitter.com/0vercl0k)' Souchet + +``` + +`src/CMakeLists.txt`: + +```txt +# Axel '0vercl0k' Souchet - January 13 2022 +cmake_minimum_required(VERSION 3.16.0) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + +project(rp++) +include_directories(${CMAKE_CURRENT_LIST_DIR}/third_party/fmt/include) +include_directories(${CMAKE_CURRENT_LIST_DIR}/third_party/beaengine/include) +include_directories(${CMAKE_CURRENT_LIST_DIR}/third_party/capstone/include) +include_directories(${CMAKE_CURRENT_LIST_DIR}/third_party/CLI11) + +add_compile_definitions(FMT_HEADER_ONLY FMT_UNICODE=0 BEA_ENGINE_STATIC CAPSTONE_HAS_ARM CAPSTONE_HAS_ARM64 CAPSTONE_USE_SYS_DYN_MEM) + +if(WIN32) + set(RP_PLAT "win") + add_compile_definitions(NOMINMAX) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +elseif(UNIX) + if(${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD) + set(RP_PLAT "fbsd") + elseif(${CMAKE_SYSTEM_NAME} STREQUAL Darwin) + set(RP_PLAT "osx") + else() + set(RP_PLAT "lin") + endif() + endif() + +set(RP_PLAT "rp-${RP_PLAT}") + +file( + GLOB + rp_files + rp/*.cpp + third_party/capstone/arch/AArch64/*.c + third_party/capstone/arch/ARM/*.c + third_party/capstone/*.c +) + +add_executable( + ${RP_PLAT} + ${rp_files} + "third_party/beaengine/src/BeaEngine.c" +) + +if(UNIX) + find_package(Threads) + target_link_libraries(${RP_PLAT} ${CMAKE_THREAD_LIBS_INIT}) + if(${RP_PLAT} STREQUAL "rp-osx") + else() + target_link_libraries(${RP_PLAT} -static) + endif() +endif() + +``` + +`src/rp/arm.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "cpu.hpp" + +#include "disassenginewrapper.hpp" +#include "ropsearch_algorithm.hpp" +#include "rpexception.hpp" +#include +#include + +class ArmCapstone : public DisassEngineWrapper { +public: + explicit ArmCapstone(const uint32_t thumb_mode) : is_thumb(true) { + cs_mode mode = CS_MODE_THUMB; + if (thumb_mode == 0) { + mode = CS_MODE_ARM; + is_thumb = false; + } + + if (cs_open(CS_ARCH_ARM, mode, &m_handle) != CS_ERR_OK) { + RAISE_EXCEPTION("Apparently no support for ARM in capstone.lib"); + } + + cs_option(m_handle, CS_OPT_DETAIL, CS_OPT_ON); + } + + ~ArmCapstone() override { cs_close(&m_handle); } + InstructionInformation disass(const uint8_t *data, uint64_t len, + const uint64_t vaddr, + DisassEngineReturn &ret) override { + cs_insn *insn = nullptr; + const size_t count = + cs_disasm(m_handle, data, size_t(len), vaddr, 1, &insn); + if (count != 1) { + ret = UnknownInstruction; + return {}; + } + + std::string mnemonic(insn[0].mnemonic); + InstructionInformation instr; + instr.address = uintptr_t(data); + instr.virtual_address_in_memory = uintptr_t(vaddr); + instr.disassembly = mnemonic + ' ' + std::string(insn[0].op_str); + instr.size = insn[0].size; + ret = AllRight; + + if (insn[0].detail == nullptr) { + cs_free(insn, count); + return instr; + } + + if (cs_insn_group(m_handle, insn, ARM_GRP_JUMP)) { + instr.is_branch = true; + instr.is_valid_ending_instr = + insn[0].detail->arm.op_count == 1 && + insn[0].detail->arm.operands[0].type != ARM_OP_IMM; + } else if (mnemonic == "b" || mnemonic == "bl" || mnemonic == "blx" || + mnemonic == "cb" || mnemonic == "cbz") { + instr.is_branch = true; + } else if (mnemonic == "swi" || mnemonic == "svc") { + instr.is_branch = true; + instr.is_valid_ending_instr = true; + } else if (mnemonic == "mov" && insn[0].detail->arm.op_count >= 1 && + insn[0].detail->arm.operands[0].type == ARM_OP_REG && + insn[0].detail->arm.operands[0].reg == ARM_REG_PC) { + instr.is_branch = true; + instr.is_valid_ending_instr = true; + } else if (mnemonic == "bx") { + instr.is_branch = true; + instr.is_valid_ending_instr = + insn[0].detail->arm.operands[0].type == ARM_OP_REG; + } else if (mnemonic == "blx") { + instr.is_branch = true; + instr.is_valid_ending_instr = true; + } else if (mnemonic == "pop") { + bool has_pc = false; + for (size_t i = 0; i < insn[0].detail->arm.op_count; ++i) { + if (insn[0].detail->arm.operands[i].type == ARM_OP_REG && + insn[0].detail->arm.operands[i].reg == ARM_REG_PC) { + has_pc = true; + break; + } + } + + if (has_pc) { + instr.is_branch = true; + instr.is_valid_ending_instr = true; + } + } + + cs_free(insn, count); + return instr; + } + + uint32_t get_size_biggest_instruction() const override { return 4; } + + uint32_t get_alignement() const override { + if (is_thumb) { + return 2; + } + + return 4; + } + +private: + csh m_handle = {}; + bool is_thumb = false; +}; + +class ARM : public CPU { +public: + std::string get_class_name() const override { return "ARM"; } + + void find_gadget_in_memory(const std::vector &p_memory, + const uint64_t vaddr, const uint32_t depth, + GadgetMultiset &gadgets, + uint32_t disass_engine_options, + std::mutex &m) override { + ArmCapstone capstone_engine(disass_engine_options); + find_rop_gadgets(p_memory, vaddr, depth, gadgets, capstone_engine, m); + } +}; + +``` + +`src/rp/arm64.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - February 7 2022 +#pragma once + +#include "cpu.hpp" + +#include "disassenginewrapper.hpp" +#include "ropsearch_algorithm.hpp" +#include "rpexception.hpp" +#include +#include + +class Arm64Capstone : public DisassEngineWrapper { +public: + Arm64Capstone() { + if (cs_open(CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN, &m_handle) != CS_ERR_OK) { + RAISE_EXCEPTION("Apparently no support for ARM64 in capstone.lib"); + } + + cs_option(m_handle, CS_OPT_DETAIL, CS_OPT_ON); + } + + ~Arm64Capstone() override { cs_close(&m_handle); } + InstructionInformation disass(const uint8_t *data, uint64_t len, + const uint64_t vaddr, + DisassEngineReturn &ret) override { + cs_insn *insn = nullptr; + const size_t count = + cs_disasm(m_handle, data, size_t(len), vaddr, 1, &insn); + if (count != 1) { + ret = UnknownInstruction; + return {}; + } + + std::string mnemonic(insn[0].mnemonic); + InstructionInformation instr; + instr.address = uintptr_t(data); + instr.virtual_address_in_memory = uintptr_t(vaddr); + instr.disassembly = mnemonic + ' ' + std::string(insn[0].op_str); + instr.size = insn[0].size; + ret = AllRight; + + if (insn[0].detail == nullptr) { + cs_free(insn, count); + std::abort(); + } + + const bool Jump = cs_insn_group(m_handle, insn, ARM64_GRP_JUMP); + const bool Call = cs_insn_group(m_handle, insn, ARM64_GRP_CALL); + const bool Ret = cs_insn_group(m_handle, insn, ARM64_GRP_RET); + const bool Int = cs_insn_group(m_handle, insn, ARM64_GRP_INT); + instr.is_branch = Jump || Call || Ret || Int; + instr.is_valid_ending_instr = + Ret || Int || + ((Jump || Call) && insn[0].detail->arm64.op_count == 1 && + insn[0].detail->arm64.operands[0].type != ARM64_OP_IMM); + + cs_free(insn, count); + return instr; + } + + uint32_t get_size_biggest_instruction() const override { return 4; } + + uint32_t get_alignement() const override { return 4; } + +private: + csh m_handle = {}; +}; + +class ARM64 : public CPU { +public: + std::string get_class_name() const override { return "ARM64"; } + + void find_gadget_in_memory(const std::vector &p_memory, + const uint64_t vaddr, const uint32_t depth, + GadgetMultiset &gadgets, + uint32_t disass_engine_options, + std::mutex &m) override { + Arm64Capstone capstone_engine; + find_rop_gadgets(p_memory, vaddr, depth, gadgets, capstone_engine, m); + } +}; + +``` + +`src/rp/coloshell.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "platform.h" +#include "rpexception.hpp" +#include +#include + +#ifdef WINDOWS +#include +#endif + +static bool g_colors_desired = false; + +// Here you will find all you need to display the data in a cute way on a +// windows/unix terminal + +#ifdef WINDOWS +enum Colors { + COLO_RED = FOREGROUND_RED | FOREGROUND_INTENSITY, + COLO_GREEN = FOREGROUND_GREEN | FOREGROUND_INTENSITY, + COLO_YELLOW = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, + COLO_DEFAULT = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED +}; + +#else +enum Colors { COLO_RED = 0, COLO_GREEN = 1, COLO_YELLOW = 2, COLO_DEFAULT = 3 }; + +#endif + +inline bool g_are_colors_enabled() { return g_colors_desired; } + +/** + * \fn static void enable_color_(const Colors colo) + * \brief Enable a color in your shell + * + * \param colo: the color you want to activate + */ +static inline void enable_color_(const Colors colo) { +#ifdef WINDOWS + HANDLE hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + if (hStdOutput == INVALID_HANDLE_VALUE) { + RAISE_EXCEPTION("Cannot find a STD_OUTPUT_HANDLE valid value"); + } + + SetConsoleTextAttribute(hStdOutput, uint16_t(colo)); +#else + const char *colors[] = {"\x1b[91m", "\x1b[92m", "\x1b[93m", "\x1b[0m"}; + fmt::print("{}", colors[colo]); +#endif +} + +/** + * \fn static void enable_color(const Colors colo) + * \brief Enable a color in your shell + * + * \param colo: the color you want to activate + */ +static inline void enable_color(const Colors colo) { + if (!g_are_colors_enabled()) { + return; + } + + enable_color_(colo); +} + +/** + * \fn static void disable_color_(const Colors colo) + * \brief Unset the color you have previously set + */ +static void disable_color_() { +#ifdef WINDOWS + HANDLE hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + if (hStdOutput == INVALID_HANDLE_VALUE) { + RAISE_EXCEPTION("Cannot find a STD_OUTPUT_HANDLE valid value"); + } + + SetConsoleTextAttribute(hStdOutput, COLO_DEFAULT); +#else + const char *colors[]{"\x1b[91m", "\x1b[92m", "\x1b[93m", "\x1b[0m"}; + fmt::print("{}", colors[COLO_DEFAULT]); +#endif +} + +/** + * \fn static void disable_color_(const Colors colo) + * \brief Unset the color you have previously set + */ +static inline void disable_color() { + if (!g_are_colors_enabled()) { + return; + } + + disable_color_(); +} + +/** + * \fn static void coloshell(const T t, const Colors colo) + * \brief Display "t" on the stdout with the color "colo" + * + * \param t: the thing you want to output + * \param colo: the color you want to enable + */ +template static void coloshell(const T t, const Colors colo) { + enable_color(colo); + fmt::print("{}", t); + disable_color(); +} + +/** + * \def w_red(text) + * Display text in red on stdout + * + * \param text: the text you want to display + */ +#define w_red(text) \ + { \ + coloshell(text, COLO_RED); \ + } + +/** + * \def w_yel(text) + * Display text in yellow on stdout + * + * \param text: the text you want to display + */ +#define w_yel(text) \ + { \ + coloshell(text, COLO_YELLOW); \ + } + +/** + * \def w_gre(text) + * Display text in green on stdout + * + * \param text: the text you want to display + */ +#define w_gre(text) \ + { \ + coloshell(text, COLO_GREEN); \ + } + +/** + * \def w_red_lf(text) + * Display text in red on stdout with a line feed + * + * \param text: the text you want to display + */ +#define w_red_lf(text) \ + { \ + w_red(text); \ + fmt::print("\n"); \ + } + +/** + * \def w_yel_lf(text) + * Display text in yellow on stdout with a line feed + * + * \param text: the text you want to display + */ +#define w_yel_lf(text) \ + { \ + w_yel(text); \ + fmt::print("\n"); \ + } + +/** + * \def w_gre_lf(text) + * Display text in green on stdout with a line feed + * + * \param text: the text you want to display + */ +#define w_gre_lf(text) \ + { \ + w_gre_lf(text); \ + fmt::print("\n"); \ + } + +/** + * \def display_hex_field(field, n) + * Display an hex value in red with 0 filling etc. + * + * \param field: It is the hex value you want to output + * \param n: It is the size of the column + */ +#define display_hex_field(field, n) \ + { \ + fmt::print("{:{}}: ", #field, n); \ + enable_color(COLO_RED); \ + fmt::print("0x{:0{}x} ", field, sizeof(field) * 2); \ + disable_color(); \ + } + +/** + * \def display_hex_field_lf(field, n) + * Display an hex value in red with 0 filling, a line feed and 25b column. + * + * \param field: It is the hex value you want to output + */ +#define display_hex_field_lf(field) \ + { \ + display_hex_field(field, 25); \ + fmt::print("\n"); \ + } + +/** + * \def display_hex_2fields_lf(field1, field2) + * Display two hex values in red on a same line. + * + * \param field1: It is the first hex value you want to output + * \param field2: It is the second hex value you want to output + */ +#define display_hex_2fields_lf(field1, field2) \ + { \ + display_hex_field(field1, 25); \ + display_hex_field(field2, 25); \ + fmt::print("\n"); \ + } + +/** + * \def display_short_hex_field(field) + * Display a short hex value in red. + * + * \param field: It is the short hex value you want to output + */ +#define display_short_hex_field(field) \ + { \ + display_hex_field(field, 14); \ + } + +/** + * \def display_short_hex_field_lf(field) + * Display a short hex value in red with a line feed. + * + * \param field: It is the short hex value you want to output + */ +#define display_short_hex_field_lf(field) \ + { \ + display_short_hex_field(field); \ + fmt::print("\n"); \ + } + +/** + * \def display_short_hex_2fields_lf(field1, field2) + * Display two short hex values in red with a line feed. + * + * \param field1: It is the first short hex value you want to output + * \param field2: It is the second short hex value you want to output + */ +#define display_short_hex_2fields_lf(field1, field2) \ + { \ + display_short_hex_field(field1); \ + display_short_hex_field(field2); \ + fmt::print("\n"); \ + } + +/** + * \def display_gadget_lf(va, gadget) + * Display a gadget with a line feed and its VA. + * + * \param va: It is the gadget VA + * \param gadget: It is the gadget you want to output + */ +#define display_gadget_lf(va, gadget) \ + { \ + enable_color(COLO_RED); \ + fmt::print("0x{:x}", va); \ + disable_color(); \ + fmt::print(": "); \ + enable_color(COLO_GREEN); \ + (gadget).print_disassembly(); \ + if (g_opts.print_bytes) { \ + fmt::print(" "); \ + (gadget).print_bytes(); \ + } \ + fmt::print(" ({} found)\n", (gadget).get_nb()); \ + disable_color(); \ + } + +/** + * \def display_gadget_lf_badbytes(va, gadget) + * Display a gadget with a line feed and its VA. + * + * \param va: It is the gadget VA + * \param gadget: It is the gadget you want to output + */ +#define display_gadget_lf_badbytes(va, gadget) \ + { \ + if (!does_badbytes_filter_apply(va, badbyte_list)) { \ + display_gadget_lf(va, gadget); \ + } else { \ + nb_gadgets_filtered++; \ + } \ + } + +/** + * \def display_offset_lf(va, hex_val, size) + * Display an offset with a line feed and the hex values. + * + * \param va: It is the gadget VA + * \param hex_val: It is the hex values + * \param size: It is the size of the hex values + */ +#define display_offset_lf(va, hex_val, size) \ + { \ + enable_color(COLO_RED); \ + fmt::print("0x{:x}", va); \ + disable_color(); \ + fmt::print(": "); \ + enable_color(COLO_GREEN); \ + for (uint32_t i = 0; i < size; ++i) { \ + if (isprint(hex_val[i])) { \ + fmt::print("{:c}", hex_val[i]); \ + } else { \ + fmt::print("\\x{:02x}", hex_val[i]); \ + } \ + } \ + fmt::print("\n"); \ + disable_color(); \ + } + +``` + +`src/rp/cpu.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "gadget.hpp" +#include +#include +#include + +/*! \class CPU + * + * A CPU is an important class that compose a part of the Program class. + */ +class CPU { +public: + virtual ~CPU() = default; + + /*! + * \brief Obtain the name of the class (useful when you use the polymorphism) + * + * \return the name of the class + */ + virtual std::string get_class_name() const = 0; + + /*! + * \brief Each CPU class is able to find all gadgets in [p_memory, + * p_memory+size] NB: The vaddr field is actually used by the BeaEngine when + * it disassembles something like jmp instruction, it needs the original + * virtual address to give you disassemble correctly (indeed jmp instruction + * are relative) + * + * \param p_memory: It is a pointer on the memory where you want to find rop + * gadget \param size: It is the size of the p_memory \param vaddr: It is the + * real virtual address of the memory which will be disassembled (see the + * previous remark) \param depth: It is the number of maximum instructions + * contained by a gadget \param gadgets: A list of the Gadget instance \param + * disass_engine_options: Options you want to pass to the disassembly engine + * + */ + virtual void find_gadget_in_memory(const std::vector &p_memory, + const uint64_t vaddr, const uint32_t depth, + GadgetMultiset &gadgets, + uint32_t disass_engine_options, + std::mutex &m) = 0; + + /*! The different architectures RP++ handles */ + enum E_CPU { + CPU_x86, /*!< x86 */ + CPU_x64, /*!< x64 */ + CPU_ARM, /*!< ARM */ + CPU_ARM64, /*!< ARM64 */ + CPU_UNKNOWN /*!< unknown cpu */ + }; +}; + +``` + +`src/rp/disassenginewrapper.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include +#include +#include + +struct InstructionInformation { + std::string disassembly; + std::string mnemonic; + uint32_t size = 0; + uintptr_t address = 0; + uintptr_t virtual_address_in_memory = 0; + std::vector bytes; + bool is_branch = false; + bool is_valid_ending_instr = false; +}; + +enum DisassEngineReturn { UnknownInstruction, OutOfBlock, AllRight }; + +class DisassEngineWrapper { +public: + DisassEngineWrapper() = default; + virtual ~DisassEngineWrapper() = default; + + DisassEngineWrapper(const DisassEngineWrapper &) = delete; + DisassEngineWrapper &operator=(const DisassEngineWrapper &) = delete; + DisassEngineWrapper(DisassEngineWrapper &&) = delete; + DisassEngineWrapper &operator=(DisassEngineWrapper &&) = delete; + + virtual InstructionInformation disass(const uint8_t *data, uint64_t len, + const uint64_t vaddr, + DisassEngineReturn &ret) = 0; + virtual uint32_t get_size_biggest_instruction() const = 0; + virtual uint32_t get_alignement() const = 0; +}; + +``` + +`src/rp/elf.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "executable_format.hpp" + +#include "arm.hpp" +#include "arm64.hpp" +#include "elf_struct.hpp" +#include "rpexception.hpp" +#include "x64.hpp" +#include "x86.hpp" + +class Elf : public ExecutableFormat { +public: + std::unique_ptr get_cpu(std::ifstream &file) override { + CPU::E_CPU cpu_type = extract_information_from_binary(file); + switch (cpu_type) { + case CPU::CPU_x86: { + return std::make_unique(); + } + + case CPU::CPU_x64: { + return std::make_unique(); + } + + case CPU::CPU_ARM: { + return std::make_unique(); + } + + case CPU::CPU_ARM64: { + return std::make_unique(); + } + + default: { + RAISE_EXCEPTION("Cannot determine the CPU type"); + } + } + } + + void display_information(const VerbosityLevel lvl) const override { + ExecutableFormat::display_information(lvl); + fmt::print("Elf Information:\n"); + m_ELFLayout->display(lvl); + } + + std::string get_class_name() const override { return "Elf"; } + + std::vector
+ get_executables_section(std::ifstream &file, + const uint64_t base) const override { + return m_ELFLayout->get_executable_section(file, base); + } + + uint64_t get_image_base_address() const override { + return m_ELFLayout->get_image_base_address(); + } + +private: + CPU::E_CPU extract_information_from_binary(std::ifstream &file) override { + uint32_t size_init = 0; + std::array buf; + CPU::E_CPU cpu = CPU::CPU_UNKNOWN; + fmt::print("Loading ELF information..\n"); + + std::streampos off = file.tellg(); + file.seekg(0, std::ios::beg); + file.read((char *)buf.data(), EI_NIDENT); + + switch (buf.at(EI_CLASS)) { + case ELFCLASS32: { + size_init = 4; + break; + } + + case ELFCLASS64: { + size_init = 8; + break; + } + + default: { + RAISE_EXCEPTION("Cannot determine the architecture size"); + } + } + + if (size_init == 8) { + init_properly_ELFLayout(); + } else { + init_properly_ELFLayout(); + } + + // Filling the structure now + m_ELFLayout->fill_structures(file); + + // Set correctly the pointer + file.seekg(off); + + switch (m_ELFLayout->get_cpu()) { + case RP_ELFEM_386: { + cpu = CPU::CPU_x86; + break; + } + + case RP_ELFEM_X86_64: { + cpu = CPU::CPU_x64; + break; + } + + case RP_ELFEM_ARM: { + cpu = CPU::CPU_ARM; + break; + } + + case RP_ELFEM_ARM64: { + cpu = CPU::CPU_ARM64; + break; + } + + default: { + RAISE_EXCEPTION("Cannot determine the CPU type"); + } + } + + return cpu; + } + + template void init_properly_ELFLayout() { + m_ELFLayout = std::make_unique>(); + if (m_ELFLayout == nullptr) { + RAISE_EXCEPTION("m_ELFLayout allocation failed"); + } + } + + std::unique_ptr m_ELFLayout; +}; + +``` + +`src/rp/elf_struct.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "coloshell.hpp" +#include "platform.h" +#include "rpexception.hpp" +#include "section.hpp" +#include "toolbox.hpp" +#include +#include +#include +#include +#include +#include +#include + +const uint32_t EI_NIDENT = 16; + +#ifdef WINDOWS +#pragma pack(push) +#pragma pack(1) +#endif + +template struct Elf_Ehdr { + std::array e_ident; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + T e_entry; + T e_phoff; + T e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; + + void display(VerbosityLevel lvl = VERBOSE_LEVEL_1) const { + w_yel_lf("-> ELF_Ehdr:"); + + display_short_hex_field_lf(e_phoff); + display_short_hex_field_lf(e_shoff); + + display_short_hex_field_lf(e_flags); + + display_short_hex_2fields_lf(e_phentsize, e_phnum); + display_short_hex_2fields_lf(e_shentsize, e_shnum); + display_short_hex_2fields_lf(e_shstrndx, e_ehsize); + } +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +using Elf32_Ehdr = Elf_Ehdr; +using Elf64_Ehdr = Elf_Ehdr; + +const uint32_t EI_OSABI = 7; +const uint32_t EI_CLASS = 4; +const uint32_t ELFCLASS32 = 1; +const uint32_t ELFCLASS64 = 2; + +template struct Elf_Phdr { +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +static std::string type_to_str(const uint32_t p_type) { + switch (p_type) { + case 0: { + return "NULL"; + } + + case 1: { + return "LOAD"; + } + + case 2: { + return "DYNAMIC"; + } + + case 3: { + return "INTERP"; + } + + case 4: { + return "NOTE"; + } + + case 5: { + return "SHLIB"; + } + + case 6: { + return "PHDR"; + } + + case 7: { + return "TLS"; + } + + case 8: { + return "NUM"; + } + + case 0x60000000: { + return "LOOS"; + } + + case 0x6fffffff: { + return "HIOS"; + } + + case 0x70000000: { + return "LOPROC"; + } + + case 0x7fffffff: { + return "HIPROC"; + } + + case 0x6474e550: { + return "EH_FRAME"; + } + + case 0x6474e551: { + return "STACK"; + } + + case 0x6474e552: { + return "RELRO"; + } + + case 0x6474e553: { + return "PROPERTY"; + } + + case 0x65041580: { + return "PAX_FLAGS"; + } + + case 0x65a3dbe5: { + return "OPENBSD_MUTABLE"; + } + + case 0x65a3dbe6: { + return "OPENBSD_RANDOMIZE"; + } + + case 0x65a3dbe7: { + return "OPENBSD_WXNEEDED"; + } + + case 0x65a3dbe8: { + return "OPENBSD_NOBTCFI"; + } + + case 0x65a3dbe9: { + return "OPENBSD_SYSCALLS"; + } + + case 0x65a41be6: { + return "OPENBSD_BOOTDATA"; + } + + case 0x70000001: { + return "SHT_AMD64_UNWIND"; + } + + case 0x6474e554: { + return "PT_GNU_SFRAME"; + } + } + + fmt::print("Unknown ELF type 0x{:x}\n", p_type); + std::abort(); + return "unknown type"; +} + +static std::string flags_to_str(const uint32_t p_flags) { + std::string ret(3, '-'); + + if (p_flags & 4) { + ret[0] = 'r'; + } + + if (p_flags & 2) { + ret[1] = 'w'; + } + + if (p_flags & 1) { + ret[2] = 'x'; + } + + return ret; +} + +template <> struct Elf_Phdr { + uint32_t p_type; + uint32_t p_offset; + uint32_t p_vaddr; + uint32_t p_paddr; + uint32_t p_filesz; + uint32_t p_memsz; + uint32_t p_flags; + uint32_t p_align; + + void display(VerbosityLevel lvl = VERBOSE_LEVEL_1) const { + w_yel_lf("-> Elf_Phdr32: "); + fmt::print(" {} {}\n", type_to_str(p_type), flags_to_str(p_flags)); + + if (lvl > VERBOSE_LEVEL_1) { + display_hex_2fields_lf(p_vaddr, p_filesz); + } + + if (lvl > VERBOSE_LEVEL_2) { + display_hex_2fields_lf(p_align, p_flags); + } + + display_hex_2fields_lf(p_offset, p_paddr); + } +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +template <> struct Elf_Phdr { + uint32_t p_type; + uint32_t p_flags; + uint64_t p_offset; + uint64_t p_vaddr; + uint64_t p_paddr; + uint64_t p_filesz; + uint64_t p_memsz; + uint64_t p_align; + + void display(VerbosityLevel lvl = VERBOSE_LEVEL_1) const { + w_yel_lf("-> Elf_Phdr64:"); + fmt::print(" {} {}\n", type_to_str(p_type), flags_to_str(p_flags)); + + if (lvl > VERBOSE_LEVEL_1) { + display_short_hex_2fields_lf(p_vaddr, p_filesz); + } + + if (lvl > VERBOSE_LEVEL_2) { + display_short_hex_2fields_lf(p_align, p_flags); + } + + display_short_hex_2fields_lf(p_offset, p_paddr); + } +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +using Elf32_Phdr = Elf_Phdr; +using Elf64_Phdr = Elf_Phdr; + +template struct Elf_Shdr { + uint32_t sh_name; + uint32_t sh_type; + T sh_flags; + T sh_addr; + T sh_offset; + T sh_size; + uint32_t sh_link; + uint32_t sh_info; + T sh_addralign; + T sh_entsize; +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +using Elf32_Shdr = Elf_Shdr; +using Elf64_Shdr = Elf_Shdr; + +#ifdef WINDOWS +#pragma pack(pop) +#endif + +template struct Elf_Shdr_Abstraction { + Elf_Shdr header; + std::string name; + + void display(VerbosityLevel lvl = VERBOSE_LEVEL_1) const { + fmt::print("0x{:<15x}0x{:<15x}{:32}\n", header.sh_addr, header.sh_size, + name); + } +}; + +using Elf_Shdr32_Abstraction = Elf_Shdr_Abstraction; +using Elf_Shdr64_Abstraction = Elf_Shdr_Abstraction; + +struct ExecutableLinkingFormatLayout { + virtual ~ExecutableLinkingFormatLayout() = default; + virtual void fill_structures(std::ifstream &file) = 0; + virtual void display(VerbosityLevel lvl = VERBOSE_LEVEL_1) const = 0; + virtual uint64_t get_image_base_address(void) const = 0; + virtual std::vector
+ get_executable_section(std::ifstream &file, const uint64_t base) const = 0; + virtual uint16_t get_cpu(void) const = 0; +}; + +const uint32_t SHT_SYMTAB = 2; +const uint32_t SHT_STRTAB = 3; + +const uint32_t RP_ELFEM_386 = 0x03; +const uint32_t RP_ELFEM_X86_64 = 0x3e; +const uint32_t RP_ELFEM_ARM = 0x28; +const uint32_t RP_ELFEM_ARM64 = 0xb7; + +template struct ELFLayout : public ExecutableLinkingFormatLayout { + Elf_Ehdr elfHeader; + std::vector>> elfProgramHeaders; + std::vector>> elfSectionHeaders; + T offset_string_table, size_string_table; + uint64_t image_base = 0; + + void display(VerbosityLevel lvl = VERBOSE_LEVEL_1) const override { + uint32_t i = 0; + elfHeader.display(lvl); + + for (const auto &programheader : elfProgramHeaders) { + programheader->display(lvl); + } + + w_yel_lf("-> Elf Headers:"); + w_gre("id"); + fmt::print("{:10}", ""); + w_gre("addr"); + fmt::print("{:13}", ""); + w_gre("size"); + fmt::print("{:13}", ""); + w_gre("name"); + fmt::print("{:32}\n", ""); + fmt::print("{:-<70}-\n", ""); + + for (const auto §ionheader : elfSectionHeaders) { + fmt::print("0x{:<10x}", i++); + sectionheader->display(lvl); + } + } + + T find_string_table(std::ifstream &file) { + Elf_Shdr elf_shdr; + std::streampos off = file.tellg(); + + file.seekg(std::streamoff(elfHeader.e_shoff), std::ios::beg); + + for (uint32_t i = 0; i < elfHeader.e_shnum; ++i) { + file.read((char *)&elf_shdr, sizeof(Elf_Shdr)); + if (elf_shdr.sh_addr == 0 && elf_shdr.sh_type == SHT_STRTAB) { + offset_string_table = elf_shdr.sh_offset; + size_string_table = elf_shdr.sh_size; + break; + } + } + + file.seekg(off); + return offset_string_table; + } + + void fill_structures(std::ifstream &file) override { + // Remember where the caller was in the file + std::streampos off = file.tellg(); + + // Dump the Elf Header + file.seekg(0, std::ios::beg); + file.read((char *)&elfHeader, sizeof(Elf_Ehdr)); + + // Goto the first Program Header, and dump them + file.seekg(std::streamoff(elfHeader.e_phoff), std::ios::beg); + for (uint32_t i = 0; i < elfHeader.e_phnum; ++i) { + auto pElfProgramHeader = std::make_unique>(); + + file.read((char *)pElfProgramHeader.get(), sizeof(Elf_Phdr)); + + // Here we assume that the first LOAD program header encountered will + // hold the image base address and I guess this assumption is quite wrong + // https://stackoverflow.com/questions/18296276/base-address-of-elf + if (type_to_str(pElfProgramHeader->p_type) == "LOAD" && image_base == 0) { + image_base = pElfProgramHeader->p_vaddr; + } + elfProgramHeaders.push_back(std::move(pElfProgramHeader)); + } + + // If we want to know the name of the different section, we need to find the + // string table section + find_string_table(file); + + // Keep the string table in memory + file.seekg(std::streamoff(offset_string_table), std::ios::beg); + + std::vector string_table_section; + string_table_section.resize(uint32_t(size_string_table)); + file.read(string_table_section.data(), std::streamsize(size_string_table)); + + // Goto the first Section Header, and dump them ! + file.seekg(std::streamoff(elfHeader.e_shoff), std::ios::beg); + for (uint32_t i = 0; i < elfHeader.e_shnum; ++i) { + auto pElfSectionHeader = std::make_unique>(); + + file.read((char *)&pElfSectionHeader->header, sizeof(Elf_Shdr)); + + // Resolve the name of the section + if (pElfSectionHeader->header.sh_name < size_string_table) { + // Yeah we know where is the string + char *name_section = + string_table_section.data() + pElfSectionHeader->header.sh_name; + pElfSectionHeader->name = + (std::strlen(name_section) == 0) ? "unknown section" : name_section; + } + + elfSectionHeaders.push_back(std::move(pElfSectionHeader)); + } + + // Set correctly the pointer + file.seekg(off); + } + + std::vector
+ get_executable_section(std::ifstream &file, + const uint64_t base) const override { + std::vector
exec_sections; + + for (const auto &programheader : elfProgramHeaders) { + if (!(programheader->p_flags & 1)) { + continue; + } + + const auto vaddr = programheader->p_vaddr - image_base; + const auto p_offset = programheader->p_offset; + const auto p_filesz = programheader->p_filesz; + Section sec(type_to_str(programheader->p_type).c_str(), p_offset, + base + vaddr, p_filesz); + + sec.dump(file); + sec.set_props(Section::Executable); + exec_sections.push_back(std::move(sec)); + } + + return exec_sections; + } + + uint64_t get_image_base_address() const override { return image_base; } + + uint16_t get_cpu() const override { return elfHeader.e_machine; } +}; + +``` + +`src/rp/executable_format.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "cpu.hpp" +#include "rpexception.hpp" +#include "section.hpp" +#include +#include +#include +#include +#include + +/*! \class ExecutableFormat + * + * An ExecutableFormat is the second part composing a Program instance ; it is + * required to parse correctly the binary file, to know where you can find its + * executable sections, etc. + */ +class ExecutableFormat { +public: + /* The format RP++ handles */ + enum E_ExecutableFormat { FORMAT_PE, FORMAT_ELF, FORMAT_UNKNOWN }; + + virtual ~ExecutableFormat() = default; + + /*! + * \brief Obtain the CPU ; for that it parses the executable format of your + * binary + * + * \return a pointer on the correct CPU + */ + virtual std::unique_ptr get_cpu(std::ifstream &file) = 0; + + /*! + * \brief Display information concerning the executable format: where + * sections begin, entry point, etc. + * + * \param lvl: Set a verbosity level + */ + virtual void display_information(const VerbosityLevel lvl) const { + fmt::print("Verbose level: {}\n", verbosity_to_string(lvl)); + } + + /*! + * \brief Retrieve the name of the class, useful when using polymorphism + * + * \return the class name + */ + virtual std::string get_class_name() const = 0; + + /*! + * \brief Get the executable sections of you binary ; it is where we will + * look for gadgets + * + * \param file: it is a file handle on your binary file + * \param base: it is the base address to use + * + * \return A vector of Section instances + */ + virtual std::vector
+ get_executables_section(std::ifstream &file, const uint64_t base) const = 0; + + /*! + * \brief Give you the base address of the executable + * + * \return The prefered base address of the executable + */ + virtual uint64_t get_image_base_address() const = 0; + +private: + /*! + * \brief Fill the structures you need, parse your executable format to + * extract the useful information + * + * \param file: It is your binary file + * + * \return The CPU type used in your binary file + */ + virtual CPU::E_CPU extract_information_from_binary(std::ifstream &) { + RAISE_EXCEPTION( + "This method should not be called ; you're doing it wrong!"); + return CPU::CPU_UNKNOWN; + } +}; + +``` + +`src/rp/gadget.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "instruction.hpp" +#include +#include +#include +#include +#include +#include +#include + +/*! \class Gadget + * + * A gadget is a sequence of instructions that ends by an ending instruction + * (ret/call/jmp) In order, to keep in memory only *unique* gadgets, each gadget + * holds a set of offset where you can find the same one. + */ +class Gadget { +public: + struct Info { + uint64_t m_offset; + uint64_t m_va_section; + + Info(const uint64_t offset, const uint64_t va_section) + : m_offset(offset), m_va_section(va_section) {} + }; + + explicit Gadget(const uint64_t offset_start) + : m_start_offset(offset_start), m_size(0) {} + + /*! + * \brief Get the entire disassembly of your gadget + * \return the disassembly + */ + std::string get_disassembly() const { + // Computing the disassembly is cheaper than keeping it in memory + // Otherwise with big binaries you end up with a *lot* of memory being used + std::string disassembly; + for (const auto &i : m_instructions) { + disassembly += i.get_disassembly() + " ; "; + } + + return disassembly; + } + + void print_disassembly() const { + for (size_t idx = 0; idx < m_instructions.size(); idx++) { + fmt::print("{} ;", m_instructions[idx].get_disassembly()); + if ((idx + 1) < m_instructions.size()) { + fmt::print(" "); + } + } + } + + /*! + * \brief Get the size of your gadget + * \return the size of the whole gadget + */ + uint32_t get_size() const { return m_size; } + + /*! + * \brief Add a list of instructions to your gadget ; don't forget it's back + * pushed in the instruction list It means the first instruction inserted will + * be the address of the gadget + * + * \param instrs: It is a list of Instruction to create our gadget (NB: the + * method copy in its memory those instructions for futur usage) \param + * va_section: It is the va section of the instructions ; a bit weird to pass + * it here yeah + */ + void add_instructions(std::vector &instrs, uint64_t va_section) { + for (const auto &instr : instrs) { + // If we haven't any offset yet, it means this instruction is the first + // one added thus, the offset of the gadget + if (m_info_gadgets.size() == 0) { + m_info_gadgets.emplace_back(m_start_offset, va_section); + } + + // We build our gadget instruction per instruction + m_instructions.emplace_back(instr); + + // Don't forget to increment the size + m_size += instr.get_size(); + } + } + + /*! + * \brief Get the first offset of this gadget (first offset because a gadget + * instance stores other offset with the same disassembly in memory) \return + * the offset (relative to m_va_section) + */ + uint64_t get_first_offset() const { return m_info_gadgets.front().m_offset; } + + /*! + * \brief Get the first va section of this gadget (first offset because a + * gadget instance stores other offset with the same disassembly in memory) + * \return the va section + */ + uint64_t get_first_va_section() const { + return m_info_gadgets.front().m_va_section; + } + + /*! + * \brief Get the first absolute address of this gadget + * \return the absolute address (computed like this: m_va_section + offset) + */ + uint64_t get_first_absolute_address() const { + return get_first_offset() + get_first_va_section(); + } + + /*! + * \brief Get the number of other equivalent gadget + * \return the number of the same gadget in memory + */ + size_t get_nb() const { return m_info_gadgets.size(); } + + /*! + * \brief Add the offset where you can find the same gadget + * + * \param offset: the offset where you can find the same gadget + */ + void add_new_one(const uint64_t offset, const uint64_t va_section) const { + m_info_gadgets.emplace_back(offset, va_section); + } + + /*! + * \brief This structure can be used for sorting Gadgets instance + * \return + */ + struct Comparator { + bool operator()(const Gadget &a, const Gadget &b) const { + const size_t a_size = a.m_instructions.size(); + const size_t b_size = b.m_instructions.size(); + size_t current_a_idx = 0, current_b_idx = 0; + size_t current_a_bytes_idx = 0, current_b_bytes_idx = 0; + while (1) { + if (current_a_idx >= a_size) { + return false; + } + if (current_b_idx >= b_size) { + return true; + } + + const auto ¤t_a_disass = + a.m_instructions[current_a_idx].get_disassembly(); + const auto ¤t_b_disass = + b.m_instructions[current_b_idx].get_disassembly(); + const int compare = current_a_disass.compare(current_b_disass); + if (compare != 0) { + return (compare < 0) ? true : false; + } + + current_a_idx++; + current_b_idx++; + } + } + }; + + void print_bytes() const { + for (const auto &i : m_instructions) { + i.print_bytes(); + } + } + +private: + uint64_t m_start_offset; /*!< this is where the gadget is starting from in + memory */ + + uint32_t m_size; /*!< the size in byte of the gadget*/ + + std::vector + m_instructions; /*!< the list of the different instructions composing + the gadget*/ + + mutable std::vector + m_info_gadgets; /*!< the vector which stores where you can find the same + gadget ; those offsets are relative to m_va_section*/ +}; + +using GadgetMultiset = std::multiset; +using GadgetSet = std::set; + +``` + +`src/rp/instruction.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include +#include +#include + +/*! \class Instruction + * + * Each instruction instance holds a disassembly, an offset (where we can find + * it in memory) and a size + */ +class Instruction { +public: + /*! + * \brief Build an instruction + * + * \param disass: The disassembly of the instruction + * \param mnemonic: The mnemonic of the instruction + * \param offset: A raw offset (relative to a section) where you can find + * this instruction + * \param size: It is the size of the instruction + */ + Instruction(const std::string &disass, const std::vector &b) + : m_disass(disass), m_bytes(b) {} + + /*! + * \brief Get the size of the instruction + * + * \return the size of the instruction + */ + uint32_t get_size() const { return m_bytes.size(); } + + /*! + * \brief Get the disassembly of the instruction + * + * \return the disassembly of the instruction + */ + const std::string &get_disassembly() const { return m_disass; } + + void print_bytes() const { + for (const auto &byte : m_bytes) { + fmt::print("\\x{:02x}", byte); + } + } + + const std::vector &bytes() const { return m_bytes; } + +private: + std::vector m_bytes; + std::string m_disass; /*!< the disassembly of the instruction */ +}; + +``` + +`src/rp/intelbeaengine.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "disassenginewrapper.hpp" +#include + +class IntelBeaEngine : public DisassEngineWrapper { +public: + /*! The different architectures BeaRopGadgetFinder handles */ + enum E_Arch { x86 = 32, x64 = 64 }; + + explicit IntelBeaEngine(const E_Arch arch) : m_arch(uint32_t(arch)) { + // those options are mostly display option for the disassembler engine + m_disasm.Options = PrefixedNumeral + NasmSyntax; + + // this one is to precise what architecture we'll disassemble + m_disasm.Archi = m_arch; + } + + InstructionInformation disass(const uint8_t *data, uint64_t len, + const uint64_t vaddr, + DisassEngineReturn &ret) override { + InstructionInformation instr; + m_disasm.EIP = UIntPtr(data); + m_disasm.VirtualAddr = vaddr; + m_disasm.SecurityBlock = uint32_t(len); + + const int len_instr = Disasm(&m_disasm); + if (len_instr == OUT_OF_BLOCK) { + ret = OutOfBlock; + return instr; + } + + // OK this one is an unknow opcode, goto the next one + if (len_instr == UNKNOWN_OPCODE) { + ret = UnknownInstruction; + return instr; + } + + ret = AllRight; + + instr.address = m_disasm.EIP; + instr.virtual_address_in_memory = uintptr_t(m_disasm.VirtualAddr); + instr.disassembly = m_disasm.CompleteInstr; + instr.mnemonic = m_disasm.Instruction.Mnemonic; + instr.size = len_instr; + + const auto branch_type = m_disasm.Instruction.BranchType; + const auto addr_value = m_disasm.Instruction.AddrValue; + const char *mnemonic_s = m_disasm.Instruction.Mnemonic; + const char *disass_s = instr.disassembly.c_str(); + + instr.is_branch = branch_type != 0; + + const bool is_good_branch_type = + // We accept all the ret type instructions (except retf/iret) + (branch_type == RetType && (strncmp(mnemonic_s, "retf", 4) != 0) && + (strncmp(mnemonic_s, "iretd", 5) != 0)) || + // call reg32 / call [reg32] + (branch_type == CallType && addr_value == 0) || + // jmp reg32 / jmp [reg32] + (branch_type == JmpType && addr_value == 0) || + // int 0x80 & int 0x2e + ((strncmp(disass_s, "int 0x80", 8) == 0) || + (strncmp(disass_s, "int 0x2e", 8) == 0) || + (strncmp(disass_s, "syscall", 7) == 0)); + + instr.is_valid_ending_instr = + is_good_branch_type && + // Yeah, we don't accept jmp far/call far + instr.disassembly.find("far") == std::string::npos; + + return instr; + } + + uint32_t get_size_biggest_instruction() const override { return 15; } + + uint32_t get_alignement() const override { return 1; } + +private: + uint32_t m_arch = 0; /*!< architecture the BeaEngine will use to disassemble*/ + DISASM m_disasm = {}; +}; + +``` + +`src/rp/macho.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "executable_format.hpp" + +#include "arm.hpp" +#include "arm64.hpp" +#include "macho_struct.hpp" +#include "x64.hpp" +#include "x86.hpp" + +class Macho : public ExecutableFormat { +public: + std::unique_ptr get_cpu(std::ifstream &file) override { + RP_MACH_HEADER header32; + + fmt::print("Loading Mach-O information..\n"); + + // Remember where the caller was in the file + std::streampos off = file.tellg(); + + file.seekg(0, std::ios::beg); + file.read((char *)&header32, sizeof(header32)); + + std::unique_ptr cpu; + switch (header32.cputype) { + case CPU_TYPE_x86_64: { + cpu = std::make_unique(); + init_properly_macho_layout(); + break; + } + + case CPU_TYPE_I386: { + cpu = std::make_unique(); + init_properly_macho_layout(); + break; + } + + case CPU_TYPE_ARM: { + cpu = std::make_unique(); + init_properly_macho_layout(); + break; + } + + case CPU_TYPE_ARM64: { + cpu = std::make_unique(); + init_properly_macho_layout(); + break; + } + + default: { + RAISE_EXCEPTION( + "Cannot determine which architecture is used in this Mach-O file"); + } + } + + file.seekg(off); + + // Now we can fill the structure + m_MachoLayout->fill_structures(file); + return cpu; + } + + std::string get_class_name() const override { return "Mach-o"; } + + std::vector
+ get_executables_section(std::ifstream &file, + const uint64_t base) const override { + return m_MachoLayout->get_executable_section(file, base); + } + + void display_information(const VerbosityLevel lvl) const override { + ExecutableFormat::display_information(lvl); + m_MachoLayout->display(lvl); + } + + uint64_t get_image_base_address() const override { + return m_MachoLayout->get_image_base_address(); + } + +private: + template void init_properly_macho_layout() { + m_MachoLayout = std::make_unique>(); + } + + CPU::E_CPU extract_information_from_binary(std::ifstream &file) override { + return CPU::CPU_UNKNOWN; + } + + std::unique_ptr m_MachoLayout; +}; + +``` + +`src/rp/macho_struct.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "coloshell.hpp" +#include "platform.h" +#include "section.hpp" +#include "toolbox.hpp" +#include +#include +#include +#include + +#ifdef WINDOWS +#pragma pack(push) +#pragma pack(1) +#endif + +const uint32_t CPU_ARCH_ABI64 = 0x1000000; +const uint32_t CPU_TYPE_I386 = 7; +const uint32_t CPU_TYPE_x86_64 = CPU_ARCH_ABI64 | CPU_TYPE_I386; +const uint32_t CPU_TYPE_ARM = 12; +const uint32_t CPU_TYPE_ARM64 = CPU_TYPE_ARM | CPU_ARCH_ABI64; + +template struct RP_MACH_HEADER {}; + +template <> struct RP_MACH_HEADER { + uint32_t magic; + uint32_t cputype; + uint32_t cpusubtype; + uint32_t filetype; + uint32_t ncmds; + uint32_t sizeofcmds; + uint32_t flags; + + void display(const VerbosityLevel lvl) const { + w_yel_lf("-> mach_header32:"); + + display_hex_2fields_lf(ncmds, sizeofcmds); + + if (lvl > VERBOSE_LEVEL_1) { + display_hex_field_lf(cpusubtype); + } + + if (lvl > VERBOSE_LEVEL_2) { + display_hex_2fields_lf(magic, cpusubtype); + display_hex_2fields_lf(filetype, flags); + } + } +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +template <> struct RP_MACH_HEADER { + uint32_t magic; + uint32_t cputype; + uint32_t cpusubtype; + uint32_t filetype; + uint32_t ncmds; + uint32_t sizeofcmds; + uint32_t flags; + uint32_t reserved; + + void display(const VerbosityLevel lvl) const { + w_yel_lf("-> mach_header64:"); + + display_hex_2fields_lf(ncmds, sizeofcmds); + + if (lvl > VERBOSE_LEVEL_1) { + display_hex_field_lf(cpusubtype); + } + + if (lvl > VERBOSE_LEVEL_2) { + display_hex_2fields_lf(magic, cpusubtype); + display_hex_2fields_lf(filetype, flags); + display_hex_field_lf(reserved); + } + } +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +const uint32_t LC_SEGMENT = 1; +const uint32_t LC_SEGMENT_64 = 0x19; + +struct RP_LOAD_COMMAND { + uint32_t cmd; + uint32_t cmdsize; +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +template struct RP_SEGMENT_COMMAND { + std::array segname; + T vmaddr; + T vmsize; + T fileoff; + T filesize; + uint32_t maxprot; + uint32_t initprot; + uint32_t nsects; + uint32_t flags; + + void display(VerbosityLevel lvl) const { + w_yel_lf("-> segment_command"); + fmt::print(" {}\n", segname.data()); + + if (lvl > VERBOSE_LEVEL_1) { + display_hex_field_lf(vmaddr); + display_hex_field_lf(vmsize); + display_hex_field_lf(fileoff); + display_hex_field_lf(filesize); + } + + if (lvl > VERBOSE_LEVEL_2) { + display_hex_2fields_lf(maxprot, initprot); + display_hex_2fields_lf(nsects, flags); + } + } +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +using SegmentCommand32 = RP_SEGMENT_COMMAND; +using SegmentCommand64 = RP_SEGMENT_COMMAND; + +const uint32_t S_ATTR_PURE_INSTRUCTIONS = 0x80000000; +const uint32_t S_ATTR_SOME_INSTRUCTIONS = 0x400; + +template struct RP_SECTION {}; + +template <> struct RP_SECTION { + std::array sectname; + std::array segname; + uint32_t addr; + uint32_t size; + uint32_t offset; + uint32_t align; + uint32_t reloff; + uint32_t nreloc; + uint32_t flags; + uint32_t reserved1; + uint32_t reserved2; + + void display(VerbosityLevel lvl) const { + w_yel_lf("-> section32"); + fmt::print(" {}.{}\n", segname.data(), sectname.data()); + + if (lvl > VERBOSE_LEVEL_1) { + display_hex_2fields_lf(addr, size); + display_hex_2fields_lf(offset, align); + } + + if (lvl > VERBOSE_LEVEL_2) { + display_hex_2fields_lf(reloff, nreloc); + display_hex_2fields_lf(flags, reserved1); + } + } +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +template <> struct RP_SECTION { + std::array sectname; + std::array segname; + uint64_t addr; + uint64_t size; + uint32_t offset; + uint32_t align; + uint32_t reloff; + uint32_t nreloc; + uint32_t flags; + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; + + void display(VerbosityLevel lvl) const { + w_yel_lf("-> section64"); + fmt::print(" {}.{}\n", segname.data(), sectname.data()); + + if (lvl > VERBOSE_LEVEL_1) { + display_hex_field_lf(addr); + display_hex_field_lf(size); + display_hex_2fields_lf(offset, align); + } + + if (lvl > VERBOSE_LEVEL_2) { + display_hex_2fields_lf(reloff, nreloc); + display_hex_2fields_lf(flags, reserved1); + display_hex_2fields_lf(reserved2, reserved3); + } + } +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +#ifdef WINDOWS +#pragma pack(pop) +#endif + +struct MachoLayout { + virtual ~MachoLayout() = default; + virtual void fill_structures(std::ifstream &file) = 0; + virtual uint32_t get_size_mach_header() const = 0; + virtual void display(const VerbosityLevel lvl = VERBOSE_LEVEL_1) const = 0; + virtual std::vector
+ get_executable_section(std::ifstream &file, const uint64_t base) const = 0; + virtual uint64_t get_image_base_address() const = 0; +}; + +template struct MachoArchLayout : public MachoLayout { + uint64_t base = 0; + RP_MACH_HEADER header; + std::vector>> seg_commands; + std::vector>> sections; + + uint32_t get_size_mach_header() const override { + return sizeof(RP_MACH_HEADER); + } + + void fill_structures(std::ifstream &file) override { + bool is_all_section_walked = false; + std::streampos off = file.tellg(); + + // Fill the header structure + file.seekg(0, std::ios::beg); + file.read((char *)&header, sizeof(RP_MACH_HEADER)); + + // The load commands now + for (uint32_t i = 0; i < header.ncmds; ++i) { + RP_LOAD_COMMAND loadcmd; + + file.read((char *)&loadcmd, sizeof(RP_LOAD_COMMAND)); + switch (loadcmd.cmd) { + case LC_SEGMENT: + case LC_SEGMENT_64: { + auto seg_cmd = std::make_unique>(); + + file.read((char *)seg_cmd.get(), sizeof(RP_SEGMENT_COMMAND)); + + if (strcasecmp((char *)seg_cmd->segname.data(), "__TEXT") == 0) { + // If this is the __text segment, we populate the base address of the + // program + base = uint64_t(seg_cmd->vmaddr); + } + + // Directly following a segment_command data structure is an array of + // section data structures, with the exact count determined by the + // nsects field of the segment_command structure. + for (uint32_t j = 0; j < seg_cmd->nsects; ++j) { + auto sect = std::make_unique>(); + file.read((char *)sect.get(), sizeof(RP_SECTION)); + sections.push_back(std::move(sect)); + } + + seg_commands.push_back(std::move(seg_cmd)); + break; + } + + default: { + // We assume that all SEGMENT_HEADER[_64] are in first, and they + // are all contiguous The proper way should be add cases for each + // COMMAND possible, and increment the file pointer of the size of the + // COMMAND read + is_all_section_walked = true; + break; + } + } + + if (is_all_section_walked) { + break; + } + } + + file.seekg(off); + } + + std::vector
+ get_executable_section(std::ifstream &file, + const uint64_t base) const override { + std::vector
exc_sect; + + for (const auto §ion : sections) { + if (!(section->flags & S_ATTR_PURE_INSTRUCTIONS) && + !(section->flags & S_ATTR_SOME_INSTRUCTIONS)) { + continue; + } + + const auto vaddr = section->addr - base; + const auto offset = section->offset; + const auto size = section->size; + Section s((char *)section->sectname.data(), offset, base + vaddr, size); + s.dump(file); + s.set_props(Section::Executable); + exc_sect.push_back(std::move(s)); + } + return exc_sect; + } + + void display(const VerbosityLevel lvl = VERBOSE_LEVEL_1) const override { + header.display(lvl); + + for (const auto &segcommand : seg_commands) { + segcommand->display(lvl); + } + + for (const auto §ion : sections) { + section->display(lvl); + } + } + + uint64_t get_image_base_address() const override { return base; } +}; + +``` + +`src/rp/main.cpp`: + +```cpp +// Axel '0vercl0k' Souchet - January 12 2022 +#include "coloshell.hpp" +#include "options.hpp" +#include "platform.h" +#include "program.hpp" +#include +#include +#include +#include +#include +#include + +#define NUM_V "2.1.5" +#ifdef ARCH_X64 +#define VERSION_TMP NUM_V " x64 built the " __DATE__ " " __TIME__ +#elif defined ARCH_X86 +#define VERSION_TMP NUM_V " x86 built the " __DATE__ " " __TIME__ +#else +#define VERSION_TMP NUM_V " arm64 built the " __DATE__ " " __TIME__ +#endif + +#define VERSION_TM VERSION_TMP " for " SYSTEM_PLATFORM + +#ifdef _DEBUG +#define VERSION VERSION_TM " (Debug)" +#else +#define VERSION VERSION_TM " (Release)" +#endif + +Options_t g_opts; + +int main(int argc, char *argv[]) { + CLI::App rp( + "rp++: a fast ROP gadget finder for pe/elf/mach-o x86/x64/ARM/ARM64 " + "binaries\nby Axel '0vercl0k' Souchet.\n"); + rp.add_option("-f,--file", g_opts.file, "Binary path")->required(); + rp.add_option("-i,--info", g_opts.display, + "display information about the binary header"); + rp.add_option("-r,--rop", g_opts.rop, + "find useful gadget for your future exploits, arg is the " + "gadget maximum size in instructions"); + const std::unordered_map raw_archi_map = { + {"x86", CPU::CPU_x86}, + {"x64", CPU::CPU_x64}, + {"arm", CPU::CPU_ARM}, + {"arm64", CPU::CPU_ARM64}}; + rp.add_option("--raw", g_opts.raw, "find gadgets in a raw file") + ->transform(CLI::CheckedTransformer(raw_archi_map, CLI::ignore_case)); + rp.add_flag("--unique", g_opts.unique, "display only unique gadget"); + rp.add_option("--search-hexa", g_opts.shexa, "try to find hex values"); + rp.add_option("--max-thread", g_opts.maxth, + "set the maximum number of threads that can be used"); + rp.add_option("--bad-bytes", g_opts.badbytes, + "the bytes you don't want to see in the gadgets' addresses"); + rp.add_option("--search-int", g_opts.sint, + "try to find a pointer on a specific integer value"); + rp.add_flag("-v,--version", g_opts.version, "print version information"); + rp.add_flag("--colors", g_colors_desired, "enable colors"); + rp.add_flag("--thumb", g_opts.thumb, + "enable thumb mode when looking for ARM gadgets"); + rp.add_option("--va", g_opts.va, + "don't use the image base of the binary, but yours instead"); + rp.add_flag("--allow-branches", g_opts.allow_branches, + "allow branches in a gadget"); + rp.add_flag("--print-bytes", g_opts.print_bytes, "print the gadget bytes"); + + CLI11_PARSE(rp, argc, argv); + + try { + if (g_opts.version) { + fmt::print("You are currently using the version {} of rp++.\n", VERSION); + } + + // It has been confusing for users to run & see no rop gadget displayed..so + // we'll default the gadget length setting for them. + if (g_opts.display == 0 && g_opts.rop == 0) { + fmt::print("No rop gadget length was specified via --rop, so setting " + "it to 5..\n"); + g_opts.rop = 5; + } + + Program p(g_opts.file, g_opts.raw); + if (g_opts.display >= VERBOSE_LEVEL_1 && + g_opts.display <= VERBOSE_LEVEL_3) { + p.display_information(VerbosityLevel(g_opts.display)); + } + + // Here we set the base being 0 if we want to have absolute virtual + // memory address displayed + const uint64_t base = g_opts.va.size() > 0 ? va_to_integer(g_opts.va) + : p.get_image_base_address(); + if (g_opts.rop > 0) { + const uint32_t options = g_opts.thumb ? 1 : 0; + fmt::print("\nWait a few seconds, rp++ is looking for gadgets ({} " + "threads max)..\n", + g_opts.maxth); + + GadgetMultiset all_gadgets = + p.find_gadgets(g_opts.rop, options, g_opts.maxth, base); + + fmt::print("A total of {} gadgets found.\n", all_gadgets.size()); + std::vector badbyte_list; + if (g_opts.badbytes.size() > 0) { + badbyte_list = string_to_hex(g_opts.badbytes); + } + + uint64_t nb_gadgets_filtered = 0; + if (g_opts.unique) { + auto unique_gadgets = + only_unique_gadgets(all_gadgets, badbyte_list, nb_gadgets_filtered); + + fmt::print("You decided to keep only the unique ones, {} unique " + "gadgets found.\n", + unique_gadgets.size()); + + // Now we walk the gadgets found and set the VA + for (const auto &unique_gadget : unique_gadgets) { + display_gadget_lf(unique_gadget.get_first_absolute_address(), + unique_gadget); + } + } else { + for (const auto &gadget : all_gadgets) { + display_gadget_lf_badbytes(gadget.get_first_absolute_address(), + gadget); + } + } + + if (g_opts.badbytes.size() > 0) { + fmt::print("\n{} gadgets have been filtered because of bad-bytes.\n", + nb_gadgets_filtered); + } + } + + if (g_opts.shexa.size() > 0) { + const std::vector &hex_values = string_to_hex(g_opts.shexa); + p.search_and_display(hex_values.data(), hex_values.size(), base); + } + + if (g_opts.sint.size() > 0) { + const uint32_t val = std::strtoul(g_opts.sint.c_str(), nullptr, 16); + p.search_and_display((const uint8_t *)&val, sizeof(val), base); + } + } catch (const std::exception &e) { + enable_color(COLO_RED); + fmt::print("{}\n", e.what()); + disable_color(); + } + + return 0; +} + +``` + +`src/rp/options.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - February 19 2022 +#pragma once +#include "cpu.hpp" +#include +#include + +struct Options_t { + std::string file; + uint8_t display = 0; + uint32_t rop = 0; + CPU::E_CPU raw = CPU::E_CPU::CPU_UNKNOWN; + std::string shexa; + uint32_t maxth = 2; + std::string badbytes; + std::string sint; + std::string va; + bool allow_branches = false; + bool unique = false; + bool version = false; + bool thumb = false; + bool print_bytes = false; +}; + +extern Options_t g_opts; + +``` + +`src/rp/pe.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "executable_format.hpp" +#include "pe_struct.hpp" +#include "rpexception.hpp" + +class PE : public ExecutableFormat { +public: + std::unique_ptr get_cpu(std::ifstream &file) override { + CPU::E_CPU cpu_type = extract_information_from_binary(file); + switch (cpu_type) { + case CPU::CPU_x86: { + return std::make_unique(); + } + + case CPU::CPU_x64: { + return std::make_unique(); + } + + case CPU::CPU_ARM: { + return std::make_unique(); + } + + case CPU::CPU_ARM64: { + return std::make_unique(); + } + + default: { + RAISE_EXCEPTION("Cannot determine the CPU type"); + } + } + } + + void display_information(const VerbosityLevel lvl) const override { + ExecutableFormat::display_information(lvl); + fmt::print("PE Information:\n"); + m_pPELayout->display(lvl); + } + + std::string get_class_name() const override { return "PE"; } + + std::vector
+ get_executables_section(std::ifstream &file, + const uint64_t base) const override { + std::vector
exec_sections; + + for (const auto §ionheader : m_pPELayout->imgSectionHeaders) { + if (!(sectionheader.Characteristics & RP_IMAGE_SCN_MEM_EXECUTE)) { + continue; + } + + const auto pointertorawdata = sectionheader.PointerToRawData; + const auto virtualaddress = sectionheader.VirtualAddress; + const auto sizeofrawdata = sectionheader.SizeOfRawData; + Section sec(sectionheader.get_name().c_str(), pointertorawdata, + base + virtualaddress, sizeofrawdata); + sec.dump(file); + sec.set_props(Section::Executable); + exec_sections.push_back(std::move(sec)); + } + return exec_sections; + } + +private: + uint64_t get_image_base_address() const override { + return m_pPELayout->get_image_base_address(); + } + + CPU::E_CPU extract_information_from_binary(std::ifstream &file) override { + RP_IMAGE_DOS_HEADER imgDosHeader; + RP_IMAGE_NT_HEADERS32 imgNtHeaders32; + CPU::E_CPU cpu = CPU::CPU_UNKNOWN; + + fmt::print("Loading PE information..\n"); + + // Remember where the caller was in the file + std::streampos off = file.tellg(); + + file.seekg(0, std::ios::beg); + file.read((char *)&imgDosHeader, sizeof(RP_IMAGE_DOS_HEADER)); + + file.seekg(imgDosHeader.e_lfanew, std::ios::beg); + // Yeah, in fact, we don't know yet if it is a x86/x64 PE; so just we grab + // the signature field, FILE_HEADER and the field Magic + file.read((char *)&imgNtHeaders32, sizeof(uint32_t) + + sizeof(RP_IMAGE_FILE_HEADER) + + sizeof(uint32_t)); + + if (imgNtHeaders32.Signature != RP_IMAGE_NT_SIGNATURE) { + RAISE_EXCEPTION( + "This file doesn't seem to be a correct PE (bad IMAGE_NT_SIGNATURE)"); + } + + switch (imgNtHeaders32.FileHeader.Machine) { + case RP_IMAGE_FILE_MACHINE_I386: { + cpu = CPU::CPU_x86; + break; + } + + case RP_IMAGE_FILE_MACHINE_AMD64: { + cpu = CPU::CPU_x64; + break; + } + + case RP_IMAGE_FILE_MACHINE_ARMTHUMB2LE: { + cpu = CPU::CPU_ARM; + break; + } + + case RP_IMAGE_FILE_MACHINE_ARM64: { + cpu = CPU::CPU_ARM64; + break; + } + + default: { + RAISE_EXCEPTION("Cannot determine the CPU type"); + } + } + + // Ok, now we can allocate the good version of the PE Layout the 32bits + // version there! + if (imgNtHeaders32.OptionalHeader.Magic == + RP_IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + init_properly_PELayout(); + } else { + init_properly_PELayout(); + } + + // Now we can fill the structure + std::memcpy(&m_pPELayout->imgDosHeader, &imgDosHeader, + m_pPELayout->get_image_dos_header_size()); + + m_pPELayout->fill_nt_structures(file); + + file.seekg(off); + return cpu; + } + + template void init_properly_PELayout() { + m_pPELayout = std::make_unique>(); + if (m_pPELayout == nullptr) { + RAISE_EXCEPTION("m_PELayout allocation failed"); + } + } + + std::unique_ptr m_pPELayout; +}; + +``` + +`src/rp/pe_struct.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "coloshell.hpp" +#include "platform.h" +#include "rpexception.hpp" +#include "toolbox.hpp" +#include +#include +#include +#include +#include +#include + +// Calculate the byte offset of a field in a structure of type |type|. +#define RP_FIELD_OFFSET(type, field) \ + ((uint32_t)(uintptr_t) & (((type *)0)->field)) + +const uint16_t RP_IMAGE_DOS_SIGNATURE = 0x5A4D; // MZ +const uint32_t RP_IMAGE_NT_SIGNATURE = 0x00004550; // PE00 + +#ifdef WINDOWS +#pragma pack(push) +#pragma pack(1) +#endif + +struct RP_IMAGE_DOS_HEADER { // DOS .EXE header + uint16_t e_magic; // Magic number + uint16_t e_cblp; // Bytes on last page of file + uint16_t e_cp; // Pages in file + uint16_t e_crlc; // Relocations + uint16_t e_cparhdr; // Size of header in paragraphs + uint16_t e_minalloc; // Minimum extra paragraphs needed + uint16_t e_maxalloc; // Maximum extra paragraphs needed + uint16_t e_ss; // Initial (relative) SS value + uint16_t e_sp; // Initial SP value + uint16_t e_csum; // Checksum + uint16_t e_ip; // Initial IP value + uint16_t e_cs; // Initial (relative) CS value + uint16_t e_lfarlc; // File address of relocation table + uint16_t e_ovno; // Overlay number + std::array e_res; // Reserved words + uint16_t e_oemid; // OEM identifier (for e_oeminfo) + uint16_t e_oeminfo; // OEM information; e_oemid specific + std::array e_res2; // Reserved words + uint32_t e_lfanew; // File address of new exe header + + void display(VerbosityLevel lvl = VERBOSE_LEVEL_1) const { + w_yel_lf("-> IMAGE_DOS_HEADER:"); + + if (lvl > VERBOSE_LEVEL_1) { + display_hex_2fields_lf(e_magic, e_cblp); + display_hex_2fields_lf(e_cp, e_crlc); + display_hex_field_lf(e_cparhdr); + } + + if (lvl > VERBOSE_LEVEL_2) { + display_hex_2fields_lf(e_minalloc, e_maxalloc); + display_hex_2fields_lf(e_ss, e_sp); + display_hex_2fields_lf(e_csum, e_ip); + display_hex_2fields_lf(e_cs, e_lfarlc); + display_hex_2fields_lf(e_oemid, e_oeminfo); + } + + display_hex_field_lf(e_lfanew); + } +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +const uint16_t RP_IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b; +const uint16_t RP_IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b; +const uint16_t RP_IMAGE_FILE_MACHINE_I386 = 0x14c; +const uint16_t RP_IMAGE_FILE_MACHINE_AMD64 = 0x8664; +const uint16_t RP_IMAGE_FILE_MACHINE_ARMTHUMB2LE = 0x1c4; +const uint16_t RP_IMAGE_FILE_MACHINE_ARM64 = 0xaa64; + +struct RP_IMAGE_FILE_HEADER { + uint16_t Machine; + uint16_t NumberOfSections; + uint32_t TimeDateStamp; + uint32_t PointerToSymbolTable; + uint32_t NumberOfSymbols; + uint16_t SizeOfOptionalHeader; + uint16_t Characteristics; + + uint16_t get_size_of_optionnal_header() const { return SizeOfOptionalHeader; } + + void display(VerbosityLevel lvl = VERBOSE_LEVEL_1) const { + w_yel_lf("-> IMAGE_FILE_HEADER:"); + + if (lvl > VERBOSE_LEVEL_1) { + display_hex_2fields_lf(Machine, SizeOfOptionalHeader); + display_hex_field_lf(PointerToSymbolTable); + } + + if (lvl > VERBOSE_LEVEL_2) { + display_hex_2fields_lf(TimeDateStamp, Characteristics); + } + + display_hex_field_lf(NumberOfSections); + } +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +// +// Directory format. +// + +struct RP_IMAGE_DATA_DIRECTORY { + uint32_t VirtualAddress; + uint32_t Size; +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +const uint32_t RP_IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; + +// +// Optional header format. +// + +const uint16_t RP_IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x40; +const uint16_t RP_IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x100; + +template struct RP_IMAGE_OPTIONAL_HEADER {}; + +template <> struct RP_IMAGE_OPTIONAL_HEADER { + uint16_t Magic; + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; + uint32_t BaseOfCode; + uint32_t BaseOfData; + uint32_t ImageBase; + uint32_t SectionAlignment; + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint32_t SizeOfStackReserve; + uint32_t SizeOfStackCommit; + uint32_t SizeOfHeapReserve; + uint32_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + std::array + DataDirectory; + + void display(VerbosityLevel lvl = VERBOSE_LEVEL_1) const { + w_yel_lf("-> IMAGE_OPTIONAL_HEADER32:"); + + fmt::print(" ASLR: "); + w_red_lf((DllCharacteristics & RP_IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE) + ? "Yes" + : "No"); + + fmt::print(" NX: "); + w_red_lf((DllCharacteristics & RP_IMAGE_DLL_CHARACTERISTICS_NX_COMPAT) + ? "Yes" + : "No"); + + if (lvl > VERBOSE_LEVEL_1) { + display_hex_2fields_lf(SectionAlignment, FileAlignment); + } + + if (lvl > VERBOSE_LEVEL_2) { + display_hex_2fields_lf(SizeOfInitializedData, SizeOfUninitializedData); + display_hex_field_lf(Magic); + } + + display_hex_2fields_lf(SizeOfCode, AddressOfEntryPoint); + display_hex_2fields_lf(BaseOfCode, BaseOfCode); + display_hex_field_lf(ImageBase); + } +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +template <> struct RP_IMAGE_OPTIONAL_HEADER { + uint16_t Magic; + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; + uint32_t BaseOfCode; + uint64_t ImageBase; + uint32_t SectionAlignment; + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint64_t SizeOfStackReserve; + uint64_t SizeOfStackCommit; + uint64_t SizeOfHeapReserve; + uint64_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + std::array + DataDirectory; + + void display(VerbosityLevel lvl = VERBOSE_LEVEL_1) const { + w_yel_lf("-> IMAGE_OPTIONAL_HEADERS64:"); + + fmt::print(" ASLR: "); + w_red_lf((DllCharacteristics & RP_IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE) + ? "Yes" + : "No"); + + fmt::print(" NX: "); + w_red_lf((DllCharacteristics & RP_IMAGE_DLL_CHARACTERISTICS_NX_COMPAT) + ? "Yes" + : "No"); + + if (lvl > VERBOSE_LEVEL_1) { + display_hex_2fields_lf(SizeOfInitializedData, SizeOfUninitializedData); + } + + if (lvl > VERBOSE_LEVEL_2) { + display_hex_2fields_lf(SectionAlignment, FileAlignment); + display_hex_field_lf(Magic); + } + + display_hex_2fields_lf(SizeOfCode, AddressOfEntryPoint); + display_hex_field_lf(BaseOfCode); + display_hex_field_lf(ImageBase); + } +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +using RP_IMAGE_OPTIONAL_HEADER32 = RP_IMAGE_OPTIONAL_HEADER; +using RP_IMAGE_OPTIONAL_HEADER64 = RP_IMAGE_OPTIONAL_HEADER; + +// +// Section header format. +// + +const uint32_t RP_IMAGE_SCN_MEM_EXECUTE = 0x20'00'00'00; +const uint32_t RP_IMAGE_SIZEOF_SHORT_NAME = 8; + +struct RP_IMAGE_SECTION_HEADER { + std::array Name; + union { + uint32_t PhysicalAddress; + uint32_t VirtualSize; + } Misc; + uint32_t VirtualAddress; + uint32_t SizeOfRawData; + uint32_t PointerToRawData; + uint32_t PointerToRelocations; + uint32_t PointerToLinenumbers; + uint16_t NumberOfRelocations; + uint16_t NumberOfLinenumbers; + uint32_t Characteristics; + + std::string get_name() const { + uint8_t name_null_terminated[RP_IMAGE_SIZEOF_SHORT_NAME + 1] = {}; + memcpy(name_null_terminated, Name.data(), Name.size()); + return (char *)name_null_terminated; + } + + void display(VerbosityLevel lvl = VERBOSE_LEVEL_1) const { + w_yel_lf("-> IMAGE_SECTION_HEADER"); + + fmt::print(" {}\n", get_name()); + + if (lvl > VERBOSE_LEVEL_1) { + display_hex_field_lf(Characteristics); + } + + display_hex_2fields_lf(Misc.PhysicalAddress, VirtualAddress); + display_hex_2fields_lf(SizeOfRawData, PointerToRawData); + } +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +template struct RP_IMAGE_NT_HEADERS { + uint32_t Signature; + RP_IMAGE_FILE_HEADER FileHeader; + RP_IMAGE_OPTIONAL_HEADER OptionalHeader; + + // Keep in mind this offset is relative to the NT Header! So if you want the + // PA of the first section: get_offset_first_section() + + // IMAGE_DOS_HEADER.e_lfanew + uintptr_t get_offset_first_section() const { + return uintptr_t(RP_FIELD_OFFSET(RP_IMAGE_NT_HEADERS, OptionalHeader) + + FileHeader.SizeOfOptionalHeader); + } + + void display(VerbosityLevel lvl = VERBOSE_LEVEL_1) const { + w_yel_lf("-> IMAGE_NT_HEADERS:"); + + if (lvl > VERBOSE_LEVEL_1) { + if (FileHeader.get_size_of_optionnal_header() >= sizeof(OptionalHeader)) + OptionalHeader.display(lvl); + } + + if (lvl > VERBOSE_LEVEL_2) { + display_hex_field_lf(Signature); + } + + FileHeader.display(lvl); + } +} +#ifdef LINUX +__attribute__((packed)) +#endif +; + +using RP_IMAGE_NT_HEADERS32 = RP_IMAGE_NT_HEADERS; +using RP_IMAGE_NT_HEADERS64 = RP_IMAGE_NT_HEADERS; + +#ifdef WINDOWS +#pragma pack(pop) +#endif + +struct PortableExecutableLayout { + RP_IMAGE_DOS_HEADER imgDosHeader; + std::vector imgSectionHeaders; + + virtual ~PortableExecutableLayout() = default; + virtual void display(VerbosityLevel lvl = VERBOSE_LEVEL_1) const { + imgDosHeader.display(lvl); + } + + uint32_t get_image_dos_header_size() const { + return sizeof(RP_IMAGE_DOS_HEADER); + } + + uint32_t get_image_section_header_size() const { + return sizeof(RP_IMAGE_SECTION_HEADER); + } + + virtual uint32_t get_nt_headers_size() const = 0; + virtual void fill_nt_structures(std::ifstream &file) = 0; + virtual uint64_t get_image_base_address() const = 0; +}; + +/* Some magic..and ABSTRACTION */ +template struct PELayout : public PortableExecutableLayout { + RP_IMAGE_NT_HEADERS imgNtHeaders; + + uint32_t get_nt_headers_size() const override { + return sizeof(RP_IMAGE_NT_HEADERS); + } + + void display(VerbosityLevel lvl = VERBOSE_LEVEL_1) const override { + PortableExecutableLayout::display(lvl); + imgNtHeaders.display(lvl); + if (lvl > VERBOSE_LEVEL_1) { + for (const auto §ionheader : imgSectionHeaders) + sectionheader.display(); + } + } + + void fill_nt_structures(std::ifstream &file) override { + // Remember where the caller was in the file + std::streampos off = file.tellg(); + + file.seekg(imgDosHeader.e_lfanew, std::ios::beg); + file.read((char *)&imgNtHeaders, get_nt_headers_size()); + + file.seekg(imgDosHeader.e_lfanew, std::ios::beg); + // This offset is relative to the NT Header, do not forget to move the file + // pointer on it + file.seekg(imgNtHeaders.get_offset_first_section(), std::ios::cur); + + for (uint32_t i = 0; i < imgNtHeaders.FileHeader.NumberOfSections; ++i) { + RP_IMAGE_SECTION_HEADER imgSectionHeader; + file.read((char *)&imgSectionHeader, get_image_section_header_size()); + imgSectionHeaders.push_back(std::move(imgSectionHeader)); + } + + file.seekg(off); + } + + uint64_t get_image_base_address() const override { + return imgNtHeaders.OptionalHeader.ImageBase; + } +}; + +using PELayout32 = PELayout; +using PELayout64 = PELayout; + +``` + +`src/rp/platform.h`: + +```h +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#if defined(__i386__) || defined(_M_IX86) +#define ARCH_X86 +#elif defined(__amd64__) || defined(_M_X64) +#define ARCH_X64 +#elif defined(__arm64__) || defined(__aarch64__) +#define ARCH_ARM64 +#else +#error Platform not supported. +#endif + +#if defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) +#define WINDOWS + +#define SYSTEM_PLATFORM "Windows" +#define strcasecmp _stricmp +#if defined ARCH_X86 +#define WINDOWS_X86 +#elif defined ARCH_X64 +#define WINDOWS_X64 +#endif +#elif defined(linux) || defined(__linux) || defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || defined(__MACH__) || defined(__OpenBSD__) +#define LINUX + +#if defined(linux) || defined(__linux) +#define SYSTEM_PLATFORM "Linux" +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#define SYSTEM_PLATFORM "FreeBSD" +#elif defined(__MACH__) +#define SYSTEM_PLATFORM "Mac OSX" +#elif defined(__OpenBSD__) +#define SYSTEM_PLATFORM "OpenBSD" +#else +#error An error occured +#endif + +#if defined ARCH_X86 +#define LINUX_X86 +#elif defined ARCH_X64 +#define LINUX_X64 +#elif defined ARCH_ARM64 +#define LINUX_ARM64 +#endif + +#else +#error Platform not supported. +#endif + +#ifdef WINDOWS +#undef UNICODE +#endif + +#ifdef LINUX +#endif + +#define x86Version uint32_t +#define x64Version uint64_t + +``` + +`src/rp/program.cpp`: + +```cpp +// Axel '0vercl0k' Souchet - January 12 2022 +#include "program.hpp" +#include "arm.hpp" +#include "coloshell.hpp" +#include "executable_format.hpp" +#include "raw.hpp" +#include "rpexception.hpp" +#include "section.hpp" +#include "toolbox.hpp" +#include "x64.hpp" +#include "x86.hpp" +#include +#include +#include +#include +#include +#include +#include + +Program::Program(const std::string &program_path, const CPU::E_CPU arch) { + uint32_t magic_dword = 0; + + fmt::print("Trying to open '{}'..\n", program_path); + m_file.open(program_path.c_str(), std::ios::binary); + if (!m_file.is_open()) { + RAISE_EXCEPTION("Cannot open the file"); + } + + if (arch != CPU::CPU_UNKNOWN) { + // If we know the CPU in the constructor, it is a raw file + m_exformat = std::make_unique(); + + switch (arch) { + case CPU::CPU_x86: { + m_cpu = std::make_unique(); + break; + } + + case CPU::CPU_x64: { + m_cpu = std::make_unique(); + break; + } + + case CPU::CPU_ARM: { + m_cpu = std::make_unique(); + break; + } + + case CPU::CPU_ARM64: { + m_cpu = std::make_unique(); + break; + } + + default: { + RAISE_EXCEPTION("Don't know your architecture"); + } + } + } else { + // This isn't a raw file, we have to determine the executable format and the + // cpu + m_file.read((char *)&magic_dword, sizeof(magic_dword)); + + m_exformat = get_executable_format(magic_dword); + if (m_exformat == nullptr) { + RAISE_EXCEPTION("get_executable_format fails"); + } + + m_cpu = m_exformat->get_cpu(m_file); + if (m_cpu == nullptr) { + RAISE_EXCEPTION("get_cpu failed"); + } + } + + fmt::print("FileFormat: {}, Arch: {}\n", m_exformat->get_class_name(), + m_cpu->get_class_name()); +} + +void Program::display_information(const VerbosityLevel lvl) { + m_exformat->display_information(lvl); +} + +GadgetMultiset Program::find_gadgets(const uint32_t depth, + const uint32_t disass_engine_options, + const size_t n_max_thread, + const uint64_t base) { + // To do a ROP gadget research, we need to know the executable section + auto executable_sections = m_exformat->get_executables_section(m_file, base); + if (executable_sections.size() == 0) { + fmt::print("It seems your binary haven't executable sections.\n"); + } + + std::queue
jobs_queue; + for (auto &executable_section : executable_sections) { + jobs_queue.push(executable_section); + } + + GadgetMultiset gadgets_found; + std::vector> thread_pool; + std::mutex m; + while (jobs_queue.size() != 0) { + if (thread_pool.size() < n_max_thread) { + auto section = std::move(jobs_queue.front()); + jobs_queue.pop(); + auto Lambda = [&](Section section) { + const auto section_buffer = section.get_section_buffer(); + const auto vaddr = section.get_vaddr(); + m_cpu->find_gadget_in_memory(section_buffer, vaddr, depth, + gadgets_found, disass_engine_options, m); + }; + thread_pool.emplace_back( + std::async(std::launch::async, Lambda, std::move(section))); + } else { + // Wait for a thread to finish + for (auto it = thread_pool.begin(); it != thread_pool.end();) { + if (it->wait_for(std::chrono::milliseconds(1)) == + std::future_status::ready) { + it->get(); + it = thread_pool.erase(it); + } else { + it++; + } + } + } + } + + // Wait for potentially unfinished threads + for (auto &f : thread_pool) { + f.get(); + } + + return gadgets_found; +} + +void Program::search_and_display(const uint8_t *hex_values, const size_t size, + const uint64_t base) { + const auto &executable_sections = + m_exformat->get_executables_section(m_file, base); + if (executable_sections.size() == 0) { + fmt::print("It seems your binary haven't executable sections.\n"); + } + + for (const auto &executable_section : executable_sections) { + const auto &offsets = executable_section.search_in_memory(hex_values, size); + for (const auto &offset : offsets) { + const uint64_t va_section = executable_section.get_vaddr(); + const uint64_t va = va_section + offset; + display_offset_lf(va, hex_values, size); + } + } +} + +uint64_t Program::get_image_base_address() const { + return m_exformat->get_image_base_address(); +} +``` + +`src/rp/program.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include +#include +#include + +#include "elf.hpp" + +/*! \class Program + * + * A program is the combination between two things: a CPU which will be used by + * the disassembler, and an ExecutableFormat in order to correctly extract the + * code (to find cool stuff in) + */ +class Program { +public: + /*! + * \brief Program instanciation requires a path where it can find your binary + * + * \param program_path: The path of your binary + */ + Program(const std::string &program_path, + const CPU::E_CPU arch = CPU::CPU_UNKNOWN); + + /*! + * \brief Display information concerning the executable format (section + * address, entry point, stuff like that) + * + * \param lvl: Set the verbosity level you want + */ + void display_information(const VerbosityLevel lvl = VERBOSE_LEVEL_1); + + /*! + * \brief Find all the rop gadgets + * + * \param depth: Set the depth of the research (don't forget the ending + * instruction doesn't count -- so if you want only ending instruction, depth + * = 0) + * \param gadgets: The gadgets found \param disass_engine_options: + * Options you want to pass to the disassembly engine + * + */ + GadgetMultiset find_gadgets(const uint32_t depth, + const uint32_t disass_engine_options, + const size_t n_max_thread, const uint64_t base); + + /*! + * \brief Find hex values in the section of the program + * + * \param hex_values: It is a pointer on where it can find the bytes to find + * in memory + * \param size: It is the size of the buffer hex_values + */ + void search_and_display(const uint8_t *hex_values, const size_t size, + const uint64_t base); + + /*! + * \brief Get the base address of the program + * + * \return size: Base address of the program + */ + uint64_t get_image_base_address() const; + +private: + std::unique_ptr m_cpu; /*!< a pointer on the CPU used by your program*/ + + std::unique_ptr + m_exformat; /*!< a pointer on the ExecutableFormat used by your program*/ + + std::ifstream m_file; /*!< the file descriptor*/ +}; + +``` + +`src/rp/raw.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "executable_format.hpp" + +class Raw : public ExecutableFormat { +public: + std::unique_ptr get_cpu(std::ifstream &file) override { + // Don't need this method + return nullptr; + } + + std::string get_class_name() const override { return "raw"; } + + std::vector
+ get_executables_section(std::ifstream &file, + const uint64_t base) const override { + std::vector
executable_sections; + + uint64_t raw_file_size = get_file_size(file); + + // It is a raw file -> we have only one "virtual" section + Section sect(".raw", 0, base, raw_file_size); + sect.dump(file); + sect.set_props(Section::Executable); + executable_sections.push_back(std::move(sect)); + return executable_sections; + } + +private: + uint64_t get_image_base_address() const override { return 0; } + + uint64_t raw_offset_to_va(const uint64_t absolute_raw_offset, + const uint64_t absolute_raw_offset_section) const { + return absolute_raw_offset; + } +}; + +``` + +`src/rp/ropsearch_algorithm.cpp`: + +```cpp +// Axel '0vercl0k' Souchet - January 12 2022 +#include "ropsearch_algorithm.hpp" +#include "options.hpp" +#include "safeint.hpp" +#include + +void find_all_gadget_from_ret(const std::vector &memory, + uint64_t vaddr, + const InstructionInformation &ending_instr_disasm, + const uint32_t depth, GadgetMultiset &gadgets, + DisassEngineWrapper &disass_engine) { + const uint8_t *data = memory.data(); + const uint8_t *end_data = memory.data() + memory.size(); + const uint32_t alignement = disass_engine.get_alignement(); + const uint32_t size_biggest_instruction = + disass_engine.get_size_biggest_instruction(); + + // We go back, trying to create the longuest gadget possible with the longuest + // instructions + uintptr_t EIP = + ending_instr_disasm.address - (depth * size_biggest_instruction); + uintptr_t VirtualAddr = ending_instr_disasm.virtual_address_in_memory - + (depth * size_biggest_instruction); + + // going back yeah, but not too much :)) + if (EIP < uintptr_t(data)) { + EIP = uintptr_t(data); + VirtualAddr = uintptr_t(vaddr); + } + + while (EIP < ending_instr_disasm.address) { + std::vector list_of_instr; + + uint64_t gadget_start_address = 0; + + // save where we were in memory + uintptr_t saved_eip = EIP; + uintptr_t saved_vaddr = VirtualAddr; + + bool is_a_valid_gadget = false; + + // now we'll try to find suitable sequence + for (uint32_t nb_ins = 0; nb_ins < depth; nb_ins++) { + DisassEngineReturn ret; + const uint8_t *EIP_ = (uint8_t *)EIP; + InstructionInformation instr = + disass_engine.disass(EIP_, end_data - EIP_, VirtualAddr, ret); + + const bool is_valid = g_opts.allow_branches || (!instr.is_branch); + // if the instruction isn't valid, ends this function + if (ret == UnknownInstruction || ret == OutOfBlock || !is_valid) { + break; + } + + // Grab the bytes if we'll need to print them later + if (g_opts.print_bytes) { + instr.bytes.assign(EIP_, EIP_ + instr.size); + } + + // Sets the begining address of the gadget as soon as we find the first + // one + if (list_of_instr.size() == 0) { + gadget_start_address = EIP - uintptr_t(data); + } + + list_of_instr.emplace_back(instr.disassembly, instr.bytes); + + EIP += instr.size; + VirtualAddr += instr.size; + + // if the address of the latest instruction found points on the ending + // one, we have a winner + if (EIP == ending_instr_disasm.address) { + is_a_valid_gadget = true; + // I reach the ending instruction without depth instruction + break; + } + + // if we point after the ending one, it's not a valid sequence + if (EIP > ending_instr_disasm.address) { + break; + } + } + + if (is_a_valid_gadget) { + // we have a valid gadget, time to build it ; add the instructions found & + // finally add the ending instruction + + // Don't forget to include the ending instruction in the chain of + // instruction + list_of_instr.emplace_back(ending_instr_disasm.disassembly, + ending_instr_disasm.bytes); + + Gadget gadget(gadget_start_address); + + // Now we populate our gadget with the instructions previously found.. + gadget.add_instructions(list_of_instr, vaddr); + gadgets.insert(std::move(gadget)); + } + + // goto the next aligned-byte + EIP = saved_eip + alignement; + VirtualAddr = saved_vaddr + alignement; + } +} + +void find_rop_gadgets(const std::vector §ion, const uint64_t vaddr, + const uint32_t depth, + GadgetMultiset &merged_gadgets_final, + DisassEngineWrapper &disass_engine, std::mutex &m) { + GadgetMultiset merged_gadgets; + const uint8_t *data = section.data(); + const uint64_t size = section.size(); + const uint32_t alignement = disass_engine.get_alignement(); + for (uint64_t offset = 0; offset < size; offset += alignement) { + DisassEngineReturn ret; + InstructionInformation ret_instr = disass_engine.disass( + data + offset, size - offset, SafeIntAdd(vaddr, offset), ret); + + // OK either this is an unknow opcode or the instruction is too long; in + // either case we goto the next one. + if (ret == UnknownInstruction || ret == OutOfBlock) { + continue; + } + + if (!ret_instr.is_valid_ending_instr) { + continue; + } + + // Grab the bytes if we'll need to print them later + if (g_opts.print_bytes) { + ret_instr.bytes.assign(data + offset, data + offset + ret_instr.size); + } + + // Do not forget to add the ending instruction only -- we give to the user + // all gadget with < depth instruction + std::vector only_ending_instr; + + only_ending_instr.emplace_back(ret_instr.disassembly, ret_instr.bytes); + + Gadget gadget_with_one_instr(offset); + + // the gadget will only have 1 ending instruction + gadget_with_one_instr.add_instructions(only_ending_instr, vaddr); + merged_gadgets.insert(std::move(gadget_with_one_instr)); + + // if we want to see gadget with more instructions + if (depth > 0) { + find_all_gadget_from_ret(section, vaddr, ret_instr, depth, merged_gadgets, + disass_engine); + } + } + + m.lock(); + merged_gadgets_final.merge(merged_gadgets); + m.unlock(); +} +``` + +`src/rp/ropsearch_algorithm.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "disassenginewrapper.hpp" +#include "gadget.hpp" +#include +#include +#include + +void find_rop_gadgets(const std::vector §ion, const uint64_t vaddr, + const uint32_t depth, GadgetMultiset &merged_gadgets, + DisassEngineWrapper &disass_engine, std::mutex &m); + +``` + +`src/rp/rpexception.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include +#include +#include + +/** + * \def RAISE_EXCEPTION(msg) + * It raises an exception with a detailed explanation in "msg" + * + * \param msg: The message that will be associated to the exeception raised + */ +#define RAISE_EXCEPTION(msg) \ + throw RpException(__FILE__, __LINE__, __FUNCTION__, msg) + +/*! \class RpException + * + * RpException is there to have a simple exception report ; easier to corret + * bugs + */ +class RpException : public std::exception { +public: + /*! + * \brief Build an RpException + * + * \param filename: The name of the file where the exception has been raised + * \param line: The line where the exception has been raised + * \param funct: The function name where the exception has been raised + * \param msg: It is a message that describes the reason of the exception + */ + RpException(const char *filename, uint32_t line, const char *funct, + const char *msg) + : m_filename(filename), m_msg(msg), m_function_name(funct), m_line(line) { + m_report = fmt::format("[EXCEPTION REPORT]:\n\t Raised in {}:{}\n\t More " + "precisely in {}\n\t Further infos: {}", + m_filename, m_line, m_function_name, m_msg); + } + + /*! + * \brief Obtain the reason the exception + * + * \return A message describing the exception + */ + const char *what(void) const throw() { return m_report.c_str(); } + +private: + std::string m_filename, /*!< the name of the file where the exception has been + raised */ + m_msg, /*!< the message that describes the reason of the exception */ + m_function_name, /*!< the function name where the exception has been + raised */ + m_report; /*!< the name of the file where the exception has been raised */ + + uint32_t m_line; /*!< the line where the exception has been raised */ +}; + +``` + +`src/rp/safeint.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "rpexception.hpp" +#include + +// The purpose of this class is to avoid integer overflow ; if one is detected, +// an exception is raised +template +typename std::enable_if::value, T>::type +SafeIntAdd(const T a, const T b) { + if (a > (std::numeric_limits::max() - b)) { + RAISE_EXCEPTION("Integer-overflow detected."); + } + + return a + b; +} + +``` + +`src/rp/section.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "safeint.hpp" +#include "toolbox.hpp" +#include +#include +#include + +/*! \class Section + * + * Each binary is divided in section, actually it is a chunk of the binary of + * a specific size which contains code or data + */ +class Section { +public: + /*! The different rights a section can have ; those rights are usually + * combined by an OR operation */ + enum Properties { + Readable, /*!< the section is readable*/ + Writeable, /*!< the section is writeable*/ + Executable /*!< the section is executable*/ + }; + + /*! + * \brief The constructor will make a copy of the memory in its own buffer + * + * \param name: The name of the section + * \param offset: It is the offset in file where you can find the section + * \param vaddr: Virtual address of the section + * \param size: It is the size of the section + */ + Section(const char *name, const uint64_t offset, const uint64_t vaddr, + const uint64_t size) + : m_name(name), m_offset(offset), m_size(size), m_vaddr(vaddr) {} + + /*! + * \brief Get the name of the section + * + * \return the name of the section + */ + std::string get_name() const { return m_name; } + + /*! + * \brief Get the size of the section + * + * \return the size of the section + */ + uint64_t get_size() const { return m_size; } + + /*! + * \brief Get the content of the section (it's the internal copy) + * + * \return a pointer on the buffer + */ + const std::vector &get_section_buffer() const { return m_section; } + + /*! + * \brief Get the (raw) offset of the section ; in other word, where it was + * found in the binary + * + * \return the offset where the section was found in the binary + */ + const uint64_t get_offset() const { return m_offset; } + + /*! + * \brief Search in memory a sequence of bytes + * + * \param val: A pointer on the bytes you want to search + * \param size: The size of the buffer + * + * \return a list of offset (relative to the section) where it found the + * sequence of bytes + */ + std::vector search_in_memory(const uint8_t *val, + const size_t size) const { + std::vector val_found; + for (uint64_t offset = 0; (offset + size) < m_size; ++offset) { + if (std::memcmp(m_section.data() + offset, val, size) == 0) { + val_found.push_back(offset); + } + } + + return val_found; + } + + /*! + * \brief Dump the raw section of your file + * + * \param file: The file + */ + void dump(std::ifstream &file) { + // NB: std::streampos performs unsigned check + uint64_t fsize = get_file_size(file); + if (SafeIntAdd(m_offset, m_size) > fsize) { + RAISE_EXCEPTION("Your file seems to be screwed up"); + } + + std::streampos backup = file.tellg(); + + file.seekg((uint32_t)m_offset, std::ios::beg); + m_section.resize((uint32_t)m_size); + + file.read((char *)m_section.data(), (uint32_t)m_size); + + file.seekg(backup); + } + + /*! + * \brief Set the properties of the section + * + * \param props: The properties of the section + */ + void set_props(const Properties props) { m_props = props; } + + uint64_t get_vaddr() const { return m_vaddr; } + +private: + std::string m_name; /*!< the name of the section*/ + + const uint64_t m_offset; /*!< the raw offset of the section*/ + + const uint64_t m_size; /*!< the size of the section of the section*/ + + Properties m_props; /*!< the properties of the section*/ + + std::vector m_section; /*!< the section content*/ + + uint64_t m_vaddr; /* !< the virtual address of the section*/ +}; + +``` + +`src/rp/toolbox.cpp`: + +```cpp +// Axel '0vercl0k' Souchet - January 12 2022 +#include "toolbox.hpp" +#include "elf.hpp" +#include "macho.hpp" +#include "pe.hpp" +#include "rpexception.hpp" +#include +#include +#include +#include + +std::string verbosity_to_string(const VerbosityLevel lvl) { + switch (lvl) { + case VERBOSE_LEVEL_1: { + return "VERBOSE_LEVEL_1"; + } + + case VERBOSE_LEVEL_2: { + return "VERBOSE_LEVEL_2"; + } + + case VERBOSE_LEVEL_3: { + return "VERBOSE_LEVEL_3"; + } + } + + std::abort(); + return "Unknwon"; +} + +std::streampos get_file_size(std::ifstream &file) { + std::streampos backup = file.tellg(); + + file.seekg(0, std::ios::beg); + std::streampos fsize = file.tellg(); + + file.seekg(0, std::ios::end); + fsize = file.tellg() - fsize; + + file.seekg(backup); + return fsize; +} + +uint64_t va_to_integer(std::string va) { + // Look for backticks; WinDbg splits a QWORD in two with one. We'll get rid of + // it if we find one as this makes it easier to copy the address directly off + // the debugger. It also means that if we find one, we'll assume the address + // is specified in base 16 so we'll force that. + const auto it = std::remove(va.begin(), va.end(), '`'); + const int radix = it != va.end() ? 16 : 0; + // If `std::remove` returned a valid iterator, this is where we want + // `strtoull` to stop; so let's terminate the string there. + if (it != va.end()) { + *it = 0; + } + + return std::strtoull(va.c_str(), nullptr, radix); +} + +// this function is completely inspirated from the previous work of jonathan +// salwan +bool is_matching(const std::string &str, const std::string &pattern) { + // we have to check the *entire* pattern + if (pattern.size() > str.size()) { + return false; + } + + size_t i = 0, max = std::min(str.length(), pattern.length()); + bool it_matches = true; + + while (i < max) { + if (pattern.at(i) != '?' && pattern.at(i) != str.at(i)) { + it_matches = false; + break; + } + + ++i; + } + + return it_matches; +} + +bool is_hex_char(const char c) { + return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F')); +} + +std::vector string_to_hex(const std::string &hex) { + const size_t len = hex.size(); + std::vector bytes; + if (len == 0) { + return bytes; + } + + for (size_t i = 0; i < len; i++) { + uint8_t byte = 0; + if (hex[i] == '\\') { + if ((i + 3) >= len) { + RAISE_EXCEPTION("Your hex values aren't formated correctly"); + } + + const bool hex_chars = is_hex_char(hex[i + 2]) && is_hex_char(hex[i + 3]); + if (hex[i + 1] != 'x' || !hex_chars) { + RAISE_EXCEPTION("Your hex values aren't formated correctly"); + } + + const char str_byte[3] = {hex[i + 2], hex[i + 3], 0}; + byte = uint8_t(strtoul(str_byte, nullptr, 16)); + i += 3; + } else { + byte = hex[i]; + } + + bytes.push_back(byte); + } + + return bytes; +} + +GadgetSet only_unique_gadgets(GadgetMultiset &list_gadgets, + const std::vector &badbytes, + uint64_t &nb_gadgets_filtered) { + GadgetSet unique_gadgets; + // Now we have a list of gadget, cool, but we want to keep only the unique! + while (!list_gadgets.empty()) { + auto node = list_gadgets.extract(list_gadgets.begin()); + // Let's see if the VA has badbytes.. + const auto offset = node.value().get_first_offset(); + const auto va_section = node.value().get_first_va_section(); + const auto va = offset + va_section; + if (does_badbytes_filter_apply(va, badbytes)) { + nb_gadgets_filtered++; + continue; + } + + // If not, let's try to insert it.. + auto [iterator, inserted] = unique_gadgets.insert(std::move(node.value())); + if (!inserted) { + // ..seems like this gadget was already in the set, let's just add another + // instance to the existing one. + iterator->add_new_one(offset, va_section); + } + } + + return unique_gadgets; +} + +bool does_badbytes_filter_apply(const uint64_t va, + const std::vector &badbytes) { + const uint8_t f = (va >> 24) & 0xff; + const uint8_t s = (va >> 16) & 0xff; + const uint8_t t = (va >> 8) & 0xff; + const uint8_t l = (va >> 0) & 0xff; + + for (const auto &badbyte : badbytes) { + if ((f == badbyte) || (s == badbyte) || (t == badbyte) || (l == badbyte)) { + return true; + } + } + + return false; +} + +std::unique_ptr +get_executable_format(const uint32_t magic_dword) { + if (uint16_t(magic_dword) == RP_IMAGE_DOS_SIGNATURE) { + return std::make_unique(); + } + + switch (magic_dword) { + case 0x464C457F: { + return std::make_unique(); + } + + // this is for x64 + case 0xFEEDFACF: + // this one for x86 + case 0xFEEDFACE: { + return std::make_unique(); + } + + case 0xBEBAFECA: { + RAISE_EXCEPTION("Hmm, actually I don't handle OSX Universal binaries. " + "You must extract them manually."); + break; + } + + default: { + RAISE_EXCEPTION("Cannot determine the executable format used"); + } + } +} + +``` + +`src/rp/toolbox.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "gadget.hpp" +#include +#include +#include +#include +#include + +/* Choose your verbosity level */ +enum VerbosityLevel { + VERBOSE_LEVEL_1 = 1, + VERBOSE_LEVEL_2 = 2, + VERBOSE_LEVEL_3 = 3 +}; + +/** + * \fn std::string verbosity_to_string(const VerbosityLevel lvl) + * \brief Convert a verbosity level in a string representation + * + * \param lvl: the verbosity level + * + * \return the string representation + */ +std::string verbosity_to_string(const VerbosityLevel lvl); + +/** + * \fn uint64_t va_to_integer(std::string va) + * \brief Convert va to an integer + * + * \param va The virtual address string + */ +uint64_t va_to_integer(std::string va); + +/** + * \fn std::streampos get_file_size(std::ifstream &file) + * \brief Get the size in byte of your file + * + * \param file: the file + * + * \return the size in byte of the file + */ +std::streampos get_file_size(std::ifstream &file); + +/** + * \fn std::vector string_to_hex(const std::string &hex) + * \brief Enable a color in your shell + * + * \param hex: The string that represents your raw hex values -- for example + * \x41BC\x90 => we want [0x41, 0x42, 0x43, 0x90] in memory + * + * \return a vector that contains the converted bytes + */ +std::vector string_to_hex(const std::string &hex); + +/** + * \fn bool is_matching(const std::string &disass, const char* p) + * \brief Try to match a string with a pattern: in this pattern you can use the + * special character '?' that represents anything + * + * \paraqm str: the string + * \param p: the pattern you will apply to str + * + * \return true if the pattern matches with str, else false + */ +bool is_matching(const std::string &str, const std::string &p); + +/** + * \fn bool is_hex_char(const char c) + * \brief Is the character c is an hexadecimal character ? + * + * \param c: the character + * + * \return true if the character is an hexadecimal char, else false + */ +bool is_hex_char(const char c); + +/** + * \fn GadgetOrderedSet only_unique_gadgets(GadgetSet &list_gadgets) + * \brief It keeps only the unique gadgets + * + * \param list_gadgets: It is the gadget list with duplicates + * \param badbytes: The list of bytes you don't want in VAs + * \param nb_gadgets_filtered: The number of gadgets that were filtered because of the + * badbytes. + * + * \return A set of unique gadgets that don't have any badbytes + */ +GadgetSet only_unique_gadgets(GadgetMultiset &list_gadgets, + const std::vector &badbytes, + uint64_t &nb_gadgets_filtered); + +/** + * \fn bool does_badbytes_filter_apply(const uint64_t va, const + * std::vector &badbytes) + * + * \brief Return true if va has a bad byte (taken from badbytes) + * + * \param va: It is the VA to check + * \param badbytes: The list of bytes you don't want in VAs + * + * \return true if va has at least one bad byte, else false + */ +bool does_badbytes_filter_apply(const uint64_t va, + const std::vector &badbytes); + +/*! + * \brief Give you a PE/ELF instance (based mostly on the magic signature) + * + * \param magic_dword: It is a dword that allows to deduce which + * ExecutableFormat is used by the binary + * + * \return A pointer on the correct ExecutableFormat deduced thanks to the + * magic_dword argument + */ +class ExecutableFormat; +std::unique_ptr +get_executable_format(const uint32_t magic_dword); + +``` + +`src/rp/x64.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "cpu.hpp" +#include "intelbeaengine.hpp" +#include "ropsearch_algorithm.hpp" + +class x64 : public CPU { +public: + std::string get_class_name() const override { return "x64"; } + + void find_gadget_in_memory(const std::vector &p_memory, + const uint64_t vaddr, const uint32_t depth, + GadgetMultiset &gadgets, + uint32_t disass_engine_options, + std::mutex &m) override { + IntelBeaEngine bea_engine(IntelBeaEngine::x64); + DisassEngineWrapper &engine = bea_engine; + find_rop_gadgets(p_memory, vaddr, depth, gadgets, engine, m); + } +}; + +``` + +`src/rp/x86.hpp`: + +```hpp +// Axel '0vercl0k' Souchet - January 12 2022 +#pragma once + +#include "cpu.hpp" +#include + +#include "intelbeaengine.hpp" + +class x86 : public CPU { +public: + std::string get_class_name() const override { return "x86"; } + + void find_gadget_in_memory(const std::vector &p_memory, + const uint64_t vaddr, const uint32_t depth, + GadgetMultiset &gadgets, + uint32_t disass_engine_options, + std::mutex &m) override { + // BeaRopGadgetFinder bea(BeaRopGadgetFinder::x86, depth); + // bea.find_rop_gadgets(p_memory, size, vaddr, gadgets); + IntelBeaEngine bea_engine(IntelBeaEngine::x86); + DisassEngineWrapper &engine = bea_engine; + find_rop_gadgets(p_memory, vaddr, depth, gadgets, engine, m); + } +}; + +``` + +`src/third_party/CLI11/CLI11.hpp`: + +```hpp +// CLI11: Version 2.1.2 +// Originally designed by Henry Schreiner +// https://github.com/CLIUtils/CLI11 +// +// This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts +// from: v2.1.2 +// +// CLI11 2.1.2 Copyright (c) 2017-2021 University of Cincinnati, developed by Henry +// Schreiner under NSF AWARD 1414736. All rights reserved. +// +// Redistribution and use in source and binary forms of CLI11, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +// Standard combined includes: +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define CLI11_VERSION_MAJOR 2 +#define CLI11_VERSION_MINOR 1 +#define CLI11_VERSION_PATCH 2 +#define CLI11_VERSION "2.1.2" + + + + +// The following version macro is very similar to the one in pybind11 +#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER) +#if __cplusplus >= 201402L +#define CLI11_CPP14 +#if __cplusplus >= 201703L +#define CLI11_CPP17 +#if __cplusplus > 201703L +#define CLI11_CPP20 +#endif +#endif +#endif +#elif defined(_MSC_VER) && __cplusplus == 199711L +// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented) +// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer +#if _MSVC_LANG >= 201402L +#define CLI11_CPP14 +#if _MSVC_LANG > 201402L && _MSC_VER >= 1910 +#define CLI11_CPP17 +#if __MSVC_LANG > 201703L && _MSC_VER >= 1910 +#define CLI11_CPP20 +#endif +#endif +#endif +#endif + +#if defined(CLI11_CPP14) +#define CLI11_DEPRECATED(reason) [[deprecated(reason)]] +#elif defined(_MSC_VER) +#define CLI11_DEPRECATED(reason) __declspec(deprecated(reason)) +#else +#define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason))) +#endif + + + + +// C standard library +// Only needed for existence checking +#if defined CLI11_CPP17 && defined __has_include && !defined CLI11_HAS_FILESYSTEM +#if __has_include() +// Filesystem cannot be used if targeting macOS < 10.15 +#if defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 +#define CLI11_HAS_FILESYSTEM 0 +#else +#include +#if defined __cpp_lib_filesystem && __cpp_lib_filesystem >= 201703 +#if defined _GLIBCXX_RELEASE && _GLIBCXX_RELEASE >= 9 +#define CLI11_HAS_FILESYSTEM 1 +#elif defined(__GLIBCXX__) +// if we are using gcc and Version <9 default to no filesystem +#define CLI11_HAS_FILESYSTEM 0 +#else +#define CLI11_HAS_FILESYSTEM 1 +#endif +#else +#define CLI11_HAS_FILESYSTEM 0 +#endif +#endif +#endif +#endif + +#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0 +#include // NOLINT(build/include) +#else +#include +#include +#endif + + + +namespace CLI { + + +/// Include the items in this namespace to get free conversion of enums to/from streams. +/// (This is available inside CLI as well, so CLI11 will use this without a using statement). +namespace enums { + +/// output streaming for enumerations +template ::value>::type> +std::ostream &operator<<(std::ostream &in, const T &item) { + // make sure this is out of the detail namespace otherwise it won't be found when needed + return in << static_cast::type>(item); +} + +} // namespace enums + +/// Export to CLI namespace +using enums::operator<<; + +namespace detail { +/// a constant defining an expected max vector size defined to be a big number that could be multiplied by 4 and not +/// produce overflow for some expected uses +constexpr int expected_max_vector_size{1 << 29}; +// Based on http://stackoverflow.com/questions/236129/split-a-string-in-c +/// Split a string by a delim +inline std::vector split(const std::string &s, char delim) { + std::vector elems; + // Check to see if empty string, give consistent result + if(s.empty()) { + elems.emplace_back(); + } else { + std::stringstream ss; + ss.str(s); + std::string item; + while(std::getline(ss, item, delim)) { + elems.push_back(item); + } + } + return elems; +} + +/// Simple function to join a string +template std::string join(const T &v, std::string delim = ",") { + std::ostringstream s; + auto beg = std::begin(v); + auto end = std::end(v); + if(beg != end) + s << *beg++; + while(beg != end) { + s << delim << *beg++; + } + return s.str(); +} + +/// Simple function to join a string from processed elements +template ::value>::type> +std::string join(const T &v, Callable func, std::string delim = ",") { + std::ostringstream s; + auto beg = std::begin(v); + auto end = std::end(v); + auto loc = s.tellp(); + while(beg != end) { + auto nloc = s.tellp(); + if(nloc > loc) { + s << delim; + loc = nloc; + } + s << func(*beg++); + } + return s.str(); +} + +/// Join a string in reverse order +template std::string rjoin(const T &v, std::string delim = ",") { + std::ostringstream s; + for(std::size_t start = 0; start < v.size(); start++) { + if(start > 0) + s << delim; + s << v[v.size() - start - 1]; + } + return s.str(); +} + +// Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string + +/// Trim whitespace from left of string +inline std::string <rim(std::string &str) { + auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace(ch, std::locale()); }); + str.erase(str.begin(), it); + return str; +} + +/// Trim anything from left of string +inline std::string <rim(std::string &str, const std::string &filter) { + auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); + str.erase(str.begin(), it); + return str; +} + +/// Trim whitespace from right of string +inline std::string &rtrim(std::string &str) { + auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace(ch, std::locale()); }); + str.erase(it.base(), str.end()); + return str; +} + +/// Trim anything from right of string +inline std::string &rtrim(std::string &str, const std::string &filter) { + auto it = + std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); + str.erase(it.base(), str.end()); + return str; +} + +/// Trim whitespace from string +inline std::string &trim(std::string &str) { return ltrim(rtrim(str)); } + +/// Trim anything from string +inline std::string &trim(std::string &str, const std::string filter) { return ltrim(rtrim(str, filter), filter); } + +/// Make a copy of the string and then trim it +inline std::string trim_copy(const std::string &str) { + std::string s = str; + return trim(s); +} + +/// remove quotes at the front and back of a string either '"' or '\'' +inline std::string &remove_quotes(std::string &str) { + if(str.length() > 1 && (str.front() == '"' || str.front() == '\'')) { + if(str.front() == str.back()) { + str.pop_back(); + str.erase(str.begin(), str.begin() + 1); + } + } + return str; +} + +/// Add a leader to the beginning of all new lines (nothing is added +/// at the start of the first line). `"; "` would be for ini files +/// +/// Can't use Regex, or this would be a subs. +inline std::string fix_newlines(const std::string &leader, std::string input) { + std::string::size_type n = 0; + while(n != std::string::npos && n < input.size()) { + n = input.find('\n', n); + if(n != std::string::npos) { + input = input.substr(0, n + 1) + leader + input.substr(n + 1); + n += leader.size(); + } + } + return input; +} + +/// Make a copy of the string and then trim it, any filter string can be used (any char in string is filtered) +inline std::string trim_copy(const std::string &str, const std::string &filter) { + std::string s = str; + return trim(s, filter); +} +/// Print a two part "help" string +inline std::ostream &format_help(std::ostream &out, std::string name, const std::string &description, std::size_t wid) { + name = " " + name; + out << std::setw(static_cast(wid)) << std::left << name; + if(!description.empty()) { + if(name.length() >= wid) + out << "\n" << std::setw(static_cast(wid)) << ""; + for(const char c : description) { + out.put(c); + if(c == '\n') { + out << std::setw(static_cast(wid)) << ""; + } + } + } + out << "\n"; + return out; +} + +/// Print subcommand aliases +inline std::ostream &format_aliases(std::ostream &out, const std::vector &aliases, std::size_t wid) { + if(!aliases.empty()) { + out << std::setw(static_cast(wid)) << " aliases: "; + bool front = true; + for(const auto &alias : aliases) { + if(!front) { + out << ", "; + } else { + front = false; + } + out << detail::fix_newlines(" ", alias); + } + out << "\n"; + } + return out; +} + +/// Verify the first character of an option +/// - is a trigger character, ! has special meaning and new lines would just be annoying to deal with +template bool valid_first_char(T c) { return ((c != '-') && (c != '!') && (c != ' ') && c != '\n'); } + +/// Verify following characters of an option +template bool valid_later_char(T c) { + // = and : are value separators, { has special meaning for option defaults, + // and \n would just be annoying to deal with in many places allowing space here has too much potential for + // inadvertent entry errors and bugs + return ((c != '=') && (c != ':') && (c != '{') && (c != ' ') && c != '\n'); +} + +/// Verify an option/subcommand name +inline bool valid_name_string(const std::string &str) { + if(str.empty() || !valid_first_char(str[0])) { + return false; + } + auto e = str.end(); + for(auto c = str.begin() + 1; c != e; ++c) + if(!valid_later_char(*c)) + return false; + return true; +} + +/// Verify an app name +inline bool valid_alias_name_string(const std::string &str) { + static const std::string badChars(std::string("\n") + '\0'); + return (str.find_first_of(badChars) == std::string::npos); +} + +/// check if a string is a container segment separator (empty or "%%") +inline bool is_separator(const std::string &str) { + static const std::string sep("%%"); + return (str.empty() || str == sep); +} + +/// Verify that str consists of letters only +inline bool isalpha(const std::string &str) { + return std::all_of(str.begin(), str.end(), [](char c) { return std::isalpha(c, std::locale()); }); +} + +/// Return a lower case version of a string +inline std::string to_lower(std::string str) { + std::transform(std::begin(str), std::end(str), std::begin(str), [](const std::string::value_type &x) { + return std::tolower(x, std::locale()); + }); + return str; +} + +/// remove underscores from a string +inline std::string remove_underscore(std::string str) { + str.erase(std::remove(std::begin(str), std::end(str), '_'), std::end(str)); + return str; +} + +/// Find and replace a substring with another substring +inline std::string find_and_replace(std::string str, std::string from, std::string to) { + + std::size_t start_pos = 0; + + while((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } + + return str; +} + +/// check if the flag definitions has possible false flags +inline bool has_default_flag_values(const std::string &flags) { + return (flags.find_first_of("{!") != std::string::npos); +} + +inline void remove_default_flag_values(std::string &flags) { + auto loc = flags.find_first_of('{', 2); + while(loc != std::string::npos) { + auto finish = flags.find_first_of("},", loc + 1); + if((finish != std::string::npos) && (flags[finish] == '}')) { + flags.erase(flags.begin() + static_cast(loc), + flags.begin() + static_cast(finish) + 1); + } + loc = flags.find_first_of('{', loc + 1); + } + flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end()); +} + +/// Check if a string is a member of a list of strings and optionally ignore case or ignore underscores +inline std::ptrdiff_t find_member(std::string name, + const std::vector names, + bool ignore_case = false, + bool ignore_underscore = false) { + auto it = std::end(names); + if(ignore_case) { + if(ignore_underscore) { + name = detail::to_lower(detail::remove_underscore(name)); + it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { + return detail::to_lower(detail::remove_underscore(local_name)) == name; + }); + } else { + name = detail::to_lower(name); + it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { + return detail::to_lower(local_name) == name; + }); + } + + } else if(ignore_underscore) { + name = detail::remove_underscore(name); + it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { + return detail::remove_underscore(local_name) == name; + }); + } else { + it = std::find(std::begin(names), std::end(names), name); + } + + return (it != std::end(names)) ? (it - std::begin(names)) : (-1); +} + +/// Find a trigger string and call a modify callable function that takes the current string and starting position of the +/// trigger and returns the position in the string to search for the next trigger string +template inline std::string find_and_modify(std::string str, std::string trigger, Callable modify) { + std::size_t start_pos = 0; + while((start_pos = str.find(trigger, start_pos)) != std::string::npos) { + start_pos = modify(str, start_pos); + } + return str; +} + +/// Split a string '"one two" "three"' into 'one two', 'three' +/// Quote characters can be ` ' or " +inline std::vector split_up(std::string str, char delimiter = '\0') { + + const std::string delims("\'\"`"); + auto find_ws = [delimiter](char ch) { + return (delimiter == '\0') ? (std::isspace(ch, std::locale()) != 0) : (ch == delimiter); + }; + trim(str); + + std::vector output; + bool embeddedQuote = false; + char keyChar = ' '; + while(!str.empty()) { + if(delims.find_first_of(str[0]) != std::string::npos) { + keyChar = str[0]; + auto end = str.find_first_of(keyChar, 1); + while((end != std::string::npos) && (str[end - 1] == '\\')) { // deal with escaped quotes + end = str.find_first_of(keyChar, end + 1); + embeddedQuote = true; + } + if(end != std::string::npos) { + output.push_back(str.substr(1, end - 1)); + if(end + 2 < str.size()) { + str = str.substr(end + 2); + } else { + str.clear(); + } + + } else { + output.push_back(str.substr(1)); + str = ""; + } + } else { + auto it = std::find_if(std::begin(str), std::end(str), find_ws); + if(it != std::end(str)) { + std::string value = std::string(str.begin(), it); + output.push_back(value); + str = std::string(it + 1, str.end()); + } else { + output.push_back(str); + str = ""; + } + } + // transform any embedded quotes into the regular character + if(embeddedQuote) { + output.back() = find_and_replace(output.back(), std::string("\\") + keyChar, std::string(1, keyChar)); + embeddedQuote = false; + } + trim(str); + } + return output; +} + +/// This function detects an equal or colon followed by an escaped quote after an argument +/// then modifies the string to replace the equality with a space. This is needed +/// to allow the split up function to work properly and is intended to be used with the find_and_modify function +/// the return value is the offset+1 which is required by the find_and_modify function. +inline std::size_t escape_detect(std::string &str, std::size_t offset) { + auto next = str[offset + 1]; + if((next == '\"') || (next == '\'') || (next == '`')) { + auto astart = str.find_last_of("-/ \"\'`", offset - 1); + if(astart != std::string::npos) { + if(str[astart] == ((str[offset] == '=') ? '-' : '/')) + str[offset] = ' '; // interpret this as a space so the split_up works properly + } + } + return offset + 1; +} + +/// Add quotes if the string contains spaces +inline std::string &add_quotes_if_needed(std::string &str) { + if((str.front() != '"' && str.front() != '\'') || str.front() != str.back()) { + char quote = str.find('"') < str.find('\'') ? '\'' : '"'; + if(str.find(' ') != std::string::npos) { + str.insert(0, 1, quote); + str.append(1, quote); + } + } + return str; +} + +} // namespace detail + + + + +// Use one of these on all error classes. +// These are temporary and are undef'd at the end of this file. +#define CLI11_ERROR_DEF(parent, name) \ + protected: \ + name(std::string ename, std::string msg, int exit_code) : parent(std::move(ename), std::move(msg), exit_code) {} \ + name(std::string ename, std::string msg, ExitCodes exit_code) \ + : parent(std::move(ename), std::move(msg), exit_code) {} \ + \ + public: \ + name(std::string msg, ExitCodes exit_code) : parent(#name, std::move(msg), exit_code) {} \ + name(std::string msg, int exit_code) : parent(#name, std::move(msg), exit_code) {} + +// This is added after the one above if a class is used directly and builds its own message +#define CLI11_ERROR_SIMPLE(name) \ + explicit name(std::string msg) : name(#name, msg, ExitCodes::name) {} + +/// These codes are part of every error in CLI. They can be obtained from e using e.exit_code or as a quick shortcut, +/// int values from e.get_error_code(). +enum class ExitCodes { + Success = 0, + IncorrectConstruction = 100, + BadNameString, + OptionAlreadyAdded, + FileError, + ConversionError, + ValidationError, + RequiredError, + RequiresError, + ExcludesError, + ExtrasError, + ConfigError, + InvalidError, + HorribleError, + OptionNotFound, + ArgumentMismatch, + BaseClass = 127 +}; + +// Error definitions + +/// @defgroup error_group Errors +/// @brief Errors thrown by CLI11 +/// +/// These are the errors that can be thrown. Some of them, like CLI::Success, are not really errors. +/// @{ + +/// All errors derive from this one +class Error : public std::runtime_error { + int actual_exit_code; + std::string error_name{"Error"}; + + public: + int get_exit_code() const { return actual_exit_code; } + + std::string get_name() const { return error_name; } + + Error(std::string name, std::string msg, int exit_code = static_cast(ExitCodes::BaseClass)) + : runtime_error(msg), actual_exit_code(exit_code), error_name(std::move(name)) {} + + Error(std::string name, std::string msg, ExitCodes exit_code) : Error(name, msg, static_cast(exit_code)) {} +}; + +// Note: Using Error::Error constructors does not work on GCC 4.7 + +/// Construction errors (not in parsing) +class ConstructionError : public Error { + CLI11_ERROR_DEF(Error, ConstructionError) +}; + +/// Thrown when an option is set to conflicting values (non-vector and multi args, for example) +class IncorrectConstruction : public ConstructionError { + CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction) + CLI11_ERROR_SIMPLE(IncorrectConstruction) + static IncorrectConstruction PositionalFlag(std::string name) { + return IncorrectConstruction(name + ": Flags cannot be positional"); + } + static IncorrectConstruction Set0Opt(std::string name) { + return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead"); + } + static IncorrectConstruction SetFlag(std::string name) { + return IncorrectConstruction(name + ": Cannot set an expected number for flags"); + } + static IncorrectConstruction ChangeNotVector(std::string name) { + return IncorrectConstruction(name + ": You can only change the expected arguments for vectors"); + } + static IncorrectConstruction AfterMultiOpt(std::string name) { + return IncorrectConstruction( + name + ": You can't change expected arguments after you've changed the multi option policy!"); + } + static IncorrectConstruction MissingOption(std::string name) { + return IncorrectConstruction("Option " + name + " is not defined"); + } + static IncorrectConstruction MultiOptionPolicy(std::string name) { + return IncorrectConstruction(name + ": multi_option_policy only works for flags and exact value options"); + } +}; + +/// Thrown on construction of a bad name +class BadNameString : public ConstructionError { + CLI11_ERROR_DEF(ConstructionError, BadNameString) + CLI11_ERROR_SIMPLE(BadNameString) + static BadNameString OneCharName(std::string name) { return BadNameString("Invalid one char name: " + name); } + static BadNameString BadLongName(std::string name) { return BadNameString("Bad long name: " + name); } + static BadNameString DashesOnly(std::string name) { + return BadNameString("Must have a name, not just dashes: " + name); + } + static BadNameString MultiPositionalNames(std::string name) { + return BadNameString("Only one positional name allowed, remove: " + name); + } +}; + +/// Thrown when an option already exists +class OptionAlreadyAdded : public ConstructionError { + CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded) + explicit OptionAlreadyAdded(std::string name) + : OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {} + static OptionAlreadyAdded Requires(std::string name, std::string other) { + return OptionAlreadyAdded(name + " requires " + other, ExitCodes::OptionAlreadyAdded); + } + static OptionAlreadyAdded Excludes(std::string name, std::string other) { + return OptionAlreadyAdded(name + " excludes " + other, ExitCodes::OptionAlreadyAdded); + } +}; + +// Parsing errors + +/// Anything that can error in Parse +class ParseError : public Error { + CLI11_ERROR_DEF(Error, ParseError) +}; + +// Not really "errors" + +/// This is a successful completion on parsing, supposed to exit +class Success : public ParseError { + CLI11_ERROR_DEF(ParseError, Success) + Success() : Success("Successfully completed, should be caught and quit", ExitCodes::Success) {} +}; + +/// -h or --help on command line +class CallForHelp : public Success { + CLI11_ERROR_DEF(Success, CallForHelp) + CallForHelp() : CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} +}; + +/// Usually something like --help-all on command line +class CallForAllHelp : public Success { + CLI11_ERROR_DEF(Success, CallForAllHelp) + CallForAllHelp() + : CallForAllHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} +}; + +/// -v or --version on command line +class CallForVersion : public Success { + CLI11_ERROR_DEF(Success, CallForVersion) + CallForVersion() + : CallForVersion("This should be caught in your main function, see examples", ExitCodes::Success) {} +}; + +/// Does not output a diagnostic in CLI11_PARSE, but allows main() to return with a specific error code. +class RuntimeError : public ParseError { + CLI11_ERROR_DEF(ParseError, RuntimeError) + explicit RuntimeError(int exit_code = 1) : RuntimeError("Runtime error", exit_code) {} +}; + +/// Thrown when parsing an INI file and it is missing +class FileError : public ParseError { + CLI11_ERROR_DEF(ParseError, FileError) + CLI11_ERROR_SIMPLE(FileError) + static FileError Missing(std::string name) { return FileError(name + " was not readable (missing?)"); } +}; + +/// Thrown when conversion call back fails, such as when an int fails to coerce to a string +class ConversionError : public ParseError { + CLI11_ERROR_DEF(ParseError, ConversionError) + CLI11_ERROR_SIMPLE(ConversionError) + ConversionError(std::string member, std::string name) + : ConversionError("The value " + member + " is not an allowed value for " + name) {} + ConversionError(std::string name, std::vector results) + : ConversionError("Could not convert: " + name + " = " + detail::join(results)) {} + static ConversionError TooManyInputsFlag(std::string name) { + return ConversionError(name + ": too many inputs for a flag"); + } + static ConversionError TrueFalse(std::string name) { + return ConversionError(name + ": Should be true/false or a number"); + } +}; + +/// Thrown when validation of results fails +class ValidationError : public ParseError { + CLI11_ERROR_DEF(ParseError, ValidationError) + CLI11_ERROR_SIMPLE(ValidationError) + explicit ValidationError(std::string name, std::string msg) : ValidationError(name + ": " + msg) {} +}; + +/// Thrown when a required option is missing +class RequiredError : public ParseError { + CLI11_ERROR_DEF(ParseError, RequiredError) + explicit RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {} + static RequiredError Subcommand(std::size_t min_subcom) { + if(min_subcom == 1) { + return RequiredError("A subcommand"); + } + return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands", + ExitCodes::RequiredError); + } + static RequiredError + Option(std::size_t min_option, std::size_t max_option, std::size_t used, const std::string &option_list) { + if((min_option == 1) && (max_option == 1) && (used == 0)) + return RequiredError("Exactly 1 option from [" + option_list + "]"); + if((min_option == 1) && (max_option == 1) && (used > 1)) { + return RequiredError("Exactly 1 option from [" + option_list + "] is required and " + std::to_string(used) + + " were given", + ExitCodes::RequiredError); + } + if((min_option == 1) && (used == 0)) + return RequiredError("At least 1 option from [" + option_list + "]"); + if(used < min_option) { + return RequiredError("Requires at least " + std::to_string(min_option) + " options used and only " + + std::to_string(used) + "were given from [" + option_list + "]", + ExitCodes::RequiredError); + } + if(max_option == 1) + return RequiredError("Requires at most 1 options be given from [" + option_list + "]", + ExitCodes::RequiredError); + + return RequiredError("Requires at most " + std::to_string(max_option) + " options be used and " + + std::to_string(used) + "were given from [" + option_list + "]", + ExitCodes::RequiredError); + } +}; + +/// Thrown when the wrong number of arguments has been received +class ArgumentMismatch : public ParseError { + CLI11_ERROR_DEF(ParseError, ArgumentMismatch) + CLI11_ERROR_SIMPLE(ArgumentMismatch) + ArgumentMismatch(std::string name, int expected, std::size_t received) + : ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name + + ", got " + std::to_string(received)) + : ("Expected at least " + std::to_string(-expected) + " arguments to " + name + + ", got " + std::to_string(received)), + ExitCodes::ArgumentMismatch) {} + + static ArgumentMismatch AtLeast(std::string name, int num, std::size_t received) { + return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required but received " + + std::to_string(received)); + } + static ArgumentMismatch AtMost(std::string name, int num, std::size_t received) { + return ArgumentMismatch(name + ": At Most " + std::to_string(num) + " required but received " + + std::to_string(received)); + } + static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) { + return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing"); + } + static ArgumentMismatch FlagOverride(std::string name) { + return ArgumentMismatch(name + " was given a disallowed flag override"); + } +}; + +/// Thrown when a requires option is missing +class RequiresError : public ParseError { + CLI11_ERROR_DEF(ParseError, RequiresError) + RequiresError(std::string curname, std::string subname) + : RequiresError(curname + " requires " + subname, ExitCodes::RequiresError) {} +}; + +/// Thrown when an excludes option is present +class ExcludesError : public ParseError { + CLI11_ERROR_DEF(ParseError, ExcludesError) + ExcludesError(std::string curname, std::string subname) + : ExcludesError(curname + " excludes " + subname, ExitCodes::ExcludesError) {} +}; + +/// Thrown when too many positionals or options are found +class ExtrasError : public ParseError { + CLI11_ERROR_DEF(ParseError, ExtrasError) + explicit ExtrasError(std::vector args) + : ExtrasError((args.size() > 1 ? "The following arguments were not expected: " + : "The following argument was not expected: ") + + detail::rjoin(args, " "), + ExitCodes::ExtrasError) {} + ExtrasError(const std::string &name, std::vector args) + : ExtrasError(name, + (args.size() > 1 ? "The following arguments were not expected: " + : "The following argument was not expected: ") + + detail::rjoin(args, " "), + ExitCodes::ExtrasError) {} +}; + +/// Thrown when extra values are found in an INI file +class ConfigError : public ParseError { + CLI11_ERROR_DEF(ParseError, ConfigError) + CLI11_ERROR_SIMPLE(ConfigError) + static ConfigError Extras(std::string item) { return ConfigError("INI was not able to parse " + item); } + static ConfigError NotConfigurable(std::string item) { + return ConfigError(item + ": This option is not allowed in a configuration file"); + } +}; + +/// Thrown when validation fails before parsing +class InvalidError : public ParseError { + CLI11_ERROR_DEF(ParseError, InvalidError) + explicit InvalidError(std::string name) + : InvalidError(name + ": Too many positional arguments with unlimited expected args", ExitCodes::InvalidError) { + } +}; + +/// This is just a safety check to verify selection and parsing match - you should not ever see it +/// Strings are directly added to this error, but again, it should never be seen. +class HorribleError : public ParseError { + CLI11_ERROR_DEF(ParseError, HorribleError) + CLI11_ERROR_SIMPLE(HorribleError) +}; + +// After parsing + +/// Thrown when counting a non-existent option +class OptionNotFound : public Error { + CLI11_ERROR_DEF(Error, OptionNotFound) + explicit OptionNotFound(std::string name) : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {} +}; + +#undef CLI11_ERROR_DEF +#undef CLI11_ERROR_SIMPLE + +/// @} + + + + +// Type tools + +// Utilities for type enabling +namespace detail { +// Based generally on https://rmf.io/cxx11/almost-static-if +/// Simple empty scoped class +enum class enabler {}; + +/// An instance to use in EnableIf +constexpr enabler dummy = {}; +} // namespace detail + +/// A copy of enable_if_t from C++14, compatible with C++11. +/// +/// We could check to see if C++14 is being used, but it does not hurt to redefine this +/// (even Google does this: https://github.com/google/skia/blob/main/include/private/SkTLogic.h) +/// It is not in the std namespace anyway, so no harm done. +template using enable_if_t = typename std::enable_if::type; + +/// A copy of std::void_t from C++17 (helper for C++11 and C++14) +template struct make_void { using type = void; }; + +/// A copy of std::void_t from C++17 - same reasoning as enable_if_t, it does not hurt to redefine +template using void_t = typename make_void::type; + +/// A copy of std::conditional_t from C++14 - same reasoning as enable_if_t, it does not hurt to redefine +template using conditional_t = typename std::conditional::type; + +/// Check to see if something is bool (fail check by default) +template struct is_bool : std::false_type {}; + +/// Check to see if something is bool (true if actually a bool) +template <> struct is_bool : std::true_type {}; + +/// Check to see if something is a shared pointer +template struct is_shared_ptr : std::false_type {}; + +/// Check to see if something is a shared pointer (True if really a shared pointer) +template struct is_shared_ptr> : std::true_type {}; + +/// Check to see if something is a shared pointer (True if really a shared pointer) +template struct is_shared_ptr> : std::true_type {}; + +/// Check to see if something is copyable pointer +template struct is_copyable_ptr { + static bool const value = is_shared_ptr::value || std::is_pointer::value; +}; + +/// This can be specialized to override the type deduction for IsMember. +template struct IsMemberType { using type = T; }; + +/// The main custom type needed here is const char * should be a string. +template <> struct IsMemberType { using type = std::string; }; + +namespace detail { + +// These are utilities for IsMember and other transforming objects + +/// Handy helper to access the element_type generically. This is not part of is_copyable_ptr because it requires that +/// pointer_traits be valid. + +/// not a pointer +template struct element_type { using type = T; }; + +template struct element_type::value>::type> { + using type = typename std::pointer_traits::element_type; +}; + +/// Combination of the element type and value type - remove pointer (including smart pointers) and get the value_type of +/// the container +template struct element_value_type { using type = typename element_type::type::value_type; }; + +/// Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost nothing. +template struct pair_adaptor : std::false_type { + using value_type = typename T::value_type; + using first_type = typename std::remove_const::type; + using second_type = typename std::remove_const::type; + + /// Get the first value (really just the underlying value) + template static auto first(Q &&pair_value) -> decltype(std::forward(pair_value)) { + return std::forward(pair_value); + } + /// Get the second value (really just the underlying value) + template static auto second(Q &&pair_value) -> decltype(std::forward(pair_value)) { + return std::forward(pair_value); + } +}; + +/// Adaptor for map-like structure (true version, must have key_type and mapped_type). +/// This wraps a mapped container in a few utilities access it in a general way. +template +struct pair_adaptor< + T, + conditional_t, void>> + : std::true_type { + using value_type = typename T::value_type; + using first_type = typename std::remove_const::type; + using second_type = typename std::remove_const::type; + + /// Get the first value (really just the underlying value) + template static auto first(Q &&pair_value) -> decltype(std::get<0>(std::forward(pair_value))) { + return std::get<0>(std::forward(pair_value)); + } + /// Get the second value (really just the underlying value) + template static auto second(Q &&pair_value) -> decltype(std::get<1>(std::forward(pair_value))) { + return std::get<1>(std::forward(pair_value)); + } +}; + +// Warning is suppressed due to "bug" in gcc<5.0 and gcc 7.0 with c++17 enabled that generates a Wnarrowing warning +// in the unevaluated context even if the function that was using this wasn't used. The standard says narrowing in +// brace initialization shouldn't be allowed but for backwards compatibility gcc allows it in some contexts. It is a +// little fuzzy what happens in template constructs and I think that was something GCC took a little while to work out. +// But regardless some versions of gcc generate a warning when they shouldn't from the following code so that should be +// suppressed +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnarrowing" +#endif +// check for constructibility from a specific type and copy assignable used in the parse detection +template class is_direct_constructible { + template + static auto test(int, std::true_type) -> decltype( +// NVCC warns about narrowing conversions here +#ifdef __CUDACC__ +#pragma diag_suppress 2361 +#endif + TT { std::declval() } +#ifdef __CUDACC__ +#pragma diag_default 2361 +#endif + , + std::is_move_assignable()); + + template static auto test(int, std::false_type) -> std::false_type; + + template static auto test(...) -> std::false_type; + + public: + static constexpr bool value = decltype(test(0, typename std::is_constructible::type()))::value; +}; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +// Check for output streamability +// Based on https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream + +template class is_ostreamable { + template + static auto test(int) -> decltype(std::declval() << std::declval(), std::true_type()); + + template static auto test(...) -> std::false_type; + + public: + static constexpr bool value = decltype(test(0))::value; +}; + +/// Check for input streamability +template class is_istreamable { + template + static auto test(int) -> decltype(std::declval() >> std::declval(), std::true_type()); + + template static auto test(...) -> std::false_type; + + public: + static constexpr bool value = decltype(test(0))::value; +}; + +/// Check for complex +template class is_complex { + template + static auto test(int) -> decltype(std::declval().real(), std::declval().imag(), std::true_type()); + + template static auto test(...) -> std::false_type; + + public: + static constexpr bool value = decltype(test(0))::value; +}; + +/// Templated operation to get a value from a stream +template ::value, detail::enabler> = detail::dummy> +bool from_stream(const std::string &istring, T &obj) { + std::istringstream is; + is.str(istring); + is >> obj; + return !is.fail() && !is.rdbuf()->in_avail(); +} + +template ::value, detail::enabler> = detail::dummy> +bool from_stream(const std::string & /*istring*/, T & /*obj*/) { + return false; +} + +// check to see if an object is a mutable container (fail by default) +template struct is_mutable_container : std::false_type {}; + +/// type trait to test if a type is a mutable container meaning it has a value_type, it has an iterator, a clear, and +/// end methods and an insert function. And for our purposes we exclude std::string and types that can be constructed +/// from a std::string +template +struct is_mutable_container< + T, + conditional_t().end()), + decltype(std::declval().clear()), + decltype(std::declval().insert(std::declval().end())>(), + std::declval()))>, + void>> + : public conditional_t::value, std::false_type, std::true_type> {}; + +// check to see if an object is a mutable container (fail by default) +template struct is_readable_container : std::false_type {}; + +/// type trait to test if a type is a container meaning it has a value_type, it has an iterator, a clear, and an end +/// methods and an insert function. And for our purposes we exclude std::string and types that can be constructed from +/// a std::string +template +struct is_readable_container< + T, + conditional_t().end()), decltype(std::declval().begin())>, void>> + : public std::true_type {}; + +// check to see if an object is a wrapper (fail by default) +template struct is_wrapper : std::false_type {}; + +// check if an object is a wrapper (it has a value_type defined) +template +struct is_wrapper, void>> : public std::true_type {}; + +// Check for tuple like types, as in classes with a tuple_size type trait +template class is_tuple_like { + template + // static auto test(int) + // -> decltype(std::conditional<(std::tuple_size::value > 0), std::true_type, std::false_type>::type()); + static auto test(int) -> decltype(std::tuple_size::type>::value, std::true_type{}); + template static auto test(...) -> std::false_type; + + public: + static constexpr bool value = decltype(test(0))::value; +}; + +/// Convert an object to a string (directly forward if this can become a string) +template ::value, detail::enabler> = detail::dummy> +auto to_string(T &&value) -> decltype(std::forward(value)) { + return std::forward(value); +} + +/// Construct a string from the object +template ::value && !std::is_convertible::value, + detail::enabler> = detail::dummy> +std::string to_string(const T &value) { + return std::string(value); +} + +/// Convert an object to a string (streaming must be supported for that type) +template ::value && !std::is_constructible::value && + is_ostreamable::value, + detail::enabler> = detail::dummy> +std::string to_string(T &&value) { + std::stringstream stream; + stream << value; + return stream.str(); +} + +/// If conversion is not supported, return an empty string (streaming is not supported for that type) +template ::value && !is_ostreamable::value && + !is_readable_container::type>::value, + detail::enabler> = detail::dummy> +std::string to_string(T &&) { + return std::string{}; +} + +/// convert a readable container to a string +template ::value && !is_ostreamable::value && + is_readable_container::value, + detail::enabler> = detail::dummy> +std::string to_string(T &&variable) { + std::vector defaults; + auto cval = variable.begin(); + auto end = variable.end(); + while(cval != end) { + defaults.emplace_back(CLI::detail::to_string(*cval)); + ++cval; + } + return std::string("[" + detail::join(defaults) + "]"); +} + +/// special template overload +template ::value, detail::enabler> = detail::dummy> +auto checked_to_string(T &&value) -> decltype(to_string(std::forward(value))) { + return to_string(std::forward(value)); +} + +/// special template overload +template ::value, detail::enabler> = detail::dummy> +std::string checked_to_string(T &&) { + return std::string{}; +} +/// get a string as a convertible value for arithmetic types +template ::value, detail::enabler> = detail::dummy> +std::string value_string(const T &value) { + return std::to_string(value); +} +/// get a string as a convertible value for enumerations +template ::value, detail::enabler> = detail::dummy> +std::string value_string(const T &value) { + return std::to_string(static_cast::type>(value)); +} +/// for other types just use the regular to_string function +template ::value && !std::is_arithmetic::value, detail::enabler> = detail::dummy> +auto value_string(const T &value) -> decltype(to_string(value)) { + return to_string(value); +} + +/// template to get the underlying value type if it exists or use a default +template struct wrapped_type { using type = def; }; + +/// Type size for regular object types that do not look like a tuple +template struct wrapped_type::value>::type> { + using type = typename T::value_type; +}; + +/// This will only trigger for actual void type +template struct type_count_base { static const int value{0}; }; + +/// Type size for regular object types that do not look like a tuple +template +struct type_count_base::value && !is_mutable_container::value && + !std::is_void::value>::type> { + static constexpr int value{1}; +}; + +/// the base tuple size +template +struct type_count_base::value && !is_mutable_container::value>::type> { + static constexpr int value{std::tuple_size::value}; +}; + +/// Type count base for containers is the type_count_base of the individual element +template struct type_count_base::value>::type> { + static constexpr int value{type_count_base::value}; +}; + +/// Set of overloads to get the type size of an object + +/// forward declare the subtype_count structure +template struct subtype_count; + +/// forward declare the subtype_count_min structure +template struct subtype_count_min; + +/// This will only trigger for actual void type +template struct type_count { static const int value{0}; }; + +/// Type size for regular object types that do not look like a tuple +template +struct type_count::value && !is_tuple_like::value && !is_complex::value && + !std::is_void::value>::type> { + static constexpr int value{1}; +}; + +/// Type size for complex since it sometimes looks like a wrapper +template struct type_count::value>::type> { + static constexpr int value{2}; +}; + +/// Type size of types that are wrappers,except complex and tuples(which can also be wrappers sometimes) +template struct type_count::value>::type> { + static constexpr int value{subtype_count::value}; +}; + +/// Type size of types that are wrappers,except containers complex and tuples(which can also be wrappers sometimes) +template +struct type_count::value && !is_complex::value && !is_tuple_like::value && + !is_mutable_container::value>::type> { + static constexpr int value{type_count::value}; +}; + +/// 0 if the index > tuple size +template +constexpr typename std::enable_if::value, int>::type tuple_type_size() { + return 0; +} + +/// Recursively generate the tuple type name +template + constexpr typename std::enable_if < I::value, int>::type tuple_type_size() { + return subtype_count::type>::value + tuple_type_size(); +} + +/// Get the type size of the sum of type sizes for all the individual tuple types +template struct type_count::value>::type> { + static constexpr int value{tuple_type_size()}; +}; + +/// definition of subtype count +template struct subtype_count { + static constexpr int value{is_mutable_container::value ? expected_max_vector_size : type_count::value}; +}; + +/// This will only trigger for actual void type +template struct type_count_min { static const int value{0}; }; + +/// Type size for regular object types that do not look like a tuple +template +struct type_count_min< + T, + typename std::enable_if::value && !is_tuple_like::value && !is_wrapper::value && + !is_complex::value && !std::is_void::value>::type> { + static constexpr int value{type_count::value}; +}; + +/// Type size for complex since it sometimes looks like a wrapper +template struct type_count_min::value>::type> { + static constexpr int value{1}; +}; + +/// Type size min of types that are wrappers,except complex and tuples(which can also be wrappers sometimes) +template +struct type_count_min< + T, + typename std::enable_if::value && !is_complex::value && !is_tuple_like::value>::type> { + static constexpr int value{subtype_count_min::value}; +}; + +/// 0 if the index > tuple size +template +constexpr typename std::enable_if::value, int>::type tuple_type_size_min() { + return 0; +} + +/// Recursively generate the tuple type name +template + constexpr typename std::enable_if < I::value, int>::type tuple_type_size_min() { + return subtype_count_min::type>::value + tuple_type_size_min(); +} + +/// Get the type size of the sum of type sizes for all the individual tuple types +template struct type_count_min::value>::type> { + static constexpr int value{tuple_type_size_min()}; +}; + +/// definition of subtype count +template struct subtype_count_min { + static constexpr int value{is_mutable_container::value + ? ((type_count::value < expected_max_vector_size) ? type_count::value : 0) + : type_count_min::value}; +}; + +/// This will only trigger for actual void type +template struct expected_count { static const int value{0}; }; + +/// For most types the number of expected items is 1 +template +struct expected_count::value && !is_wrapper::value && + !std::is_void::value>::type> { + static constexpr int value{1}; +}; +/// number of expected items in a vector +template struct expected_count::value>::type> { + static constexpr int value{expected_max_vector_size}; +}; + +/// number of expected items in a vector +template +struct expected_count::value && is_wrapper::value>::type> { + static constexpr int value{expected_count::value}; +}; + +// Enumeration of the different supported categorizations of objects +enum class object_category : int { + char_value = 1, + integral_value = 2, + unsigned_integral = 4, + enumeration = 6, + boolean_value = 8, + floating_point = 10, + number_constructible = 12, + double_constructible = 14, + integer_constructible = 16, + // string like types + string_assignable = 23, + string_constructible = 24, + other = 45, + // special wrapper or container types + wrapper_value = 50, + complex_number = 60, + tuple_value = 70, + container_value = 80, + +}; + +/// Set of overloads to classify an object according to type + +/// some type that is not otherwise recognized +template struct classify_object { + static constexpr object_category value{object_category::other}; +}; + +/// Signed integers +template +struct classify_object< + T, + typename std::enable_if::value && !std::is_same::value && std::is_signed::value && + !is_bool::value && !std::is_enum::value>::type> { + static constexpr object_category value{object_category::integral_value}; +}; + +/// Unsigned integers +template +struct classify_object::value && std::is_unsigned::value && + !std::is_same::value && !is_bool::value>::type> { + static constexpr object_category value{object_category::unsigned_integral}; +}; + +/// single character values +template +struct classify_object::value && !std::is_enum::value>::type> { + static constexpr object_category value{object_category::char_value}; +}; + +/// Boolean values +template struct classify_object::value>::type> { + static constexpr object_category value{object_category::boolean_value}; +}; + +/// Floats +template struct classify_object::value>::type> { + static constexpr object_category value{object_category::floating_point}; +}; + +/// String and similar direct assignment +template +struct classify_object::value && !std::is_integral::value && + std::is_assignable::value>::type> { + static constexpr object_category value{object_category::string_assignable}; +}; + +/// String and similar constructible and copy assignment +template +struct classify_object< + T, + typename std::enable_if::value && !std::is_integral::value && + !std::is_assignable::value && (type_count::value == 1) && + std::is_constructible::value>::type> { + static constexpr object_category value{object_category::string_constructible}; +}; + +/// Enumerations +template struct classify_object::value>::type> { + static constexpr object_category value{object_category::enumeration}; +}; + +template struct classify_object::value>::type> { + static constexpr object_category value{object_category::complex_number}; +}; + +/// Handy helper to contain a bunch of checks that rule out many common types (integers, string like, floating point, +/// vectors, and enumerations +template struct uncommon_type { + using type = typename std::conditional::value && !std::is_integral::value && + !std::is_assignable::value && + !std::is_constructible::value && !is_complex::value && + !is_mutable_container::value && !std::is_enum::value, + std::true_type, + std::false_type>::type; + static constexpr bool value = type::value; +}; + +/// wrapper type +template +struct classify_object::value && is_wrapper::value && + !is_tuple_like::value && uncommon_type::value)>::type> { + static constexpr object_category value{object_category::wrapper_value}; +}; + +/// Assignable from double or int +template +struct classify_object::value && type_count::value == 1 && + !is_wrapper::value && is_direct_constructible::value && + is_direct_constructible::value>::type> { + static constexpr object_category value{object_category::number_constructible}; +}; + +/// Assignable from int +template +struct classify_object::value && type_count::value == 1 && + !is_wrapper::value && !is_direct_constructible::value && + is_direct_constructible::value>::type> { + static constexpr object_category value{object_category::integer_constructible}; +}; + +/// Assignable from double +template +struct classify_object::value && type_count::value == 1 && + !is_wrapper::value && is_direct_constructible::value && + !is_direct_constructible::value>::type> { + static constexpr object_category value{object_category::double_constructible}; +}; + +/// Tuple type +template +struct classify_object< + T, + typename std::enable_if::value && + ((type_count::value >= 2 && !is_wrapper::value) || + (uncommon_type::value && !is_direct_constructible::value && + !is_direct_constructible::value))>::type> { + static constexpr object_category value{object_category::tuple_value}; + // the condition on this class requires it be like a tuple, but on some compilers (like Xcode) tuples can be + // constructed from just the first element so tuples of can be constructed from a string, which + // could lead to issues so there are two variants of the condition, the first isolates things with a type size >=2 + // mainly to get tuples on Xcode with the exception of wrappers, the second is the main one and just separating out + // those cases that are caught by other object classifications +}; + +/// container type +template struct classify_object::value>::type> { + static constexpr object_category value{object_category::container_value}; +}; + +// Type name print + +/// Was going to be based on +/// http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template +/// But this is cleaner and works better in this case + +template ::value == object_category::char_value, detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "CHAR"; +} + +template ::value == object_category::integral_value || + classify_object::value == object_category::integer_constructible, + detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "INT"; +} + +template ::value == object_category::unsigned_integral, detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "UINT"; +} + +template ::value == object_category::floating_point || + classify_object::value == object_category::number_constructible || + classify_object::value == object_category::double_constructible, + detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "FLOAT"; +} + +/// Print name for enumeration types +template ::value == object_category::enumeration, detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "ENUM"; +} + +/// Print name for enumeration types +template ::value == object_category::boolean_value, detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "BOOLEAN"; +} + +/// Print name for enumeration types +template ::value == object_category::complex_number, detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "COMPLEX"; +} + +/// Print for all other types +template ::value >= object_category::string_assignable && + classify_object::value <= object_category::other, + detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "TEXT"; +} +/// typename for tuple value +template ::value == object_category::tuple_value && type_count_base::value >= 2, + detail::enabler> = detail::dummy> +std::string type_name(); // forward declaration + +/// Generate type name for a wrapper or container value +template ::value == object_category::container_value || + classify_object::value == object_category::wrapper_value, + detail::enabler> = detail::dummy> +std::string type_name(); // forward declaration + +/// Print name for single element tuple types +template ::value == object_category::tuple_value && type_count_base::value == 1, + detail::enabler> = detail::dummy> +inline std::string type_name() { + return type_name::type>::type>(); +} + +/// Empty string if the index > tuple size +template +inline typename std::enable_if::value, std::string>::type tuple_name() { + return std::string{}; +} + +/// Recursively generate the tuple type name +template +inline typename std::enable_if<(I < type_count_base::value), std::string>::type tuple_name() { + std::string str = std::string(type_name::type>::type>()) + + ',' + tuple_name(); + if(str.back() == ',') + str.pop_back(); + return str; +} + +/// Print type name for tuples with 2 or more elements +template ::value == object_category::tuple_value && type_count_base::value >= 2, + detail::enabler>> +inline std::string type_name() { + auto tname = std::string(1, '[') + tuple_name(); + tname.push_back(']'); + return tname; +} + +/// get the type name for a type that has a value_type member +template ::value == object_category::container_value || + classify_object::value == object_category::wrapper_value, + detail::enabler>> +inline std::string type_name() { + return type_name(); +} + +// Lexical cast + +/// Convert to an unsigned integral +template ::value, detail::enabler> = detail::dummy> +bool integral_conversion(const std::string &input, T &output) noexcept { + if(input.empty()) { + return false; + } + char *val = nullptr; + std::uint64_t output_ll = std::strtoull(input.c_str(), &val, 0); + output = static_cast(output_ll); + return val == (input.c_str() + input.size()) && static_cast(output) == output_ll; +} + +/// Convert to a signed integral +template ::value, detail::enabler> = detail::dummy> +bool integral_conversion(const std::string &input, T &output) noexcept { + if(input.empty()) { + return false; + } + char *val = nullptr; + std::int64_t output_ll = std::strtoll(input.c_str(), &val, 0); + output = static_cast(output_ll); + return val == (input.c_str() + input.size()) && static_cast(output) == output_ll; +} + +/// Convert a flag into an integer value typically binary flags +inline std::int64_t to_flag_value(std::string val) { + static const std::string trueString("true"); + static const std::string falseString("false"); + if(val == trueString) { + return 1; + } + if(val == falseString) { + return -1; + } + val = detail::to_lower(val); + std::int64_t ret; + if(val.size() == 1) { + if(val[0] >= '1' && val[0] <= '9') { + return (static_cast(val[0]) - '0'); + } + switch(val[0]) { + case '0': + case 'f': + case 'n': + case '-': + ret = -1; + break; + case 't': + case 'y': + case '+': + ret = 1; + break; + default: + throw std::invalid_argument("unrecognized character"); + } + return ret; + } + if(val == trueString || val == "on" || val == "yes" || val == "enable") { + ret = 1; + } else if(val == falseString || val == "off" || val == "no" || val == "disable") { + ret = -1; + } else { + ret = std::stoll(val); + } + return ret; +} + +/// Integer conversion +template ::value == object_category::integral_value || + classify_object::value == object_category::unsigned_integral, + detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + return integral_conversion(input, output); +} + +/// char values +template ::value == object_category::char_value, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + if(input.size() == 1) { + output = static_cast(input[0]); + return true; + } + return integral_conversion(input, output); +} + +/// Boolean values +template ::value == object_category::boolean_value, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + try { + auto out = to_flag_value(input); + output = (out > 0); + return true; + } catch(const std::invalid_argument &) { + return false; + } catch(const std::out_of_range &) { + // if the number is out of the range of a 64 bit value then it is still a number and for this purpose is still + // valid all we care about the sign + output = (input[0] != '-'); + return true; + } +} + +/// Floats +template ::value == object_category::floating_point, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + if(input.empty()) { + return false; + } + char *val = nullptr; + auto output_ld = std::strtold(input.c_str(), &val); + output = static_cast(output_ld); + return val == (input.c_str() + input.size()); +} + +/// complex +template ::value == object_category::complex_number, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + using XC = typename wrapped_type::type; + XC x{0.0}, y{0.0}; + auto str1 = input; + bool worked = false; + auto nloc = str1.find_last_of("+-"); + if(nloc != std::string::npos && nloc > 0) { + worked = detail::lexical_cast(str1.substr(0, nloc), x); + str1 = str1.substr(nloc); + if(str1.back() == 'i' || str1.back() == 'j') + str1.pop_back(); + worked = worked && detail::lexical_cast(str1, y); + } else { + if(str1.back() == 'i' || str1.back() == 'j') { + str1.pop_back(); + worked = detail::lexical_cast(str1, y); + x = XC{0}; + } else { + worked = detail::lexical_cast(str1, x); + y = XC{0}; + } + } + if(worked) { + output = T{x, y}; + return worked; + } + return from_stream(input, output); +} + +/// String and similar direct assignment +template ::value == object_category::string_assignable, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + output = input; + return true; +} + +/// String and similar constructible and copy assignment +template < + typename T, + enable_if_t::value == object_category::string_constructible, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + output = T(input); + return true; +} + +/// Enumerations +template ::value == object_category::enumeration, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + typename std::underlying_type::type val; + if(!integral_conversion(input, val)) { + return false; + } + output = static_cast(val); + return true; +} + +/// wrapper types +template ::value == object_category::wrapper_value && + std::is_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + typename T::value_type val; + if(lexical_cast(input, val)) { + output = val; + return true; + } + return from_stream(input, output); +} + +template ::value == object_category::wrapper_value && + !std::is_assignable::value && std::is_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + typename T::value_type val; + if(lexical_cast(input, val)) { + output = T{val}; + return true; + } + return from_stream(input, output); +} + +/// Assignable from double or int +template < + typename T, + enable_if_t::value == object_category::number_constructible, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + int val; + if(integral_conversion(input, val)) { + output = T(val); + return true; + } else { + double dval; + if(lexical_cast(input, dval)) { + output = T{dval}; + return true; + } + } + return from_stream(input, output); +} + +/// Assignable from int +template < + typename T, + enable_if_t::value == object_category::integer_constructible, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + int val; + if(integral_conversion(input, val)) { + output = T(val); + return true; + } + return from_stream(input, output); +} + +/// Assignable from double +template < + typename T, + enable_if_t::value == object_category::double_constructible, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + double val; + if(lexical_cast(input, val)) { + output = T{val}; + return true; + } + return from_stream(input, output); +} + +/// Non-string convertible from an int +template ::value == object_category::other && std::is_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + int val; + if(integral_conversion(input, val)) { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4800) +#endif + // with Atomic this could produce a warning due to the conversion but if atomic gets here it is an old style + // so will most likely still work + output = val; +#ifdef _MSC_VER +#pragma warning(pop) +#endif + return true; + } + // LCOV_EXCL_START + // This version of cast is only used for odd cases in an older compilers the fail over + // from_stream is tested elsewhere an not relevant for coverage here + return from_stream(input, output); + // LCOV_EXCL_STOP +} + +/// Non-string parsable by a stream +template ::value == object_category::other && !std::is_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + static_assert(is_istreamable::value, + "option object type must have a lexical cast overload or streaming input operator(>>) defined, if it " + "is convertible from another type use the add_option(...) with XC being the known type"); + return from_stream(input, output); +} + +/// Assign a value through lexical cast operations +/// Strings can be empty so we need to do a little different +template ::value && + (classify_object::value == object_category::string_assignable || + classify_object::value == object_category::string_constructible), + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, AssignTo &output) { + return lexical_cast(input, output); +} + +/// Assign a value through lexical cast operations +template ::value && std::is_assignable::value && + classify_object::value != object_category::string_assignable && + classify_object::value != object_category::string_constructible, + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, AssignTo &output) { + if(input.empty()) { + output = AssignTo{}; + return true; + } + + return lexical_cast(input, output); +} + +/// Assign a value through lexical cast operations +template ::value && !std::is_assignable::value && + classify_object::value == object_category::wrapper_value, + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, AssignTo &output) { + if(input.empty()) { + typename AssignTo::value_type emptyVal{}; + output = emptyVal; + return true; + } + return lexical_cast(input, output); +} + +/// Assign a value through lexical cast operations for int compatible values +/// mainly for atomic operations on some compilers +template ::value && !std::is_assignable::value && + classify_object::value != object_category::wrapper_value && + std::is_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, AssignTo &output) { + if(input.empty()) { + output = 0; + return true; + } + int val; + if(lexical_cast(input, val)) { + output = val; + return true; + } + return false; +} + +/// Assign a value converted from a string in lexical cast to the output value directly +template ::value && std::is_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, AssignTo &output) { + ConvertTo val{}; + bool parse_result = (!input.empty()) ? lexical_cast(input, val) : true; + if(parse_result) { + output = val; + } + return parse_result; +} + +/// Assign a value from a lexical cast through constructing a value and move assigning it +template < + typename AssignTo, + typename ConvertTo, + enable_if_t::value && !std::is_assignable::value && + std::is_move_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, AssignTo &output) { + ConvertTo val{}; + bool parse_result = input.empty() ? true : lexical_cast(input, val); + if(parse_result) { + output = AssignTo(val); // use () form of constructor to allow some implicit conversions + } + return parse_result; +} + +/// primary lexical conversion operation, 1 string to 1 type of some kind +template ::value <= object_category::other && + classify_object::value <= object_category::wrapper_value, + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + return lexical_assign(strings[0], output); +} + +/// Lexical conversion if there is only one element but the conversion type is for two, then call a two element +/// constructor +template ::value <= 2) && expected_count::value == 1 && + is_tuple_like::value && type_count_base::value == 2, + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + // the remove const is to handle pair types coming from a container + typename std::remove_const::type>::type v1; + typename std::tuple_element<1, ConvertTo>::type v2; + bool retval = lexical_assign(strings[0], v1); + if(strings.size() > 1) { + retval = retval && lexical_assign(strings[1], v2); + } + if(retval) { + output = AssignTo{v1, v2}; + } + return retval; +} + +/// Lexical conversion of a container types of single elements +template ::value && is_mutable_container::value && + type_count::value == 1, + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + output.erase(output.begin(), output.end()); + for(const auto &elem : strings) { + typename AssignTo::value_type out; + bool retval = lexical_assign(elem, out); + if(!retval) { + return false; + } + output.insert(output.end(), std::move(out)); + } + return (!output.empty()); +} + +/// Lexical conversion for complex types +template ::value, detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + + if(strings.size() >= 2 && !strings[1].empty()) { + using XC2 = typename wrapped_type::type; + XC2 x{0.0}, y{0.0}; + auto str1 = strings[1]; + if(str1.back() == 'i' || str1.back() == 'j') { + str1.pop_back(); + } + auto worked = detail::lexical_cast(strings[0], x) && detail::lexical_cast(str1, y); + if(worked) { + output = ConvertTo{x, y}; + } + return worked; + } else { + return lexical_assign(strings[0], output); + } +} + +/// Conversion to a vector type using a particular single type as the conversion type +template ::value && (expected_count::value == 1) && + (type_count::value == 1), + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + bool retval = true; + output.clear(); + output.reserve(strings.size()); + for(const auto &elem : strings) { + + output.emplace_back(); + retval = retval && lexical_assign(elem, output.back()); + } + return (!output.empty()) && retval; +} + +// forward declaration + +/// Lexical conversion of a container types with conversion type of two elements +template ::value && is_mutable_container::value && + type_count_base::value == 2, + detail::enabler> = detail::dummy> +bool lexical_conversion(std::vector strings, AssignTo &output); + +/// Lexical conversion of a vector types with type_size >2 forward declaration +template ::value && is_mutable_container::value && + type_count_base::value != 2 && + ((type_count::value > 2) || + (type_count::value > type_count_base::value)), + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output); + +/// Conversion for tuples +template ::value && is_tuple_like::value && + (type_count_base::value != type_count::value || + type_count::value > 2), + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output); // forward declaration + +/// Conversion for operations where the assigned type is some class but the conversion is a mutable container or large +/// tuple +template ::value && !is_mutable_container::value && + classify_object::value != object_category::wrapper_value && + (is_mutable_container::value || type_count::value > 2), + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + + if(strings.size() > 1 || (!strings.empty() && !(strings.front().empty()))) { + ConvertTo val; + auto retval = lexical_conversion(strings, val); + output = AssignTo{val}; + return retval; + } + output = AssignTo{}; + return true; +} + +/// function template for converting tuples if the static Index is greater than the tuple size +template +inline typename std::enable_if<(I >= type_count_base::value), bool>::type +tuple_conversion(const std::vector &, AssignTo &) { + return true; +} + +/// Conversion of a tuple element where the type size ==1 and not a mutable container +template +inline typename std::enable_if::value && type_count::value == 1, bool>::type +tuple_type_conversion(std::vector &strings, AssignTo &output) { + auto retval = lexical_assign(strings[0], output); + strings.erase(strings.begin()); + return retval; +} + +/// Conversion of a tuple element where the type size !=1 but the size is fixed and not a mutable container +template +inline typename std::enable_if::value && (type_count::value > 1) && + type_count::value == type_count_min::value, + bool>::type +tuple_type_conversion(std::vector &strings, AssignTo &output) { + auto retval = lexical_conversion(strings, output); + strings.erase(strings.begin(), strings.begin() + type_count::value); + return retval; +} + +/// Conversion of a tuple element where the type is a mutable container or a type with different min and max type sizes +template +inline typename std::enable_if::value || + type_count::value != type_count_min::value, + bool>::type +tuple_type_conversion(std::vector &strings, AssignTo &output) { + + std::size_t index{subtype_count_min::value}; + const std::size_t mx_count{subtype_count::value}; + const std::size_t mx{(std::max)(mx_count, strings.size())}; + + while(index < mx) { + if(is_separator(strings[index])) { + break; + } + ++index; + } + bool retval = lexical_conversion( + std::vector(strings.begin(), strings.begin() + static_cast(index)), output); + strings.erase(strings.begin(), strings.begin() + static_cast(index) + 1); + return retval; +} + +/// Tuple conversion operation +template +inline typename std::enable_if<(I < type_count_base::value), bool>::type +tuple_conversion(std::vector strings, AssignTo &output) { + bool retval = true; + using ConvertToElement = typename std:: + conditional::value, typename std::tuple_element::type, ConvertTo>::type; + if(!strings.empty()) { + retval = retval && tuple_type_conversion::type, ConvertToElement>( + strings, std::get(output)); + } + retval = retval && tuple_conversion(std::move(strings), output); + return retval; +} + +/// Lexical conversion of a container types with tuple elements of size 2 +template ::value && is_mutable_container::value && + type_count_base::value == 2, + detail::enabler>> +bool lexical_conversion(std::vector strings, AssignTo &output) { + output.clear(); + while(!strings.empty()) { + + typename std::remove_const::type>::type v1; + typename std::tuple_element<1, typename ConvertTo::value_type>::type v2; + bool retval = tuple_type_conversion(strings, v1); + if(!strings.empty()) { + retval = retval && tuple_type_conversion(strings, v2); + } + if(retval) { + output.insert(output.end(), typename AssignTo::value_type{v1, v2}); + } else { + return false; + } + } + return (!output.empty()); +} + +/// lexical conversion of tuples with type count>2 or tuples of types of some element with a type size>=2 +template ::value && is_tuple_like::value && + (type_count_base::value != type_count::value || + type_count::value > 2), + detail::enabler>> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + static_assert( + !is_tuple_like::value || type_count_base::value == type_count_base::value, + "if the conversion type is defined as a tuple it must be the same size as the type you are converting to"); + return tuple_conversion(strings, output); +} + +/// Lexical conversion of a vector types for everything but tuples of two elements and types of size 1 +template ::value && is_mutable_container::value && + type_count_base::value != 2 && + ((type_count::value > 2) || + (type_count::value > type_count_base::value)), + detail::enabler>> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + bool retval = true; + output.clear(); + std::vector temp; + std::size_t ii{0}; + std::size_t icount{0}; + std::size_t xcm{type_count::value}; + auto ii_max = strings.size(); + while(ii < ii_max) { + temp.push_back(strings[ii]); + ++ii; + ++icount; + if(icount == xcm || is_separator(temp.back()) || ii == ii_max) { + if(static_cast(xcm) > type_count_min::value && is_separator(temp.back())) { + temp.pop_back(); + } + typename AssignTo::value_type temp_out; + retval = retval && + lexical_conversion(temp, temp_out); + temp.clear(); + if(!retval) { + return false; + } + output.insert(output.end(), std::move(temp_out)); + icount = 0; + } + } + return retval; +} + +/// conversion for wrapper types +template ::value == object_category::wrapper_value && + std::is_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + if(strings.empty() || strings.front().empty()) { + output = ConvertTo{}; + return true; + } + typename ConvertTo::value_type val; + if(lexical_conversion(strings, val)) { + output = ConvertTo{val}; + return true; + } + return false; +} + +/// conversion for wrapper types +template ::value == object_category::wrapper_value && + !std::is_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, AssignTo &output) { + using ConvertType = typename ConvertTo::value_type; + if(strings.empty() || strings.front().empty()) { + output = ConvertType{}; + return true; + } + ConvertType val; + if(lexical_conversion(strings, val)) { + output = val; + return true; + } + return false; +} + +/// Sum a vector of flag representations +/// The flag vector produces a series of strings in a vector, simple true is represented by a "1", simple false is +/// by +/// "-1" an if numbers are passed by some fashion they are captured as well so the function just checks for the most +/// common true and false strings then uses stoll to convert the rest for summing +template ::value, detail::enabler> = detail::dummy> +void sum_flag_vector(const std::vector &flags, T &output) { + std::int64_t count{0}; + for(auto &flag : flags) { + count += detail::to_flag_value(flag); + } + output = (count > 0) ? static_cast(count) : T{0}; +} + +/// Sum a vector of flag representations +/// The flag vector produces a series of strings in a vector, simple true is represented by a "1", simple false is +/// by +/// "-1" an if numbers are passed by some fashion they are captured as well so the function just checks for the most +/// common true and false strings then uses stoll to convert the rest for summing +template ::value, detail::enabler> = detail::dummy> +void sum_flag_vector(const std::vector &flags, T &output) { + std::int64_t count{0}; + for(auto &flag : flags) { + count += detail::to_flag_value(flag); + } + output = static_cast(count); +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4800) +#endif +// with Atomic this could produce a warning due to the conversion but if atomic gets here it is an old style so will +// most likely still work + +/// Sum a vector of flag representations +/// The flag vector produces a series of strings in a vector, simple true is represented by a "1", simple false is +/// by +/// "-1" an if numbers are passed by some fashion they are captured as well so the function just checks for the most +/// common true and false strings then uses stoll to convert the rest for summing +template ::value && !std::is_unsigned::value, detail::enabler> = detail::dummy> +void sum_flag_vector(const std::vector &flags, T &output) { + std::int64_t count{0}; + for(auto &flag : flags) { + count += detail::to_flag_value(flag); + } + std::string out = detail::to_string(count); + lexical_cast(out, output); +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +} // namespace detail + + + +namespace detail { + +// Returns false if not a short option. Otherwise, sets opt name and rest and returns true +inline bool split_short(const std::string ¤t, std::string &name, std::string &rest) { + if(current.size() > 1 && current[0] == '-' && valid_first_char(current[1])) { + name = current.substr(1, 1); + rest = current.substr(2); + return true; + } + return false; +} + +// Returns false if not a long option. Otherwise, sets opt name and other side of = and returns true +inline bool split_long(const std::string ¤t, std::string &name, std::string &value) { + if(current.size() > 2 && current.substr(0, 2) == "--" && valid_first_char(current[2])) { + auto loc = current.find_first_of('='); + if(loc != std::string::npos) { + name = current.substr(2, loc - 2); + value = current.substr(loc + 1); + } else { + name = current.substr(2); + value = ""; + } + return true; + } + return false; +} + +// Returns false if not a windows style option. Otherwise, sets opt name and value and returns true +inline bool split_windows_style(const std::string ¤t, std::string &name, std::string &value) { + if(current.size() > 1 && current[0] == '/' && valid_first_char(current[1])) { + auto loc = current.find_first_of(':'); + if(loc != std::string::npos) { + name = current.substr(1, loc - 1); + value = current.substr(loc + 1); + } else { + name = current.substr(1); + value = ""; + } + return true; + } + return false; +} + +// Splits a string into multiple long and short names +inline std::vector split_names(std::string current) { + std::vector output; + std::size_t val; + while((val = current.find(",")) != std::string::npos) { + output.push_back(trim_copy(current.substr(0, val))); + current = current.substr(val + 1); + } + output.push_back(trim_copy(current)); + return output; +} + +/// extract default flag values either {def} or starting with a ! +inline std::vector> get_default_flag_values(const std::string &str) { + std::vector flags = split_names(str); + flags.erase(std::remove_if(flags.begin(), + flags.end(), + [](const std::string &name) { + return ((name.empty()) || (!(((name.find_first_of('{') != std::string::npos) && + (name.back() == '}')) || + (name[0] == '!')))); + }), + flags.end()); + std::vector> output; + output.reserve(flags.size()); + for(auto &flag : flags) { + auto def_start = flag.find_first_of('{'); + std::string defval = "false"; + if((def_start != std::string::npos) && (flag.back() == '}')) { + defval = flag.substr(def_start + 1); + defval.pop_back(); + flag.erase(def_start, std::string::npos); + } + flag.erase(0, flag.find_first_not_of("-!")); + output.emplace_back(flag, defval); + } + return output; +} + +/// Get a vector of short names, one of long names, and a single name +inline std::tuple, std::vector, std::string> +get_names(const std::vector &input) { + + std::vector short_names; + std::vector long_names; + std::string pos_name; + + for(std::string name : input) { + if(name.length() == 0) { + continue; + } + if(name.length() > 1 && name[0] == '-' && name[1] != '-') { + if(name.length() == 2 && valid_first_char(name[1])) + short_names.emplace_back(1, name[1]); + else + throw BadNameString::OneCharName(name); + } else if(name.length() > 2 && name.substr(0, 2) == "--") { + name = name.substr(2); + if(valid_name_string(name)) + long_names.push_back(name); + else + throw BadNameString::BadLongName(name); + } else if(name == "-" || name == "--") { + throw BadNameString::DashesOnly(name); + } else { + if(pos_name.length() > 0) + throw BadNameString::MultiPositionalNames(name); + pos_name = name; + } + } + + return std::tuple, std::vector, std::string>( + short_names, long_names, pos_name); +} + +} // namespace detail + + + +class App; + +/// Holds values to load into Options +struct ConfigItem { + /// This is the list of parents + std::vector parents{}; + + /// This is the name + std::string name{}; + + /// Listing of inputs + std::vector inputs{}; + + /// The list of parents and name joined by "." + std::string fullname() const { + std::vector tmp = parents; + tmp.emplace_back(name); + return detail::join(tmp, "."); + } +}; + +/// This class provides a converter for configuration files. +class Config { + protected: + std::vector items{}; + + public: + /// Convert an app into a configuration + virtual std::string to_config(const App *, bool, bool, std::string) const = 0; + + /// Convert a configuration into an app + virtual std::vector from_config(std::istream &) const = 0; + + /// Get a flag value + virtual std::string to_flag(const ConfigItem &item) const { + if(item.inputs.size() == 1) { + return item.inputs.at(0); + } + throw ConversionError::TooManyInputsFlag(item.fullname()); + } + + /// Parse a config file, throw an error (ParseError:ConfigParseError or FileError) on failure + std::vector from_file(const std::string &name) { + std::ifstream input{name}; + if(!input.good()) + throw FileError::Missing(name); + + return from_config(input); + } + + /// Virtual destructor + virtual ~Config() = default; +}; + +/// This converter works with INI/TOML files; to write INI files use ConfigINI +class ConfigBase : public Config { + protected: + /// the character used for comments + char commentChar = '#'; + /// the character used to start an array '\0' is a default to not use + char arrayStart = '['; + /// the character used to end an array '\0' is a default to not use + char arrayEnd = ']'; + /// the character used to separate elements in an array + char arraySeparator = ','; + /// the character used separate the name from the value + char valueDelimiter = '='; + /// the character to use around strings + char stringQuote = '"'; + /// the character to use around single characters + char characterQuote = '\''; + /// the maximum number of layers to allow + uint8_t maximumLayers{255}; + /// the separator used to separator parent layers + char parentSeparatorChar{'.'}; + /// Specify the configuration index to use for arrayed sections + int16_t configIndex{-1}; + /// Specify the configuration section that should be used + std::string configSection{}; + + public: + std::string + to_config(const App * /*app*/, bool default_also, bool write_description, std::string prefix) const override; + + std::vector from_config(std::istream &input) const override; + /// Specify the configuration for comment characters + ConfigBase *comment(char cchar) { + commentChar = cchar; + return this; + } + /// Specify the start and end characters for an array + ConfigBase *arrayBounds(char aStart, char aEnd) { + arrayStart = aStart; + arrayEnd = aEnd; + return this; + } + /// Specify the delimiter character for an array + ConfigBase *arrayDelimiter(char aSep) { + arraySeparator = aSep; + return this; + } + /// Specify the delimiter between a name and value + ConfigBase *valueSeparator(char vSep) { + valueDelimiter = vSep; + return this; + } + /// Specify the quote characters used around strings and characters + ConfigBase *quoteCharacter(char qString, char qChar) { + stringQuote = qString; + characterQuote = qChar; + return this; + } + /// Specify the maximum number of parents + ConfigBase *maxLayers(uint8_t layers) { + maximumLayers = layers; + return this; + } + /// Specify the separator to use for parent layers + ConfigBase *parentSeparator(char sep) { + parentSeparatorChar = sep; + return this; + } + /// get a reference to the configuration section + std::string §ionRef() { return configSection; } + /// get the section + const std::string §ion() const { return configSection; } + /// specify a particular section of the configuration file to use + ConfigBase *section(const std::string §ionName) { + configSection = sectionName; + return this; + } + + /// get a reference to the configuration index + int16_t &indexRef() { return configIndex; } + /// get the section index + int16_t index() const { return configIndex; } + /// specify a particular index in the section to use (-1) for all sections to use + ConfigBase *index(int16_t sectionIndex) { + configIndex = sectionIndex; + return this; + } +}; + +/// the default Config is the TOML file format +using ConfigTOML = ConfigBase; + +/// ConfigINI generates a "standard" INI compliant output +class ConfigINI : public ConfigTOML { + + public: + ConfigINI() { + commentChar = ';'; + arrayStart = '\0'; + arrayEnd = '\0'; + arraySeparator = ' '; + valueDelimiter = '='; + } +}; + + + +class Option; + +/// @defgroup validator_group Validators + +/// @brief Some validators that are provided +/// +/// These are simple `std::string(const std::string&)` validators that are useful. They return +/// a string if the validation fails. A custom struct is provided, as well, with the same user +/// semantics, but with the ability to provide a new type name. +/// @{ + +/// +class Validator { + protected: + /// This is the description function, if empty the description_ will be used + std::function desc_function_{[]() { return std::string{}; }}; + + /// This is the base function that is to be called. + /// Returns a string error message if validation fails. + std::function func_{[](std::string &) { return std::string{}; }}; + /// The name for search purposes of the Validator + std::string name_{}; + /// A Validator will only apply to an indexed value (-1 is all elements) + int application_index_ = -1; + /// Enable for Validator to allow it to be disabled if need be + bool active_{true}; + /// specify that a validator should not modify the input + bool non_modifying_{false}; + + public: + Validator() = default; + /// Construct a Validator with just the description string + explicit Validator(std::string validator_desc) : desc_function_([validator_desc]() { return validator_desc; }) {} + /// Construct Validator from basic information + Validator(std::function op, std::string validator_desc, std::string validator_name = "") + : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(op)), + name_(std::move(validator_name)) {} + /// Set the Validator operation function + Validator &operation(std::function op) { + func_ = std::move(op); + return *this; + } + /// This is the required operator for a Validator - provided to help + /// users (CLI11 uses the member `func` directly) + std::string operator()(std::string &str) const { + std::string retstring; + if(active_) { + if(non_modifying_) { + std::string value = str; + retstring = func_(value); + } else { + retstring = func_(str); + } + } + return retstring; + } + + /// This is the required operator for a Validator - provided to help + /// users (CLI11 uses the member `func` directly) + std::string operator()(const std::string &str) const { + std::string value = str; + return (active_) ? func_(value) : std::string{}; + } + + /// Specify the type string + Validator &description(std::string validator_desc) { + desc_function_ = [validator_desc]() { return validator_desc; }; + return *this; + } + /// Specify the type string + Validator description(std::string validator_desc) const { + Validator newval(*this); + newval.desc_function_ = [validator_desc]() { return validator_desc; }; + return newval; + } + /// Generate type description information for the Validator + std::string get_description() const { + if(active_) { + return desc_function_(); + } + return std::string{}; + } + /// Specify the type string + Validator &name(std::string validator_name) { + name_ = std::move(validator_name); + return *this; + } + /// Specify the type string + Validator name(std::string validator_name) const { + Validator newval(*this); + newval.name_ = std::move(validator_name); + return newval; + } + /// Get the name of the Validator + const std::string &get_name() const { return name_; } + /// Specify whether the Validator is active or not + Validator &active(bool active_val = true) { + active_ = active_val; + return *this; + } + /// Specify whether the Validator is active or not + Validator active(bool active_val = true) const { + Validator newval(*this); + newval.active_ = active_val; + return newval; + } + + /// Specify whether the Validator can be modifying or not + Validator &non_modifying(bool no_modify = true) { + non_modifying_ = no_modify; + return *this; + } + /// Specify the application index of a validator + Validator &application_index(int app_index) { + application_index_ = app_index; + return *this; + } + /// Specify the application index of a validator + Validator application_index(int app_index) const { + Validator newval(*this); + newval.application_index_ = app_index; + return newval; + } + /// Get the current value of the application index + int get_application_index() const { return application_index_; } + /// Get a boolean if the validator is active + bool get_active() const { return active_; } + + /// Get a boolean if the validator is allowed to modify the input returns true if it can modify the input + bool get_modifying() const { return !non_modifying_; } + + /// Combining validators is a new validator. Type comes from left validator if function, otherwise only set if the + /// same. + Validator operator&(const Validator &other) const { + Validator newval; + + newval._merge_description(*this, other, " AND "); + + // Give references (will make a copy in lambda function) + const std::function &f1 = func_; + const std::function &f2 = other.func_; + + newval.func_ = [f1, f2](std::string &input) { + std::string s1 = f1(input); + std::string s2 = f2(input); + if(!s1.empty() && !s2.empty()) + return std::string("(") + s1 + ") AND (" + s2 + ")"; + else + return s1 + s2; + }; + + newval.active_ = (active_ & other.active_); + newval.application_index_ = application_index_; + return newval; + } + + /// Combining validators is a new validator. Type comes from left validator if function, otherwise only set if the + /// same. + Validator operator|(const Validator &other) const { + Validator newval; + + newval._merge_description(*this, other, " OR "); + + // Give references (will make a copy in lambda function) + const std::function &f1 = func_; + const std::function &f2 = other.func_; + + newval.func_ = [f1, f2](std::string &input) { + std::string s1 = f1(input); + std::string s2 = f2(input); + if(s1.empty() || s2.empty()) + return std::string(); + + return std::string("(") + s1 + ") OR (" + s2 + ")"; + }; + newval.active_ = (active_ & other.active_); + newval.application_index_ = application_index_; + return newval; + } + + /// Create a validator that fails when a given validator succeeds + Validator operator!() const { + Validator newval; + const std::function &dfunc1 = desc_function_; + newval.desc_function_ = [dfunc1]() { + auto str = dfunc1(); + return (!str.empty()) ? std::string("NOT ") + str : std::string{}; + }; + // Give references (will make a copy in lambda function) + const std::function &f1 = func_; + + newval.func_ = [f1, dfunc1](std::string &test) -> std::string { + std::string s1 = f1(test); + if(s1.empty()) { + return std::string("check ") + dfunc1() + " succeeded improperly"; + } + return std::string{}; + }; + newval.active_ = active_; + newval.application_index_ = application_index_; + return newval; + } + + private: + void _merge_description(const Validator &val1, const Validator &val2, const std::string &merger) { + + const std::function &dfunc1 = val1.desc_function_; + const std::function &dfunc2 = val2.desc_function_; + + desc_function_ = [=]() { + std::string f1 = dfunc1(); + std::string f2 = dfunc2(); + if((f1.empty()) || (f2.empty())) { + return f1 + f2; + } + return std::string(1, '(') + f1 + ')' + merger + '(' + f2 + ')'; + }; + } +}; // namespace CLI + +/// Class wrapping some of the accessors of Validator +class CustomValidator : public Validator { + public: +}; +// The implementation of the built in validators is using the Validator class; +// the user is only expected to use the const (static) versions (since there's no setup). +// Therefore, this is in detail. +namespace detail { + +/// CLI enumeration of different file types +enum class path_type { nonexistent, file, directory }; + +#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0 +/// get the type of the path from a file name +inline path_type check_path(const char *file) noexcept { + std::error_code ec; + auto stat = std::filesystem::status(file, ec); + if(ec) { + return path_type::nonexistent; + } + switch(stat.type()) { + case std::filesystem::file_type::none: + case std::filesystem::file_type::not_found: + return path_type::nonexistent; + case std::filesystem::file_type::directory: + return path_type::directory; + case std::filesystem::file_type::symlink: + case std::filesystem::file_type::block: + case std::filesystem::file_type::character: + case std::filesystem::file_type::fifo: + case std::filesystem::file_type::socket: + case std::filesystem::file_type::regular: + case std::filesystem::file_type::unknown: + default: + return path_type::file; + } +} +#else +/// get the type of the path from a file name +inline path_type check_path(const char *file) noexcept { +#if defined(_MSC_VER) + struct __stat64 buffer; + if(_stat64(file, &buffer) == 0) { + return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file; + } +#else + struct stat buffer; + if(stat(file, &buffer) == 0) { + return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file; + } +#endif + return path_type::nonexistent; +} +#endif +/// Check for an existing file (returns error message if check fails) +class ExistingFileValidator : public Validator { + public: + ExistingFileValidator() : Validator("FILE") { + func_ = [](std::string &filename) { + auto path_result = check_path(filename.c_str()); + if(path_result == path_type::nonexistent) { + return "File does not exist: " + filename; + } + if(path_result == path_type::directory) { + return "File is actually a directory: " + filename; + } + return std::string(); + }; + } +}; + +/// Check for an existing directory (returns error message if check fails) +class ExistingDirectoryValidator : public Validator { + public: + ExistingDirectoryValidator() : Validator("DIR") { + func_ = [](std::string &filename) { + auto path_result = check_path(filename.c_str()); + if(path_result == path_type::nonexistent) { + return "Directory does not exist: " + filename; + } + if(path_result == path_type::file) { + return "Directory is actually a file: " + filename; + } + return std::string(); + }; + } +}; + +/// Check for an existing path +class ExistingPathValidator : public Validator { + public: + ExistingPathValidator() : Validator("PATH(existing)") { + func_ = [](std::string &filename) { + auto path_result = check_path(filename.c_str()); + if(path_result == path_type::nonexistent) { + return "Path does not exist: " + filename; + } + return std::string(); + }; + } +}; + +/// Check for an non-existing path +class NonexistentPathValidator : public Validator { + public: + NonexistentPathValidator() : Validator("PATH(non-existing)") { + func_ = [](std::string &filename) { + auto path_result = check_path(filename.c_str()); + if(path_result != path_type::nonexistent) { + return "Path already exists: " + filename; + } + return std::string(); + }; + } +}; + +/// Validate the given string is a legal ipv4 address +class IPV4Validator : public Validator { + public: + IPV4Validator() : Validator("IPV4") { + func_ = [](std::string &ip_addr) { + auto result = CLI::detail::split(ip_addr, '.'); + if(result.size() != 4) { + return std::string("Invalid IPV4 address must have four parts (") + ip_addr + ')'; + } + int num; + for(const auto &var : result) { + bool retval = detail::lexical_cast(var, num); + if(!retval) { + return std::string("Failed parsing number (") + var + ')'; + } + if(num < 0 || num > 255) { + return std::string("Each IP number must be between 0 and 255 ") + var; + } + } + return std::string(); + }; + } +}; + +} // namespace detail + +// Static is not needed here, because global const implies static. + +/// Check for existing file (returns error message if check fails) +const detail::ExistingFileValidator ExistingFile; + +/// Check for an existing directory (returns error message if check fails) +const detail::ExistingDirectoryValidator ExistingDirectory; + +/// Check for an existing path +const detail::ExistingPathValidator ExistingPath; + +/// Check for an non-existing path +const detail::NonexistentPathValidator NonexistentPath; + +/// Check for an IP4 address +const detail::IPV4Validator ValidIPV4; + +/// Validate the input as a particular type +template class TypeValidator : public Validator { + public: + explicit TypeValidator(const std::string &validator_name) : Validator(validator_name) { + func_ = [](std::string &input_string) { + auto val = DesiredType(); + if(!detail::lexical_cast(input_string, val)) { + return std::string("Failed parsing ") + input_string + " as a " + detail::type_name(); + } + return std::string(); + }; + } + TypeValidator() : TypeValidator(detail::type_name()) {} +}; + +/// Check for a number +const TypeValidator Number("NUMBER"); + +/// Produce a range (factory). Min and max are inclusive. +class Range : public Validator { + public: + /// This produces a range with min and max inclusive. + /// + /// Note that the constructor is templated, but the struct is not, so C++17 is not + /// needed to provide nice syntax for Range(a,b). + template + Range(T min_val, T max_val, const std::string &validator_name = std::string{}) : Validator(validator_name) { + if(validator_name.empty()) { + std::stringstream out; + out << detail::type_name() << " in [" << min_val << " - " << max_val << "]"; + description(out.str()); + } + + func_ = [min_val, max_val](std::string &input) { + T val; + bool converted = detail::lexical_cast(input, val); + if((!converted) || (val < min_val || val > max_val)) + return std::string("Value ") + input + " not in range " + std::to_string(min_val) + " to " + + std::to_string(max_val); + + return std::string{}; + }; + } + + /// Range of one value is 0 to value + template + explicit Range(T max_val, const std::string &validator_name = std::string{}) + : Range(static_cast(0), max_val, validator_name) {} +}; + +/// Check for a non negative number +const Range NonNegativeNumber((std::numeric_limits::max)(), "NONNEGATIVE"); + +/// Check for a positive valued number (val>0.0), min() her is the smallest positive number +const Range PositiveNumber((std::numeric_limits::min)(), (std::numeric_limits::max)(), "POSITIVE"); + +/// Produce a bounded range (factory). Min and max are inclusive. +class Bound : public Validator { + public: + /// This bounds a value with min and max inclusive. + /// + /// Note that the constructor is templated, but the struct is not, so C++17 is not + /// needed to provide nice syntax for Range(a,b). + template Bound(T min_val, T max_val) { + std::stringstream out; + out << detail::type_name() << " bounded to [" << min_val << " - " << max_val << "]"; + description(out.str()); + + func_ = [min_val, max_val](std::string &input) { + T val; + bool converted = detail::lexical_cast(input, val); + if(!converted) { + return std::string("Value ") + input + " could not be converted"; + } + if(val < min_val) + input = detail::to_string(min_val); + else if(val > max_val) + input = detail::to_string(max_val); + + return std::string{}; + }; + } + + /// Range of one value is 0 to value + template explicit Bound(T max_val) : Bound(static_cast(0), max_val) {} +}; + +namespace detail { +template ::type>::value, detail::enabler> = detail::dummy> +auto smart_deref(T value) -> decltype(*value) { + return *value; +} + +template < + typename T, + enable_if_t::type>::value, detail::enabler> = detail::dummy> +typename std::remove_reference::type &smart_deref(T &value) { + return value; +} +/// Generate a string representation of a set +template std::string generate_set(const T &set) { + using element_t = typename detail::element_type::type; + using iteration_type_t = typename detail::pair_adaptor::value_type; // the type of the object pair + std::string out(1, '{'); + out.append(detail::join( + detail::smart_deref(set), + [](const iteration_type_t &v) { return detail::pair_adaptor::first(v); }, + ",")); + out.push_back('}'); + return out; +} + +/// Generate a string representation of a map +template std::string generate_map(const T &map, bool key_only = false) { + using element_t = typename detail::element_type::type; + using iteration_type_t = typename detail::pair_adaptor::value_type; // the type of the object pair + std::string out(1, '{'); + out.append(detail::join( + detail::smart_deref(map), + [key_only](const iteration_type_t &v) { + std::string res{detail::to_string(detail::pair_adaptor::first(v))}; + + if(!key_only) { + res.append("->"); + res += detail::to_string(detail::pair_adaptor::second(v)); + } + return res; + }, + ",")); + out.push_back('}'); + return out; +} + +template struct has_find { + template + static auto test(int) -> decltype(std::declval().find(std::declval()), std::true_type()); + template static auto test(...) -> decltype(std::false_type()); + + static const auto value = decltype(test(0))::value; + using type = std::integral_constant; +}; + +/// A search function +template ::value, detail::enabler> = detail::dummy> +auto search(const T &set, const V &val) -> std::pair { + using element_t = typename detail::element_type::type; + auto &setref = detail::smart_deref(set); + auto it = std::find_if(std::begin(setref), std::end(setref), [&val](decltype(*std::begin(setref)) v) { + return (detail::pair_adaptor::first(v) == val); + }); + return {(it != std::end(setref)), it}; +} + +/// A search function that uses the built in find function +template ::value, detail::enabler> = detail::dummy> +auto search(const T &set, const V &val) -> std::pair { + auto &setref = detail::smart_deref(set); + auto it = setref.find(val); + return {(it != std::end(setref)), it}; +} + +/// A search function with a filter function +template +auto search(const T &set, const V &val, const std::function &filter_function) + -> std::pair { + using element_t = typename detail::element_type::type; + // do the potentially faster first search + auto res = search(set, val); + if((res.first) || (!(filter_function))) { + return res; + } + // if we haven't found it do the longer linear search with all the element translations + auto &setref = detail::smart_deref(set); + auto it = std::find_if(std::begin(setref), std::end(setref), [&](decltype(*std::begin(setref)) v) { + V a{detail::pair_adaptor::first(v)}; + a = filter_function(a); + return (a == val); + }); + return {(it != std::end(setref)), it}; +} + +// the following suggestion was made by Nikita Ofitserov(@himikof) +// done in templates to prevent compiler warnings on negation of unsigned numbers + +/// Do a check for overflow on signed numbers +template +inline typename std::enable_if::value, T>::type overflowCheck(const T &a, const T &b) { + if((a > 0) == (b > 0)) { + return ((std::numeric_limits::max)() / (std::abs)(a) < (std::abs)(b)); + } else { + return ((std::numeric_limits::min)() / (std::abs)(a) > -(std::abs)(b)); + } +} +/// Do a check for overflow on unsigned numbers +template +inline typename std::enable_if::value, T>::type overflowCheck(const T &a, const T &b) { + return ((std::numeric_limits::max)() / a < b); +} + +/// Performs a *= b; if it doesn't cause integer overflow. Returns false otherwise. +template typename std::enable_if::value, bool>::type checked_multiply(T &a, T b) { + if(a == 0 || b == 0 || a == 1 || b == 1) { + a *= b; + return true; + } + if(a == (std::numeric_limits::min)() || b == (std::numeric_limits::min)()) { + return false; + } + if(overflowCheck(a, b)) { + return false; + } + a *= b; + return true; +} + +/// Performs a *= b; if it doesn't equal infinity. Returns false otherwise. +template +typename std::enable_if::value, bool>::type checked_multiply(T &a, T b) { + T c = a * b; + if(std::isinf(c) && !std::isinf(a) && !std::isinf(b)) { + return false; + } + a = c; + return true; +} + +} // namespace detail +/// Verify items are in a set +class IsMember : public Validator { + public: + using filter_fn_t = std::function; + + /// This allows in-place construction using an initializer list + template + IsMember(std::initializer_list values, Args &&...args) + : IsMember(std::vector(values), std::forward(args)...) {} + + /// This checks to see if an item is in a set (empty function) + template explicit IsMember(T &&set) : IsMember(std::forward(set), nullptr) {} + + /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter + /// both sides of the comparison before computing the comparison. + template explicit IsMember(T set, F filter_function) { + + // Get the type of the contained item - requires a container have ::value_type + // if the type does not have first_type and second_type, these are both value_type + using element_t = typename detail::element_type::type; // Removes (smart) pointers if needed + using item_t = typename detail::pair_adaptor::first_type; // Is value_type if not a map + + using local_item_t = typename IsMemberType::type; // This will convert bad types to good ones + // (const char * to std::string) + + // Make a local copy of the filter function, using a std::function if not one already + std::function filter_fn = filter_function; + + // This is the type name for help, it will take the current version of the set contents + desc_function_ = [set]() { return detail::generate_set(detail::smart_deref(set)); }; + + // This is the function that validates + // It stores a copy of the set pointer-like, so shared_ptr will stay alive + func_ = [set, filter_fn](std::string &input) { + local_item_t b; + if(!detail::lexical_cast(input, b)) { + throw ValidationError(input); // name is added later + } + if(filter_fn) { + b = filter_fn(b); + } + auto res = detail::search(set, b, filter_fn); + if(res.first) { + // Make sure the version in the input string is identical to the one in the set + if(filter_fn) { + input = detail::value_string(detail::pair_adaptor::first(*(res.second))); + } + + // Return empty error string (success) + return std::string{}; + } + + // If you reach this point, the result was not found + return input + " not in " + detail::generate_set(detail::smart_deref(set)); + }; + } + + /// You can pass in as many filter functions as you like, they nest (string only currently) + template + IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other) + : IsMember( + std::forward(set), + [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); }, + other...) {} +}; + +/// definition of the default transformation object +template using TransformPairs = std::vector>; + +/// Translate named items to other or a value set +class Transformer : public Validator { + public: + using filter_fn_t = std::function; + + /// This allows in-place construction + template + Transformer(std::initializer_list> values, Args &&...args) + : Transformer(TransformPairs(values), std::forward(args)...) {} + + /// direct map of std::string to std::string + template explicit Transformer(T &&mapping) : Transformer(std::forward(mapping), nullptr) {} + + /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter + /// both sides of the comparison before computing the comparison. + template explicit Transformer(T mapping, F filter_function) { + + static_assert(detail::pair_adaptor::type>::value, + "mapping must produce value pairs"); + // Get the type of the contained item - requires a container have ::value_type + // if the type does not have first_type and second_type, these are both value_type + using element_t = typename detail::element_type::type; // Removes (smart) pointers if needed + using item_t = typename detail::pair_adaptor::first_type; // Is value_type if not a map + using local_item_t = typename IsMemberType::type; // Will convert bad types to good ones + // (const char * to std::string) + + // Make a local copy of the filter function, using a std::function if not one already + std::function filter_fn = filter_function; + + // This is the type name for help, it will take the current version of the set contents + desc_function_ = [mapping]() { return detail::generate_map(detail::smart_deref(mapping)); }; + + func_ = [mapping, filter_fn](std::string &input) { + local_item_t b; + if(!detail::lexical_cast(input, b)) { + return std::string(); + // there is no possible way we can match anything in the mapping if we can't convert so just return + } + if(filter_fn) { + b = filter_fn(b); + } + auto res = detail::search(mapping, b, filter_fn); + if(res.first) { + input = detail::value_string(detail::pair_adaptor::second(*res.second)); + } + return std::string{}; + }; + } + + /// You can pass in as many filter functions as you like, they nest + template + Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other) + : Transformer( + std::forward(mapping), + [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); }, + other...) {} +}; + +/// translate named items to other or a value set +class CheckedTransformer : public Validator { + public: + using filter_fn_t = std::function; + + /// This allows in-place construction + template + CheckedTransformer(std::initializer_list> values, Args &&...args) + : CheckedTransformer(TransformPairs(values), std::forward(args)...) {} + + /// direct map of std::string to std::string + template explicit CheckedTransformer(T mapping) : CheckedTransformer(std::move(mapping), nullptr) {} + + /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter + /// both sides of the comparison before computing the comparison. + template explicit CheckedTransformer(T mapping, F filter_function) { + + static_assert(detail::pair_adaptor::type>::value, + "mapping must produce value pairs"); + // Get the type of the contained item - requires a container have ::value_type + // if the type does not have first_type and second_type, these are both value_type + using element_t = typename detail::element_type::type; // Removes (smart) pointers if needed + using item_t = typename detail::pair_adaptor::first_type; // Is value_type if not a map + using local_item_t = typename IsMemberType::type; // Will convert bad types to good ones + // (const char * to std::string) + using iteration_type_t = typename detail::pair_adaptor::value_type; // the type of the object pair + + // Make a local copy of the filter function, using a std::function if not one already + std::function filter_fn = filter_function; + + auto tfunc = [mapping]() { + std::string out("value in "); + out += detail::generate_map(detail::smart_deref(mapping)) + " OR {"; + out += detail::join( + detail::smart_deref(mapping), + [](const iteration_type_t &v) { return detail::to_string(detail::pair_adaptor::second(v)); }, + ","); + out.push_back('}'); + return out; + }; + + desc_function_ = tfunc; + + func_ = [mapping, tfunc, filter_fn](std::string &input) { + local_item_t b; + bool converted = detail::lexical_cast(input, b); + if(converted) { + if(filter_fn) { + b = filter_fn(b); + } + auto res = detail::search(mapping, b, filter_fn); + if(res.first) { + input = detail::value_string(detail::pair_adaptor::second(*res.second)); + return std::string{}; + } + } + for(const auto &v : detail::smart_deref(mapping)) { + auto output_string = detail::value_string(detail::pair_adaptor::second(v)); + if(output_string == input) { + return std::string(); + } + } + + return "Check " + input + " " + tfunc() + " FAILED"; + }; + } + + /// You can pass in as many filter functions as you like, they nest + template + CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other) + : CheckedTransformer( + std::forward(mapping), + [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); }, + other...) {} +}; + +/// Helper function to allow ignore_case to be passed to IsMember or Transform +inline std::string ignore_case(std::string item) { return detail::to_lower(item); } + +/// Helper function to allow ignore_underscore to be passed to IsMember or Transform +inline std::string ignore_underscore(std::string item) { return detail::remove_underscore(item); } + +/// Helper function to allow checks to ignore spaces to be passed to IsMember or Transform +inline std::string ignore_space(std::string item) { + item.erase(std::remove(std::begin(item), std::end(item), ' '), std::end(item)); + item.erase(std::remove(std::begin(item), std::end(item), '\t'), std::end(item)); + return item; +} + +/// Multiply a number by a factor using given mapping. +/// Can be used to write transforms for SIZE or DURATION inputs. +/// +/// Example: +/// With mapping = `{"b"->1, "kb"->1024, "mb"->1024*1024}` +/// one can recognize inputs like "100", "12kb", "100 MB", +/// that will be automatically transformed to 100, 14448, 104857600. +/// +/// Output number type matches the type in the provided mapping. +/// Therefore, if it is required to interpret real inputs like "0.42 s", +/// the mapping should be of a type or . +class AsNumberWithUnit : public Validator { + public: + /// Adjust AsNumberWithUnit behavior. + /// CASE_SENSITIVE/CASE_INSENSITIVE controls how units are matched. + /// UNIT_OPTIONAL/UNIT_REQUIRED throws ValidationError + /// if UNIT_REQUIRED is set and unit literal is not found. + enum Options { + CASE_SENSITIVE = 0, + CASE_INSENSITIVE = 1, + UNIT_OPTIONAL = 0, + UNIT_REQUIRED = 2, + DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL + }; + + template + explicit AsNumberWithUnit(std::map mapping, + Options opts = DEFAULT, + const std::string &unit_name = "UNIT") { + description(generate_description(unit_name, opts)); + validate_mapping(mapping, opts); + + // transform function + func_ = [mapping, opts](std::string &input) -> std::string { + Number num; + + detail::rtrim(input); + if(input.empty()) { + throw ValidationError("Input is empty"); + } + + // Find split position between number and prefix + auto unit_begin = input.end(); + while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) { + --unit_begin; + } + + std::string unit{unit_begin, input.end()}; + input.resize(static_cast(std::distance(input.begin(), unit_begin))); + detail::trim(input); + + if(opts & UNIT_REQUIRED && unit.empty()) { + throw ValidationError("Missing mandatory unit"); + } + if(opts & CASE_INSENSITIVE) { + unit = detail::to_lower(unit); + } + if(unit.empty()) { + if(!detail::lexical_cast(input, num)) { + throw ValidationError(std::string("Value ") + input + " could not be converted to " + + detail::type_name()); + } + // No need to modify input if no unit passed + return {}; + } + + // find corresponding factor + auto it = mapping.find(unit); + if(it == mapping.end()) { + throw ValidationError(unit + + " unit not recognized. " + "Allowed values: " + + detail::generate_map(mapping, true)); + } + + if(!input.empty()) { + bool converted = detail::lexical_cast(input, num); + if(!converted) { + throw ValidationError(std::string("Value ") + input + " could not be converted to " + + detail::type_name()); + } + // perform safe multiplication + bool ok = detail::checked_multiply(num, it->second); + if(!ok) { + throw ValidationError(detail::to_string(num) + " multiplied by " + unit + + " factor would cause number overflow. Use smaller value."); + } + } else { + num = static_cast(it->second); + } + + input = detail::to_string(num); + + return {}; + }; + } + + private: + /// Check that mapping contains valid units. + /// Update mapping for CASE_INSENSITIVE mode. + template static void validate_mapping(std::map &mapping, Options opts) { + for(auto &kv : mapping) { + if(kv.first.empty()) { + throw ValidationError("Unit must not be empty."); + } + if(!detail::isalpha(kv.first)) { + throw ValidationError("Unit must contain only letters."); + } + } + + // make all units lowercase if CASE_INSENSITIVE + if(opts & CASE_INSENSITIVE) { + std::map lower_mapping; + for(auto &kv : mapping) { + auto s = detail::to_lower(kv.first); + if(lower_mapping.count(s)) { + throw ValidationError(std::string("Several matching lowercase unit representations are found: ") + + s); + } + lower_mapping[detail::to_lower(kv.first)] = kv.second; + } + mapping = std::move(lower_mapping); + } + } + + /// Generate description like this: NUMBER [UNIT] + template static std::string generate_description(const std::string &name, Options opts) { + std::stringstream out; + out << detail::type_name() << ' '; + if(opts & UNIT_REQUIRED) { + out << name; + } else { + out << '[' << name << ']'; + } + return out.str(); + } +}; + +/// Converts a human-readable size string (with unit literal) to uin64_t size. +/// Example: +/// "100" => 100 +/// "1 b" => 100 +/// "10Kb" => 10240 // you can configure this to be interpreted as kilobyte (*1000) or kibibyte (*1024) +/// "10 KB" => 10240 +/// "10 kb" => 10240 +/// "10 kib" => 10240 // *i, *ib are always interpreted as *bibyte (*1024) +/// "10kb" => 10240 +/// "2 MB" => 2097152 +/// "2 EiB" => 2^61 // Units up to exibyte are supported +class AsSizeValue : public AsNumberWithUnit { + public: + using result_t = std::uint64_t; + + /// If kb_is_1000 is true, + /// interpret 'kb', 'k' as 1000 and 'kib', 'ki' as 1024 + /// (same applies to higher order units as well). + /// Otherwise, interpret all literals as factors of 1024. + /// The first option is formally correct, but + /// the second interpretation is more wide-spread + /// (see https://en.wikipedia.org/wiki/Binary_prefix). + explicit AsSizeValue(bool kb_is_1000) : AsNumberWithUnit(get_mapping(kb_is_1000)) { + if(kb_is_1000) { + description("SIZE [b, kb(=1000b), kib(=1024b), ...]"); + } else { + description("SIZE [b, kb(=1024b), ...]"); + } + } + + private: + /// Get mapping + static std::map init_mapping(bool kb_is_1000) { + std::map m; + result_t k_factor = kb_is_1000 ? 1000 : 1024; + result_t ki_factor = 1024; + result_t k = 1; + result_t ki = 1; + m["b"] = 1; + for(std::string p : {"k", "m", "g", "t", "p", "e"}) { + k *= k_factor; + ki *= ki_factor; + m[p] = k; + m[p + "b"] = k; + m[p + "i"] = ki; + m[p + "ib"] = ki; + } + return m; + } + + /// Cache calculated mapping + static std::map get_mapping(bool kb_is_1000) { + if(kb_is_1000) { + static auto m = init_mapping(true); + return m; + } else { + static auto m = init_mapping(false); + return m; + } + } +}; + +namespace detail { +/// Split a string into a program name and command line arguments +/// the string is assumed to contain a file name followed by other arguments +/// the return value contains is a pair with the first argument containing the program name and the second +/// everything else. +inline std::pair split_program_name(std::string commandline) { + // try to determine the programName + std::pair vals; + trim(commandline); + auto esp = commandline.find_first_of(' ', 1); + while(detail::check_path(commandline.substr(0, esp).c_str()) != path_type::file) { + esp = commandline.find_first_of(' ', esp + 1); + if(esp == std::string::npos) { + // if we have reached the end and haven't found a valid file just assume the first argument is the + // program name + if(commandline[0] == '"' || commandline[0] == '\'' || commandline[0] == '`') { + bool embeddedQuote = false; + auto keyChar = commandline[0]; + auto end = commandline.find_first_of(keyChar, 1); + while((end != std::string::npos) && (commandline[end - 1] == '\\')) { // deal with escaped quotes + end = commandline.find_first_of(keyChar, end + 1); + embeddedQuote = true; + } + if(end != std::string::npos) { + vals.first = commandline.substr(1, end - 1); + esp = end + 1; + if(embeddedQuote) { + vals.first = find_and_replace(vals.first, std::string("\\") + keyChar, std::string(1, keyChar)); + } + } else { + esp = commandline.find_first_of(' ', 1); + } + } else { + esp = commandline.find_first_of(' ', 1); + } + + break; + } + } + if(vals.first.empty()) { + vals.first = commandline.substr(0, esp); + rtrim(vals.first); + } + + // strip the program name + vals.second = (esp != std::string::npos) ? commandline.substr(esp + 1) : std::string{}; + ltrim(vals.second); + return vals; +} + +} // namespace detail +/// @} + + + + +class Option; +class App; + +/// This enum signifies the type of help requested +/// +/// This is passed in by App; all user classes must accept this as +/// the second argument. + +enum class AppFormatMode { + Normal, ///< The normal, detailed help + All, ///< A fully expanded help + Sub, ///< Used when printed as part of expanded subcommand +}; + +/// This is the minimum requirements to run a formatter. +/// +/// A user can subclass this is if they do not care at all +/// about the structure in CLI::Formatter. +class FormatterBase { + protected: + /// @name Options + ///@{ + + /// The width of the first column + std::size_t column_width_{30}; + + /// @brief The required help printout labels (user changeable) + /// Values are Needs, Excludes, etc. + std::map labels_{}; + + ///@} + /// @name Basic + ///@{ + + public: + FormatterBase() = default; + FormatterBase(const FormatterBase &) = default; + FormatterBase(FormatterBase &&) = default; + + /// Adding a destructor in this form to work around bug in GCC 4.7 + virtual ~FormatterBase() noexcept {} // NOLINT(modernize-use-equals-default) + + /// This is the key method that puts together help + virtual std::string make_help(const App *, std::string, AppFormatMode) const = 0; + + ///@} + /// @name Setters + ///@{ + + /// Set the "REQUIRED" label + void label(std::string key, std::string val) { labels_[key] = val; } + + /// Set the column width + void column_width(std::size_t val) { column_width_ = val; } + + ///@} + /// @name Getters + ///@{ + + /// Get the current value of a name (REQUIRED, etc.) + std::string get_label(std::string key) const { + if(labels_.find(key) == labels_.end()) + return key; + else + return labels_.at(key); + } + + /// Get the current column width + std::size_t get_column_width() const { return column_width_; } + + ///@} +}; + +/// This is a specialty override for lambda functions +class FormatterLambda final : public FormatterBase { + using funct_t = std::function; + + /// The lambda to hold and run + funct_t lambda_; + + public: + /// Create a FormatterLambda with a lambda function + explicit FormatterLambda(funct_t funct) : lambda_(std::move(funct)) {} + + /// Adding a destructor (mostly to make GCC 4.7 happy) + ~FormatterLambda() noexcept override {} // NOLINT(modernize-use-equals-default) + + /// This will simply call the lambda function + std::string make_help(const App *app, std::string name, AppFormatMode mode) const override { + return lambda_(app, name, mode); + } +}; + +/// This is the default Formatter for CLI11. It pretty prints help output, and is broken into quite a few +/// overridable methods, to be highly customizable with minimal effort. +class Formatter : public FormatterBase { + public: + Formatter() = default; + Formatter(const Formatter &) = default; + Formatter(Formatter &&) = default; + + /// @name Overridables + ///@{ + + /// This prints out a group of options with title + /// + virtual std::string make_group(std::string group, bool is_positional, std::vector opts) const; + + /// This prints out just the positionals "group" + virtual std::string make_positionals(const App *app) const; + + /// This prints out all the groups of options + std::string make_groups(const App *app, AppFormatMode mode) const; + + /// This prints out all the subcommands + virtual std::string make_subcommands(const App *app, AppFormatMode mode) const; + + /// This prints out a subcommand + virtual std::string make_subcommand(const App *sub) const; + + /// This prints out a subcommand in help-all + virtual std::string make_expanded(const App *sub) const; + + /// This prints out all the groups of options + virtual std::string make_footer(const App *app) const; + + /// This displays the description line + virtual std::string make_description(const App *app) const; + + /// This displays the usage line + virtual std::string make_usage(const App *app, std::string name) const; + + /// This puts everything together + std::string make_help(const App * /*app*/, std::string, AppFormatMode) const override; + + ///@} + /// @name Options + ///@{ + + /// This prints out an option help line, either positional or optional form + virtual std::string make_option(const Option *opt, bool is_positional) const { + std::stringstream out; + detail::format_help( + out, make_option_name(opt, is_positional) + make_option_opts(opt), make_option_desc(opt), column_width_); + return out.str(); + } + + /// @brief This is the name part of an option, Default: left column + virtual std::string make_option_name(const Option *, bool) const; + + /// @brief This is the options part of the name, Default: combined into left column + virtual std::string make_option_opts(const Option *) const; + + /// @brief This is the description. Default: Right column, on new line if left column too large + virtual std::string make_option_desc(const Option *) const; + + /// @brief This is used to print the name on the USAGE line + virtual std::string make_option_usage(const Option *opt) const; + + ///@} +}; + + + + +using results_t = std::vector; +/// callback function definition +using callback_t = std::function; + +class Option; +class App; + +using Option_p = std::unique_ptr