Previous step is here.
At this step we will develop nice command line interface for our packer.
At first we need compiled Boost library. If you studied previous steps, you should have it already. If you have not built it yet, I will explain how to do that. For example, you unpacked the library archive to C:\boost directory. Open this directory and run bootstrap.bat file. bjam.exe file will appear in this directory in some time. Run the console (cmd) and go to C:\boost directory with cd command. Type the command
1 |
bjam variant=debug link=static threading=multi runtime-link=static |
wait for debug version with static linking to be built, and then type
1 |
bjam variant=release link=static threading=multi runtime-link=static |
to build the release version. Boost is built and we can turn to the packer (simple_pe_packer project). Add two includes to main.cpp:
1 2 |
#include <boost/program_options.hpp> #include <boost/timer.hpp> |
First one is necessary to implement command line interface, the second one is used to count file packing time. Replace lines
1 2 3 4 5 6 7 8 9 |
//Tell the user how to use our packer //No packing options at this step //You just need to pass a name of the file to pack //via command line if(argc != 2) { std::cout << "Usage: simple_pe_packer.exe PE_FILE" << std::endl; return 0; } |
to these ones:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
//To use short namespace alias namespace po = boost::program_options; //Timer counts the time //required for packing boost::timer pack_timer; //Forced packing - even potentially incorrect file //will be packed bool force_mode = false; //Whether resources should be packed bool repack_resources; //Whether loading configuration directory should be packed bool rebuild_load_config; //Whether DOS header should be stripped bool strip_dos_headers; //File alignment after packing unsigned long file_alignment; //Path to source file std::string input_file_name; //Path to packed file std::string output_file_name; //Create options description po::options_description visible_options("DXPack Packer 1.0\nCommand Line Options"); try { //Create allowed options list //Add default values (not for all of them) po::options_description cmdline; //out-file,o - means that option name is "--out-file" //and its short alias is "-o" visible_options.add_options() ("out-file,o", po::value<std::string>(&output_file_name), "Output file name") ("file-align,a", po::value<unsigned long>(&file_alignment)->default_value(512), "Packed file alignment") ("strip-dos,s", po::value<bool>(&strip_dos_headers)->default_value(true), "Strip DOS headers") ("repack-res,r", po::value<bool>(&repack_resources)->default_value(true), "Repack resources") ("build-load-config,l", po::value<bool>(&rebuild_load_config)->default_value(true), "Rebuild Load Config directory") ("force,f", "Force packing of possibly incorrect binaries") ; cmdline.add(visible_options); //Hidden option - name of the file to pack cmdline.add_options() ("image", po::value<std::string>(&input_file_name), "PE image to pack") ; //Unnamed (name of the file to pack should go first) po::positional_options_description desc_pos; desc_pos.add("image", 1); //Parse the command line po::variables_map vm; po::store(po::command_line_parser(argc, argv). options(cmdline).positional(desc_pos).run(), vm); po::notify(vm); //If the path to source file is not specified if(input_file_name.empty()) throw std::runtime_error("No input file specified"); //If forced packing mode is specified if(vm.count("force")) { std::cout << "Force mode is active!" << std::endl; force_mode = true; } } catch(const std::exception& e) { //If something went wrong - output options description std::cout << e.what() << std::endl << std::endl; std::cout << visible_options << std::endl; system("pause"); return 0; } |
I will not describe the piece of code in detail, just say that we here process our packer command line easily and conveniently using boost::program_options library. I put all options available via command line (bool force_mode, bool repack_resources, bool rebuild_load_config, bool strip_dos_headers, unsigned long file_alignment, std::string input_file_name, std::string output_file_name) to the packer source code, and I will not explain what exactly has changed, because many parts of the code were slightly modified. Besides that, at the end of the source code I made output of the elapsed packing time, which was counted with boost::timer library. You can view all changes as always by downloading the full packer solution.
This version of the packer could be perhaps considered completed. Yes, it creates suspicious imports and probably has other issues, but it is fully operable, it supports things that other packers can't do (for example, TLS with callbacks or loading configuration repacking) and it has command line interface. So, besides the source code I will share the packer EXE file, in case it will be useful for someone.
Full solution for this step: own-packer-step-11
Built packer (EXE): simple_pe_packer.zip
UPD: New version with fixed bugs is here.