├── .gitignore ├── Dockerfile ├── README.rst └── slides ├── .gitignore ├── Makefile └── qemu_plugins.rst /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atos-tools/qemu-plugins-tutorial/d4e23e6fa20604a7179ece1e73069a6044368e3e/.gitignore -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:sid 2 | 3 | RUN echo "deb-src http://deb.debian.org/debian sid main" >> /etc/apt/sources.list 4 | RUN apt update && \ 5 | apt build-dep -y qemu && \ 6 | apt install -y libcapstone-dev less git 7 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ===================== 2 | QEMU plugins tutorial 3 | ===================== 4 | 5 | Environment 6 | =========== 7 | 8 | Setup 9 | ----- 10 | 11 | Without docker 12 | ++++++++++++++ 13 | 14 | - Install dependencies on your machine 15 | 16 | .. code:: shell 17 | 18 | apt update && 19 | apt build-dep -y qemu && 20 | apt install -y libcapstone-dev git 21 | 22 | With docker 23 | +++++++++++ 24 | 25 | - Install docker: https://docs.docker.com/engine/installation/ 26 | 27 | - Get our image or build it from Dockerfile (see commands below) 28 | 29 | - If you work with docker: 30 | 31 | - build and run from inside container (work directory will be mounted 32 | inside your container). 33 | - edit files from outside in your host rootfs (with your favorite editor). 34 | - More simply: keep one terminal inside your container and another to edit 35 | your files. 36 | 37 | - Docker commands to load/run existing image 38 | 39 | .. code:: shell 40 | 41 | # to load docker image from archive 42 | docker load < docker_tutorial_qemu.tar.xz 43 | # to enter docker container, use: 44 | docker run -it --rm=true -u $UID -v $PWD:$PWD -w $PWD tutorial:qemu bash 45 | 46 | - Docker commands to build image 47 | 48 | .. code:: shell 49 | 50 | # clone this repo 51 | git clone https://github.com/atos-tools/qemu-plugins-tutorial 52 | cd qemu-plugins-tutorial 53 | # build your image from Dockerfile 54 | docker build -t tutorial:qemu - < Dockerfile 55 | # if you want to export it, save image as a compressed file 56 | docker save tutorial:qemu | xz > docker_tutorial_qemu.tar.xz 57 | 58 | Build 59 | ----- 60 | 61 | Get sources 62 | 63 | .. code:: shell 64 | 65 | # from your host 66 | # get sources from git 67 | git clone https://github.com/atos-tools/qemu --branch next/master --depth 1 68 | # or from our file 69 | # tar xvf qemu_src.tar.xz 70 | cd qemu 71 | 72 | # now switch to docker container 73 | docker run -it --rm=true -u $UID -v $PWD:$PWD -w $PWD tutorial:qemu bash 74 | 75 | # configure project 76 | ./configure --enable-capstone --enable-tcg-plugin --target-list=x86_64-linux-user 77 | # build 78 | make -j4 79 | # test cache plugin with ls 80 | ./x86_64-linux-user/qemu-x86_64 --tcg-plugin dineroIV /bin/ls 81 | 82 | Run 83 | --- 84 | 85 | .. code:: shell 86 | 87 | ./x86_64-linux-user/qemu-x86_64 --tcg-plugin plugin_name /path/to/bin 88 | 89 | QEMU code 90 | ========= 91 | 92 | Plugins 93 | ------- 94 | 95 | QEMU plugins sources are located in ``tcg/plugins`` 96 | 97 | The QEMU plugins API 98 | -------------------- 99 | 100 | A detailed introduction is available in ``tcg/plugins/README`` 101 | 102 | .. code:: C 103 | 104 | /* called after generation of each QEMU opcode */ 105 | void after_gen_opc(const TCGPluginInterface *tpi, 106 | const TPIOpCode *opcode); 107 | 108 | /* function called at the beginning of each translation block */ 109 | void pre_tb_helper_code_t(const TCGPluginInterface *tpi, 110 | TPIHelperInfo info, 111 | uint64_t address, 112 | uint64_t data1, 113 | uint64_t data2, 114 | const TranslationBlock* tb); 115 | 116 | /* set data passed to previous helper */ 117 | void pre_tb_helper_data(const TCGPluginInterface *tpi, 118 | TPIHelperInfo info, uint64_t address, 119 | uint64_t *data1, uint64_t *data2, 120 | const TranslationBlock* tb); 121 | 122 | /* called at exit */ 123 | void cpus_stopped(const TCGPluginInterface *tpi); 124 | 125 | /* called to initialize plugin */ 126 | void tpi_init(TCGPluginInterface *tpi) 127 | 128 | Tutorial 129 | ======== 130 | 131 | Objective 132 | --------- 133 | 134 | For this tutorial, you will build a coverage plugin: 135 | 136 | :: 137 | 138 | // symbol __pthread_cleanup_pop_restore 139 | 6 | 0x40016acb30: mov rax, qword ptr [rdi + 0x18] 140 | 6 | 0x40016acb34: mov qword ptr fs:[0x2f8], rax 141 | 6 | 0x40016acb3d: mov eax, dword ptr [rdi + 0x10] 142 | 6 | 0x40016acb40: test eax, eax 143 | 6 | 0x40016acb42: jne 0x40016acb60 144 | 6 | 0x40016acb44: test esi, esi 145 | 6 | 0x40016acb46: jne 0x40016acb50 146 | 6 | 0x40016acb48: ret 147 | 0 | 0x40016acb4a: nop word ptr [rax + rax] 148 | 0 | 0x40016acb50: mov rdx, qword ptr [rdi + 8] 149 | 150 | For each known function (where symbol name is available), we print its assembly 151 | listing with hit count for each instruction. 152 | 153 | Coverage plugin (existing) is in ``tcg/plugins/coverage.c``. 154 | 155 | To disassemble code, we use capstone library. For structures/containers, we use 156 | glib. 157 | 158 | Plugin structure 159 | ---------------- 160 | 161 | For each instruction translated, we keep a mapping between its address and the 162 | number of time it was hit. 163 | 164 | When instruction is translated, we insert a call to a function that increments 165 | the hit counter. 166 | 167 | Finally, for each function we hit (whose symbol was found), we disassemble it 168 | and print the hit count. 169 | 170 | Code 171 | ---- 172 | 173 | You can create a new plugin named coverage-tiny by reading and copy/pasting 174 | following code. Existing coverage plugin is a bit more complex, but you can look 175 | at the code as well. 176 | 177 | First, edit file ``Makefile.target``, search for 'tcg-plugin-coverage:', add: 178 | 179 | .. code:: Makefile 180 | 181 | tcg-plugin-coverage-tiny.o: CFLAGS += $(CAPSTONE_CFLAGS) 182 | tcg-plugin-coverage-tiny.so: LIBS += $(CAPSTONE_LDFLAGS) 183 | 184 | Then you can edit ``tcg/plugin/coverage-tiny.c`` with following code. 185 | 186 | //We start with classic includes 187 | 188 | .. code:: C 189 | 190 | #include 191 | 192 | /* tcg plugin include */ 193 | #include "tcg-plugin.h" 194 | #include "disas/disas.h" /* lookup symbol */ 195 | 196 | /* disassemble library */ 197 | #include 198 | 199 | //Then some global variables and structures 200 | 201 | .. code:: C 202 | 203 | static GHashTable *symbol_table; /* symbol_name -> address/size */ 204 | 205 | static GHashTable *address_table; /* instr address -> count */ 206 | 207 | struct symbol_table_entry 208 | { 209 | uint64_t symbol_address; 210 | uint64_t symbol_size; 211 | }; 212 | 213 | struct address_table_entry 214 | { 215 | uint64_t count; 216 | }; 217 | 218 | //Our callbacks declaration. 219 | 220 | .. code:: C 221 | 222 | /* to be called when instruction is hit */ 223 | static void hit_instruction(int64_t count_ptr); 224 | 225 | /* callback for each opcode generated */ 226 | static void after_gen_opc(const TCGPluginInterface *tpi, 227 | const TPIOpCode *tpi_opcode); 228 | 229 | /* callback to report the coverage at exit */ 230 | static void cpus_stopped(const TCGPluginInterface *tpi); 231 | 232 | //Plugin initialize function. 233 | 234 | .. code:: C 235 | 236 | void tpi_init(TCGPluginInterface *tpi) 237 | { 238 | /* initialize tpi struct */ 239 | TPI_INIT_VERSION_GENERIC(tpi); 240 | /* declare a helper to be able to insert call to hit_instruction 241 | function directly inside a TranslationBlock */ 242 | TPI_DECL_FUNC_1(tpi, hit_instruction, void, i64); 243 | 244 | /* register callbacks */ 245 | tpi->after_gen_opc = after_gen_opc; 246 | tpi->cpus_stopped = cpus_stopped; 247 | 248 | /* initialize global tables */ 249 | symbol_table = g_hash_table_new_full( 250 | g_str_hash, g_str_equal, g_free, g_free); 251 | address_table = g_hash_table_new_full( 252 | g_direct_hash, g_direct_equal, NULL, g_free); 253 | } 254 | 255 | //Function to execute when instruction is hit 256 | 257 | .. code:: C 258 | 259 | static void hit_instruction(int64_t count_ptr) 260 | { 261 | (*(uint64_t*)count_ptr)++; 262 | } 263 | 264 | //Callback when a new opcode is generated 265 | 266 | .. code:: C 267 | 268 | static void after_gen_opc(const TCGPluginInterface *tpi, 269 | const TPIOpCode *tpi_opcode) 270 | { 271 | const char *symbol = NULL; 272 | const char *filename = NULL; 273 | uint64_t symbol_address = 0; 274 | uint64_t symbol_size = 0; 275 | 276 | /* execute this callback only per translated instruction (that may 277 | result in several opcodes). We detect a special value for it. */ 278 | if (tpi_opcode->operator != INDEX_op_insn_start) 279 | return; 280 | 281 | /* check if instruction address pc is inside a known symbol. 282 | If not, return */ 283 | if (!lookup_symbol4( 284 | tpi_opcode->pc, &symbol, &filename, &symbol_address, &symbol_size) 285 | || symbol[0] == '\0') 286 | return; 287 | 288 | /* check if we met this symbol already */ 289 | struct symbol_table_entry *symbol_table_entry = 290 | g_hash_table_lookup(symbol_table, symbol); 291 | if (symbol_table_entry == NULL) { 292 | /* create an entry for this symbol */ 293 | symbol_table_entry = g_new(struct symbol_table_entry, 1); 294 | symbol_table_entry->symbol_address = symbol_address; 295 | symbol_table_entry->symbol_size = symbol_size; 296 | g_hash_table_insert(symbol_table, g_strdup(symbol), symbol_table_entry); 297 | } 298 | 299 | /* same for hit count */ 300 | uint64_t *address_table_key = (uint64_t *)tpi_opcode->pc; 301 | struct address_table_entry *address_table_entry = g_hash_table_lookup( 302 | address_table, address_table_key); 303 | if (address_table_entry == NULL) { 304 | address_table_entry = g_new(struct address_table_entry, 1); 305 | /* initialize count to 0 */ 306 | address_table_entry->count = 0; 307 | g_hash_table_insert( 308 | address_table, address_table_key, address_table_entry); 309 | } 310 | 311 | /* generate call to hit instruction directly with pointer on count */ 312 | TCGArg args[] = { 313 | GET_TCGV_I64(tcg_const_i64((uint64_t)&address_table_entry->count)) }; 314 | tcg_gen_callN(tpi->tcg_ctx, hit_instruction, TCG_CALL_DUMMY_ARG, 1, args); 315 | } 316 | 317 | //Now display the final result 318 | 319 | .. code:: C 320 | 321 | /* iterate on symbols met and print result */ 322 | static void output_symbol_coverage(gpointer key, 323 | gpointer value, 324 | gpointer user_data); 325 | 326 | /* called at program exit */ 327 | static void cpus_stopped(const TCGPluginInterface *tpi) 328 | { 329 | csh cs_handle; 330 | 331 | /* initialize capstone */ 332 | if (cs_open(CS_ARCH_X86, CS_MODE_64, &cs_handle) != CS_ERR_OK) 333 | abort(); 334 | cs_option(cs_handle, CS_OPT_DETAIL, CS_OPT_ON); 335 | 336 | void* data[] = {(void*)tpi, (void*)cs_handle}; 337 | /* output coverage for each symbol */ 338 | g_hash_table_foreach(symbol_table, output_symbol_coverage, data); 339 | 340 | // clean everything 341 | g_hash_table_destroy(symbol_table); 342 | g_hash_table_destroy(address_table); 343 | 344 | cs_close(&cs_handle); 345 | } 346 | 347 | static void output_symbol_coverage(gpointer key, 348 | gpointer value, 349 | gpointer user_data) 350 | { 351 | void** data = (void**)user_data; 352 | const TCGPluginInterface *tpi = (const TCGPluginInterface *)data[0]; 353 | csh cs_handle = (csh)data[1]; 354 | const char *symbol = (const char *)key; 355 | struct symbol_table_entry *symbol_entry = value; 356 | FILE *output = tpi->output; 357 | 358 | /* retrieve pointer on original code to disassemble it */ 359 | const uint8_t *code = (const uint8_t *)(intptr_t)tpi_guest_ptr( 360 | tpi, symbol_entry->symbol_address); 361 | size_t size = symbol_entry->symbol_size; 362 | uint64_t address = symbol_entry->symbol_address; 363 | 364 | fprintf(output, "// symbol %s\n", symbol); 365 | 366 | /* alloc instruction */ 367 | cs_insn *insn = cs_malloc(cs_handle); 368 | /* start disassemble */ 369 | while (cs_disasm_iter(cs_handle, &code, &size, &address, insn)) { 370 | /* retrieve hit count for this instruction */ 371 | struct address_table_entry *value = 372 | g_hash_table_lookup(address_table, (uint64_t *)insn->address); 373 | uint64_t count = value ? value->count : 0; 374 | /* output instruction and count */ 375 | fprintf(output, "%8" PRIu64 " | 0x%"PRIx64":\t %s\t %s\n", 376 | count, 377 | insn->address, 378 | insn->mnemonic, 379 | insn->op_str 380 | ); 381 | } 382 | cs_free(insn, 1); 383 | } 384 | 385 | Try it 386 | ------ 387 | 388 | Why not use your plugin directly to cover qemu itself? 389 | 390 | .. code:: shell 391 | 392 | ./x86_64-linux-user/qemu-x86_64 --tcg-plugin coverage-tiny ./x86_64-linux-user/qemu-x86_64 /bin/false 393 | # you can try coverage plugin as well 394 | ./x86_64-linux-user/qemu-x86_64 --tcg-plugin coverage ./x86_64-linux-user/qemu-x86_64 /bin/false |& less -R 395 | 396 | To go further 397 | ------------- 398 | 399 | - Write small code example to see coverage for it. 400 | - Try to map assembly to source code. 401 | - You can read code of other plugins, or write a new one from scratch! 402 | 403 | References 404 | ---------- 405 | 406 | - glib data types: https://developer.gnome.org/glib/stable/glib-data-types.html 407 | - capstone disassembler tools doc: http://www.capstone-engine.org/iteration.html 408 | - qemu-plugins repository (branch next/master): https://github.com/atos-tools/qemu 409 | 410 | Slides 411 | ------ 412 | 413 | You need to have ``pandoc`` installed to build slides. 414 | 415 | .. code:: shell 416 | 417 | # need pandoc installed 418 | # apt install pandoc 419 | make -C slides 420 | # see slides with browser 421 | firefox slides/build/qemu_plugins.html 422 | -------------------------------------------------------------------------------- /slides/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /slides/Makefile: -------------------------------------------------------------------------------- 1 | SRC = $(wildcard *.rst) 2 | PRES = $(SRC:%.rst=build/%.html) 3 | 4 | all: $(PRES) 5 | 6 | build/reveal.js: 7 | rm -rf build/reveal.js 8 | mkdir -p build/ 9 | git clone --depth=1 https://github.com/hakimel/reveal.js/ build/reveal.js 10 | 11 | build/%.html: %.rst build/reveal.js 12 | mkdir -p build/ 13 | pandoc $< \ 14 | -o $@ \ 15 | -t revealjs \ 16 | -V theme:league \ 17 | --self-contained -V revealjs-url:build/reveal.js 18 | sed -e "s/Reveal.initialize({/Reveal.initialize({\nslideNumber: 'c\/t',/" \ 19 | -i $@ 20 | 21 | clean: 22 | rm -rf build 23 | -------------------------------------------------------------------------------- /slides/qemu_plugins.rst: -------------------------------------------------------------------------------- 1 | Instrument programs with QEMU 2 | ############################# 3 | 4 | Pierrick Bouvier - STMicroelectronics/INRIA-CORSE 5 | 6 | Antoine Moynault - STMicroelectronics 7 | 8 | Lyon, 21 June 2017 9 | 10 | ------------------------------------- 11 | 12 | QEMU 13 | #### 14 | 15 | - A Fast and Portable Dynamic Translator 16 | - Created by Fabrice Bellard (tcc, ffmpeg, pi computation, ...) 17 | - Plugins (Credits @C.Vincent (ST), @C.Guillon (ST)) API to instrument code 18 | 19 | ------------------------------------- 20 | 21 | QEMU 22 | #### 23 | 24 | - Translates code from guest code (binary or system) to host code (cpu 25 | running QEMU) 26 | - Uses an IR 27 | - Provides system mode (full VM) or user mode (binary only) 28 | 29 | ------------------------------------- 30 | 31 | QEMU Translation 32 | ################ 33 | 34 | - From guest code (binary arch) to IR 35 | - Then, from IR to host code (cpu executing) 36 | - QEMU performs register allocation, liveness analysis for each block translated 37 | 38 | ------------------------------------- 39 | 40 | QEMU Translation 41 | ################ 42 | 43 | - Translate block of code each time it is met for the first time 44 | - A block code starts/ends when a jump/branch instruction is met 45 | - Several blocks can be chained once their are translated 46 | - When jumping in the middle of existing block, a new one is translated (no 47 | reuse) 48 | 49 | ------------------------------------- 50 | 51 | Plugins (existing) 52 | ################## 53 | 54 | API allowing to execute code when: 55 | 56 | - a new block is translated 57 | - after block is translated 58 | - an IR opcode is emitted 59 | - an instruction is decoded 60 | - various services (insert call to helper, modify generated code, ...) 61 | 62 | ------------------------------------- 63 | 64 | Plugins API 65 | ########### 66 | 67 | .. code:: C 68 | 69 | void after_gen_opc(const TCGPluginInterface *tpi, 70 | const TPIOpCode *opcode); 71 | 72 | void pre_tb_helper_code_t(const TCGPluginInterface *tpi, 73 | TPIHelperInfo info, 74 | uint64_t address, 75 | uint64_t data1, 76 | uint64_t data2, 77 | const TranslationBlock* tb); 78 | 79 | void cpus_stopped(const TCGPluginInterface *tpi); 80 | 81 | void tpi_init(TCGPluginInterface *tpi) 82 | 83 | ------------------------------------- 84 | 85 | Plugins API (param) 86 | ################### 87 | 88 | - receive special query from gdb (through maintenance packet) 89 | - declare/read/write parameters in plugins 90 | - activate/deactive plugin during execution 91 | 92 | .. code:: C 93 | 94 | void tpi_init(TCGPluginInterface *tpi) 95 | { 96 | ... 97 | tpi_declare_param_int(tpi, "param_int", 98 | ¶m_int, -42, 99 | "This is an int param"); 100 | tpi_declare_param_string(tpi, "param_string", 101 | ¶m_string, "Welcome!", 102 | "what a nice string"); 103 | ... 104 | } 105 | 106 | ------------------------------------- 107 | 108 | Plugins (available) 109 | ################### 110 | 111 | - **dineroIV**: cache simulator 112 | - **dyncount**: count instructions per type 113 | - **dyntrace**: disassemble on the fly 114 | - **ftrace**: trace entry/exit of functions 115 | - **icount**: count instructions (with helper) 116 | - **profile**: count instructions per function 117 | - **trace**: print address of each TB 118 | 119 | ------------------------------------- 120 | 121 | Tutorial 122 | ######## 123 | 124 | Let's build a plugin! 125 | 126 | Please follow tutorial at: https://github.com/atos-tools/qemu-plugins-tutorial 127 | --------------------------------------------------------------------------------