├── .gitignore ├── README.md ├── build.cmd ├── build.sh ├── create-eclipse-project.cmd ├── create-eclipse-project.sh ├── data ├── mars_analytica_stack_machine_trace.log ├── solve.py └── symbolic_exec_stack_machine.py ├── out └── CEmulatorPlugin-1.0.0.jar ├── scripts ├── build.xml └── generateEclipseProjectFilesForPlugin.py └── src └── com └── pnf └── plugin └── cemulator ├── CEmulatorPlugin.java ├── CFG.java ├── EmulatorException.java ├── EmulatorLog.java ├── EmulatorState.java ├── HeadlessClient.java ├── MarsAnalyticaCEmulator.java └── SimpleCEmulator.java /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | bin/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JEB C Emulator 2 | 3 | Plugin to emulate JEB's decompiled C code: 4 | 5 | - It was originally built to analyze an heavily obfuscated crackme executable dubbed MarsAnalytica (see [companion blog](https://www.pnfsoftware.com/blog/traveling-around-mars-with-c-emulation/)) 6 | 7 | - Emulator has some strong limitations and should serve primarily as an example of what is doable with JEB decompiled C code (see SimpleCEmulator.java for known limitations) 8 | 9 | - Emulator can be extended by adding specific logic in a class inheriting from SimpleCEmulator (see for example MarsAnalyticaCEmulator.java) 10 | 11 | - /data repository contains an extract of MArsAnalytica's stack machine trace, and python scripts to replay it with symbols rather than concrete input, and to solve it using Z3 12 | 13 | ## Running it 14 | 15 | ### JEB's UI 16 | 17 | - You need JEB version 4.0 or above 18 | - Copy emulator Jar from the out/ folder to your JEB's coreplugins/ folder 19 | - In JEB UI, File > Plugins > Execute an Engines Plugin > CEmulator 20 | 21 | ### CLI 22 | 23 | - The plugin comes with a headless client, made to gather long emulator runs, with the ability to provide heap/stack memory dumps as starting point; see HeadlessClient.java for possible arguments 24 | 25 | - To run headless client: java -cp CEmulatorPlugin-1.0.0.jar;[JEB INSTALL FOLDER]\bin\app\jeb.jar;. com.pnf.plugin.cemulator.HeadlessClient [ARGUMENTS] 26 | 27 | ## References 28 | 29 | - [Traveling Around Mars With C Emulation](https://www.pnfsoftware.com/blog/traveling-around-mars-with-c-emulation/) 30 | 31 | - [API Documentation](https://www.pnfsoftware.com/jeb/apidoc/reference/packages.html) 32 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | ant -f scripts\build.xml -DpluginClassname=com.pnf.plugin.cemulator.CEmulatorPlugin -DpluginFilename=CEmulatorPlugin -DpluginVersion=1.0.0 -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ant -f scripts/build.xml -DpluginClassname=SamplePlugin -DpluginFilename=SamplePlugin -DpluginVersion=1.0.0 -------------------------------------------------------------------------------- /create-eclipse-project.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | python scripts\generateEclipseProjectFilesForPlugin.py %1 3 | -------------------------------------------------------------------------------- /create-eclipse-project.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | python scripts/generateEclipseProjectFilesForPlugin.py $1 -------------------------------------------------------------------------------- /data/mars_analytica_stack_machine_trace.log: -------------------------------------------------------------------------------- 1 | ; MarsAnalytica stack machine trace 2 | ; inital series of SET operators with concrete values are commented, 3 | ; as they are replaced by symbols in symbolic executor 4 | ; S: SET index:7 value:97 5 | ; S: SET index:8 value:98 6 | ; S: SET index:13 value:99 7 | ; S: SET index:15 value:100 8 | ; S: SET index:16 value:101 9 | ; S: SET index:26 value:102 10 | ; S: SET index:27 value:103 11 | ; S: SET index:22 value:104 12 | ; S: SET index:21 value:105 13 | ; S: SET index:4 value:106 14 | ; S: SET index:18 value:107 15 | ; S: SET index:28 value:108 16 | ; S: SET index:23 value:109 17 | ; S: SET index:29 value:110 18 | ; S: SET index:9 value:111 19 | ; S: SET index:1 value:112 20 | ; S: SET index:25 value:113 21 | ; S: SET index:30 value:114 22 | ; S: SET index:17 value:115 23 | S: PUSH 0 24 | S: PUSH 0 25 | S: POP (0) 26 | S: SET index:31 value:0 27 | S: PUSH 1 28 | S: POP (1) 29 | S: SET index:31 value:1 30 | S: PUSH 0 31 | S: POP (0) 32 | S: SET index:31 value:0 33 | S: PUSH 1 34 | S: POP (1) 35 | S: SET index:31 value:1 36 | S: PUSH 0 37 | S: PUSH 2700 38 | S: POP (2700) 39 | S: SET index:32 value:2700 40 | S: GET index:32 41 | S: PUSH 2700 42 | S: PUSH 2 43 | S: POP (2) 44 | S: POP (2700) 45 | S: PUSH 2702 46 | | operation: (+,#op=2) 47 | S: POP (2702) 48 | S: SET index:32 value:2702 49 | S: PUSH 0 50 | S: POP (0) 51 | S: SET index:31 value:0 52 | S: PUSH 1 53 | S: POP (1) 54 | S: SET index:31 value:1 55 | S: PUSH 0 56 | S: POP (0) 57 | S: SET index:31 value:0 58 | S: PUSH 1 59 | S: POP (1) 60 | S: SET index:31 value:1 61 | S: PUSH 0 62 | S: PUSH 2700 63 | S: POP (2700) 64 | S: SET index:33 value:2700 65 | S: GET index:33 66 | S: PUSH 2700 67 | S: PUSH 2 68 | S: POP (2) 69 | S: POP (2700) 70 | S: PUSH 2702 71 | | operation: (+,#op=2) 72 | S: POP (2702) 73 | S: SET index:33 value:2702 74 | S: PUSH 0 75 | S: POP (0) 76 | S: SET index:31 value:0 77 | S: PUSH 1 78 | S: POP (1) 79 | S: SET index:31 value:1 80 | S: PUSH 0 81 | S: POP (0) 82 | S: SET index:31 value:0 83 | S: PUSH 1 84 | S: POP (1) 85 | S: SET index:31 value:1 86 | S: PUSH 0 87 | S: POP (0) 88 | S: SET index:31 value:0 89 | S: PUSH 1 90 | S: POP (1) 91 | S: SET index:31 value:1 92 | S: GET index:33 93 | S: PUSH 2702 94 | S: GET index:32 95 | S: PUSH 2702 96 | S: POP (2702) 97 | S: POP (2702) 98 | S: TEST (==,#op=2) 99 | S: PUSH 0 100 | S: POP (0) 101 | S: TEST (!,#op=1) 102 | S: PUSH 0 103 | S: POP (0) 104 | S: SET index:31 value:0 105 | S: PUSH 1 106 | S: POP (1) 107 | S: SET index:31 value:1 108 | S: PUSH 0 109 | S: POP (0) 110 | S: SET index:31 value:0 111 | S: PUSH 32 112 | S: GET index:7 113 | S: PUSH 97 114 | S: POP (97) 115 | S: POP (32) 116 | S: TEST (>,#op=2) 117 | S: PUSH 1 118 | S: GET index:7 119 | S: PUSH 97 120 | S: PUSH 127 121 | S: POP (127) 122 | S: POP (97) 123 | S: TEST (>,#op=2) 124 | S: PUSH 1 125 | S: PUSH 32 126 | S: GET index:8 127 | S: PUSH 98 128 | S: POP (98) 129 | S: POP (32) 130 | S: TEST (>,#op=2) 131 | S: PUSH 1 132 | S: GET index:8 133 | S: PUSH 98 134 | S: PUSH 127 135 | S: POP (127) 136 | S: POP (98) 137 | S: TEST (>,#op=2) 138 | S: PUSH 1 139 | S: PUSH 32 140 | S: GET index:13 141 | S: PUSH 99 142 | S: POP (99) 143 | S: POP (32) 144 | S: TEST (>,#op=2) 145 | S: PUSH 1 146 | S: GET index:13 147 | S: PUSH 99 148 | S: PUSH 127 149 | S: POP (127) 150 | S: POP (99) 151 | S: TEST (>,#op=2) 152 | S: PUSH 1 153 | S: PUSH 32 154 | S: GET index:15 155 | S: PUSH 100 156 | S: POP (100) 157 | S: POP (32) 158 | S: TEST (>,#op=2) 159 | S: PUSH 1 160 | S: GET index:15 161 | S: PUSH 100 162 | S: PUSH 127 163 | S: POP (127) 164 | S: POP (100) 165 | S: TEST (>,#op=2) 166 | S: PUSH 1 167 | S: PUSH 32 168 | S: GET index:16 169 | S: PUSH 101 170 | S: POP (101) 171 | S: POP (32) 172 | S: TEST (>,#op=2) 173 | S: PUSH 1 174 | S: GET index:16 175 | S: PUSH 101 176 | S: PUSH 127 177 | S: POP (127) 178 | S: POP (101) 179 | S: TEST (>,#op=2) 180 | S: PUSH 1 181 | S: PUSH 32 182 | S: GET index:26 183 | S: PUSH 102 184 | S: POP (102) 185 | S: POP (32) 186 | S: TEST (>,#op=2) 187 | S: PUSH 1 188 | S: GET index:26 189 | S: PUSH 102 190 | S: PUSH 127 191 | S: POP (127) 192 | S: POP (102) 193 | S: TEST (>,#op=2) 194 | S: PUSH 1 195 | S: PUSH 32 196 | S: GET index:27 197 | S: PUSH 103 198 | S: POP (103) 199 | S: POP (32) 200 | S: TEST (>,#op=2) 201 | S: PUSH 1 202 | S: GET index:27 203 | S: PUSH 103 204 | S: PUSH 127 205 | S: POP (127) 206 | S: POP (103) 207 | S: TEST (>,#op=2) 208 | S: PUSH 1 209 | S: PUSH 32 210 | S: GET index:22 211 | S: PUSH 104 212 | S: POP (104) 213 | S: POP (32) 214 | S: TEST (>,#op=2) 215 | S: PUSH 1 216 | S: GET index:22 217 | S: PUSH 104 218 | S: PUSH 127 219 | S: POP (127) 220 | S: POP (104) 221 | S: TEST (>,#op=2) 222 | S: PUSH 1 223 | S: PUSH 32 224 | S: GET index:21 225 | S: PUSH 105 226 | S: POP (105) 227 | S: POP (32) 228 | S: TEST (>,#op=2) 229 | S: PUSH 1 230 | S: GET index:21 231 | S: PUSH 105 232 | S: PUSH 127 233 | S: POP (127) 234 | S: POP (105) 235 | S: TEST (>,#op=2) 236 | S: PUSH 1 237 | S: PUSH 32 238 | S: GET index:4 239 | S: PUSH 106 240 | S: POP (106) 241 | S: POP (32) 242 | S: TEST (>,#op=2) 243 | S: PUSH 1 244 | S: GET index:4 245 | S: PUSH 106 246 | S: PUSH 127 247 | S: POP (127) 248 | S: POP (106) 249 | S: TEST (>,#op=2) 250 | S: PUSH 1 251 | S: PUSH 32 252 | S: GET index:18 253 | S: PUSH 107 254 | S: POP (107) 255 | S: POP (32) 256 | S: TEST (>,#op=2) 257 | S: PUSH 1 258 | S: GET index:18 259 | S: PUSH 107 260 | S: PUSH 127 261 | S: POP (127) 262 | S: POP (107) 263 | S: TEST (>,#op=2) 264 | S: PUSH 1 265 | S: PUSH 32 266 | S: GET index:28 267 | S: PUSH 108 268 | S: POP (108) 269 | S: POP (32) 270 | S: TEST (>,#op=2) 271 | S: PUSH 1 272 | S: GET index:28 273 | S: PUSH 108 274 | S: PUSH 127 275 | S: POP (127) 276 | S: POP (108) 277 | S: TEST (>,#op=2) 278 | S: PUSH 1 279 | S: PUSH 32 280 | S: GET index:23 281 | S: PUSH 109 282 | S: POP (109) 283 | S: POP (32) 284 | S: TEST (>,#op=2) 285 | S: PUSH 1 286 | S: GET index:23 287 | S: PUSH 109 288 | S: PUSH 127 289 | S: POP (127) 290 | S: POP (109) 291 | S: TEST (>,#op=2) 292 | S: PUSH 1 293 | S: PUSH 32 294 | S: GET index:29 295 | S: PUSH 110 296 | S: POP (110) 297 | S: POP (32) 298 | S: TEST (>,#op=2) 299 | S: PUSH 1 300 | S: GET index:29 301 | S: PUSH 110 302 | S: PUSH 127 303 | S: POP (127) 304 | S: POP (110) 305 | S: TEST (>,#op=2) 306 | S: PUSH 1 307 | S: PUSH 32 308 | S: GET index:9 309 | S: PUSH 111 310 | S: POP (111) 311 | S: POP (32) 312 | S: TEST (>,#op=2) 313 | S: PUSH 1 314 | S: GET index:9 315 | S: PUSH 111 316 | S: PUSH 127 317 | S: POP (127) 318 | S: POP (111) 319 | S: TEST (>,#op=2) 320 | S: PUSH 1 321 | S: PUSH 32 322 | S: GET index:1 323 | S: PUSH 112 324 | S: POP (112) 325 | S: POP (32) 326 | S: TEST (>,#op=2) 327 | S: PUSH 1 328 | S: GET index:1 329 | S: PUSH 112 330 | S: PUSH 127 331 | S: POP (127) 332 | S: POP (112) 333 | S: TEST (>,#op=2) 334 | S: PUSH 1 335 | S: PUSH 32 336 | S: GET index:25 337 | S: PUSH 113 338 | S: POP (113) 339 | S: POP (32) 340 | S: TEST (>,#op=2) 341 | S: PUSH 1 342 | S: GET index:25 343 | S: PUSH 113 344 | S: PUSH 127 345 | S: POP (127) 346 | S: POP (113) 347 | S: TEST (>,#op=2) 348 | S: PUSH 1 349 | S: PUSH 32 350 | S: GET index:30 351 | S: PUSH 114 352 | S: POP (114) 353 | S: POP (32) 354 | S: TEST (>,#op=2) 355 | S: PUSH 1 356 | S: GET index:30 357 | S: PUSH 114 358 | S: PUSH 127 359 | S: POP (127) 360 | S: POP (114) 361 | S: TEST (>,#op=2) 362 | S: PUSH 1 363 | S: PUSH 32 364 | S: GET index:17 365 | S: PUSH 115 366 | S: POP (115) 367 | S: POP (32) 368 | S: TEST (>,#op=2) 369 | S: PUSH 1 370 | S: GET index:17 371 | S: PUSH 115 372 | S: PUSH 127 373 | S: POP (127) 374 | S: POP (115) 375 | S: TEST (>,#op=2) 376 | S: PUSH 1 377 | S: POP (1) 378 | S: TEST (==,cte=1) 379 | S: POP (1) 380 | S: TEST (==,cte=1) 381 | S: PUSH 1 382 | S: POP (1) 383 | S: TEST (==,cte=1) 384 | S: POP (1) 385 | S: TEST (==,cte=1) 386 | S: PUSH 1 387 | S: POP (1) 388 | S: TEST (==,cte=1) 389 | S: POP (1) 390 | S: TEST (==,cte=1) 391 | S: PUSH 1 392 | S: POP (1) 393 | S: TEST (==,cte=1) 394 | S: POP (1) 395 | S: TEST (==,cte=1) 396 | S: PUSH 1 397 | S: POP (1) 398 | S: TEST (==,cte=1) 399 | S: POP (1) 400 | S: TEST (==,cte=1) 401 | S: PUSH 1 402 | S: POP (1) 403 | S: TEST (==,cte=1) 404 | S: POP (1) 405 | S: TEST (==,cte=1) 406 | S: PUSH 1 407 | S: POP (1) 408 | S: TEST (==,cte=1) 409 | S: POP (1) 410 | S: TEST (==,cte=1) 411 | S: PUSH 1 412 | S: POP (1) 413 | S: TEST (==,cte=1) 414 | S: POP (1) 415 | S: TEST (==,cte=1) 416 | S: PUSH 1 417 | S: POP (1) 418 | S: TEST (==,cte=1) 419 | S: POP (1) 420 | S: TEST (==,cte=1) 421 | S: PUSH 1 422 | S: POP (1) 423 | S: TEST (==,cte=1) 424 | S: POP (1) 425 | S: TEST (==,cte=1) 426 | S: PUSH 1 427 | S: POP (1) 428 | S: TEST (==,cte=1) 429 | S: POP (1) 430 | S: TEST (==,cte=1) 431 | S: PUSH 1 432 | S: POP (1) 433 | S: TEST (==,cte=1) 434 | S: POP (1) 435 | S: TEST (==,cte=1) 436 | S: PUSH 1 437 | S: POP (1) 438 | S: TEST (==,cte=1) 439 | S: POP (1) 440 | S: TEST (==,cte=1) 441 | S: PUSH 1 442 | S: POP (1) 443 | S: TEST (==,cte=1) 444 | S: POP (1) 445 | S: TEST (==,cte=1) 446 | S: PUSH 1 447 | S: POP (1) 448 | S: TEST (==,cte=1) 449 | S: POP (1) 450 | S: TEST (==,cte=1) 451 | S: PUSH 1 452 | S: POP (1) 453 | S: TEST (==,cte=1) 454 | S: POP (1) 455 | S: TEST (==,cte=1) 456 | S: PUSH 1 457 | S: POP (1) 458 | S: TEST (==,cte=1) 459 | S: POP (1) 460 | S: TEST (==,cte=1) 461 | S: PUSH 1 462 | S: POP (1) 463 | S: TEST (==,cte=1) 464 | S: POP (1) 465 | S: TEST (==,cte=1) 466 | S: PUSH 1 467 | S: POP (1) 468 | S: TEST (==,cte=1) 469 | S: POP (1) 470 | S: TEST (==,cte=1) 471 | S: PUSH 1 472 | S: POP (1) 473 | S: TEST (==,cte=1) 474 | S: POP (1) 475 | S: TEST (==,cte=1) 476 | S: PUSH 1 477 | S: POP (1) 478 | S: TEST (==,cte=1) 479 | S: POP (1) 480 | S: TEST (==,cte=1) 481 | S: PUSH 1 482 | S: POP (1) 483 | S: TEST (==,cte=1) 484 | S: POP (1) 485 | S: TEST (==,cte=1) 486 | S: PUSH 1 487 | S: POP (1) 488 | S: TEST (==,cte=1) 489 | S: POP (1) 490 | S: TEST (==,cte=1) 491 | S: PUSH 1 492 | S: POP (1) 493 | S: TEST (==,cte=1) 494 | S: POP (1) 495 | S: TEST (==,cte=1) 496 | S: PUSH 1 497 | S: POP (1) 498 | S: TEST (==,cte=1) 499 | S: POP (1) 500 | S: TEST (==,cte=1) 501 | S: PUSH 1 502 | S: POP (1) 503 | S: TEST (==,cte=1) 504 | S: POP (1) 505 | S: TEST (==,cte=1) 506 | S: PUSH 1 507 | S: POP (1) 508 | S: TEST (==,cte=1) 509 | S: POP (1) 510 | S: TEST (==,cte=1) 511 | S: PUSH 1 512 | S: POP (1) 513 | S: TEST (==,cte=1) 514 | S: POP (1) 515 | S: TEST (==,cte=1) 516 | S: PUSH 1 517 | S: POP (1) 518 | S: TEST (==,cte=1) 519 | S: POP (1) 520 | S: TEST (==,cte=1) 521 | S: PUSH 1 522 | S: POP (1) 523 | S: TEST (==,cte=1) 524 | S: POP (1) 525 | S: TEST (==,cte=1) 526 | S: PUSH 1 527 | S: POP (1) 528 | S: TEST (==,cte=1) 529 | S: POP (1) 530 | S: TEST (==,cte=1) 531 | S: PUSH 1 532 | S: POP (1) 533 | S: TEST (==,cte=1) 534 | S: POP (1) 535 | S: TEST (==,cte=1) 536 | S: PUSH 1 537 | S: POP (1) 538 | S: TEST (==,cte=1) 539 | S: POP (1) 540 | S: TEST (==,cte=1) 541 | S: PUSH 1 542 | S: POP (1) 543 | S: TEST (==,cte=1) 544 | S: POP (1) 545 | S: TEST (==,cte=1) 546 | S: PUSH 1 547 | S: POP (1) 548 | S: TEST (==,cte=1) 549 | S: POP (1) 550 | S: TEST (==,cte=1) 551 | S: PUSH 1 552 | S: POP (1) 553 | S: TEST (==,cte=1) 554 | S: POP (1) 555 | S: TEST (==,cte=1) 556 | S: PUSH 1 557 | S: POP (1) 558 | S: TEST (==,cte=1) 559 | S: POP (1) 560 | S: TEST (==,cte=1) 561 | S: PUSH 1 562 | S: POP (1) 563 | S: TEST (,#op=1) 564 | S: PUSH 13 565 | S: PUSH 124 566 | S: POP (124) 567 | S: POP (13) 568 | S: PUSH 137 569 | | operation: (+,#op=2) 570 | S: PUSH 99 571 | S: SWAP 572 | S: POP (137) 573 | S: POP (99) 574 | S: PUSH 38 575 | | operation: (-,#op=2) 576 | S: PUSH 33 577 | S: SWAP 578 | S: POP (38) 579 | S: POP (33) 580 | S: PUSH 7 581 | | operation: (^,#op=2) 582 | S: PUSH 7 583 | S: POP (7) 584 | S: POP (7) 585 | S: TEST (==,#op=2) 586 | S: PUSH 1 587 | S: POP (1) 588 | S: TEST (,#op=1) 589 | S: PUSH 1 590 | S: POP (1) 591 | S: SET index:31 value:1 592 | S: GET index:9 593 | S: PUSH 111 594 | S: GET index:27 595 | S: PUSH 103 596 | S: POP (103) 597 | S: POP (111) 598 | S: PUSH 11433 599 | | operation: (*,#op=2) 600 | S: GET index:23 601 | S: PUSH 109 602 | S: GET index:18 603 | S: PUSH 107 604 | S: SWAP 605 | S: POP (109) 606 | S: POP (107) 607 | S: PUSH 2 608 | | operation: (-,#op=2) 609 | S: GET index:29 610 | S: PUSH 110 611 | S: SWAP 612 | S: POP (2) 613 | S: POP (110) 614 | S: PUSH 108 615 | | operation: (^,#op=2) 616 | S: POP (108) 617 | S: POP (11433) 618 | S: PUSH 1234764 619 | | operation: (*,#op=2) 620 | S: PUSH 16335 621 | S: POP (16335) 622 | S: POP (1234764) 623 | S: TEST (!=,#op=2) 624 | S: PUSH 0 625 | S: GET index:17 626 | S: PUSH 115 627 | S: GET index:8 628 | S: PUSH 98 629 | S: SWAP 630 | S: POP (115) 631 | S: POP (98) 632 | S: PUSH 17 633 | | operation: (^,#op=2) 634 | S: GET index:1 635 | S: PUSH 112 636 | S: GET index:22 637 | S: PUSH 104 638 | S: SWAP 639 | S: POP (112) 640 | S: POP (104) 641 | S: PUSH 8 642 | | operation: (-,#op=2) 643 | S: SWAP 644 | S: POP (17) 645 | S: POP (8) 646 | S: PUSH 25 647 | | operation: (^,#op=2) 648 | S: PUSH 83 649 | S: POP (83) 650 | S: POP (25) 651 | S: TEST (!=,#op=2) 652 | S: PUSH 0 653 | S: GET index:30 654 | S: PUSH 114 655 | S: GET index:25 656 | S: PUSH 113 657 | S: SWAP 658 | S: POP (114) 659 | S: POP (113) 660 | S: PUSH 1 661 | | operation: (-,#op=2) 662 | S: GET index:26 663 | S: PUSH 102 664 | S: GET index:4 665 | S: PUSH 106 666 | S: POP (106) 667 | S: POP (102) 668 | S: PUSH 208 669 | | operation: (+,#op=2) 670 | S: GET index:7 671 | S: PUSH 97 672 | S: SWAP 673 | S: POP (208) 674 | S: POP (97) 675 | S: PUSH 177 676 | | operation: (^,#op=2) 677 | S: POP (177) 678 | S: POP (1) 679 | S: PUSH 177 680 | | operation: (*,#op=2) 681 | S: PUSH 4294961394 682 | S: POP (4294961394) 683 | S: POP (177) 684 | S: TEST (!=,#op=2) 685 | S: PUSH 0 686 | S: GET index:15 687 | S: PUSH 100 688 | S: GET index:28 689 | S: PUSH 108 690 | S: SWAP 691 | S: POP (100) 692 | S: POP (108) 693 | S: PUSH -8 694 | | operation: (-,#op=2) 695 | S: PUSH 11 696 | S: POP (11) 697 | S: POP (4294967288) 698 | S: TEST (!=,#op=2) 699 | S: PUSH 0 700 | S: GET index:16 701 | S: PUSH 101 702 | S: GET index:13 703 | S: PUSH 99 704 | S: POP (99) 705 | S: POP (101) 706 | S: PUSH 200 707 | | operation: (+,#op=2) 708 | S: GET index:21 709 | S: PUSH 105 710 | S: SWAP 711 | S: POP (200) 712 | S: POP (105) 713 | S: PUSH 161 714 | | operation: (^,#op=2) 715 | S: PUSH 3 716 | S: POP (3) 717 | S: POP (161) 718 | S: TEST (!=,#op=2) 719 | S: PUSH 0 720 | S: GET index:1 721 | S: PUSH 112 722 | S: GET index:16 723 | S: PUSH 101 724 | S: SWAP 725 | S: POP (112) 726 | S: POP (101) 727 | S: PUSH 11 728 | | operation: (-,#op=2) 729 | S: GET index:21 730 | S: PUSH 105 731 | S: POP (105) 732 | S: POP (11) 733 | S: PUSH 116 734 | | operation: (+,#op=2) 735 | S: PUSH 176 736 | S: POP (176) 737 | S: POP (116) 738 | S: TEST (!=,#op=2) 739 | S: PUSH 0 740 | S: GET index:4 741 | S: PUSH 106 742 | S: GET index:18 743 | S: PUSH 107 744 | S: SWAP 745 | S: POP (106) 746 | S: POP (107) 747 | S: PUSH 1 748 | | operation: (^,#op=2) 749 | S: GET index:17 750 | S: PUSH 115 751 | S: GET index:28 752 | S: PUSH 108 753 | S: POP (108) 754 | S: POP (115) 755 | S: PUSH 223 756 | | operation: (+,#op=2) 757 | S: SWAP 758 | S: POP (1) 759 | S: POP (223) 760 | S: PUSH -222 761 | | operation: (-,#op=2) 762 | S: GET index:27 763 | S: PUSH 103 764 | S: SWAP 765 | S: POP (4294967074) 766 | S: POP (103) 767 | S: PUSH 4294967109 768 | | operation: (^,#op=2) 769 | S: PUSH 4294967097 770 | S: POP (4294967097) 771 | S: POP (4294967109) 772 | S: TEST (!=,#op=2) 773 | S: PUSH 0 774 | S: GET index:25 775 | S: PUSH 113 776 | S: GET index:13 777 | S: PUSH 99 778 | S: POP (99) 779 | S: POP (113) 780 | S: PUSH 11187 781 | | operation: (*,#op=2) 782 | S: GET index:7 783 | S: PUSH 97 784 | S: GET index:30 785 | S: PUSH 114 786 | S: SWAP 787 | S: POP (97) 788 | S: POP (114) 789 | S: PUSH 19 790 | | operation: (^,#op=2) 791 | S: GET index:8 792 | S: PUSH 98 793 | S: POP (98) 794 | S: POP (19) 795 | S: PUSH 1862 796 | | operation: (*,#op=2) 797 | S: POP (1862) 798 | S: POP (11187) 799 | S: PUSH 13049 800 | | operation: (+,#op=2) 801 | S: PUSH 9985 802 | S: POP (9985) 803 | S: POP (13049) 804 | S: TEST (!=,#op=2) 805 | S: PUSH 0 806 | S: GET index:29 807 | S: PUSH 110 808 | S: GET index:9 809 | S: PUSH 111 810 | S: POP (111) 811 | S: POP (110) 812 | S: PUSH 12210 813 | | operation: (*,#op=2) 814 | S: GET index:22 815 | S: PUSH 104 816 | S: SWAP 817 | S: POP (12210) 818 | S: POP (104) 819 | S: PUSH 12106 820 | | operation: (-,#op=2) 821 | S: PUSH 2083 822 | S: POP (2083) 823 | S: POP (12106) 824 | S: TEST (!=,#op=2) 825 | S: PUSH 0 826 | S: GET index:15 827 | S: PUSH 100 828 | S: GET index:23 829 | S: PUSH 109 830 | S: POP (109) 831 | S: POP (100) 832 | S: PUSH 209 833 | | operation: (+,#op=2) 834 | S: GET index:26 835 | S: PUSH 102 836 | S: SWAP 837 | S: POP (209) 838 | S: POP (102) 839 | S: PUSH 107 840 | | operation: (-,#op=2) 841 | S: PUSH 110 842 | S: POP (110) 843 | S: POP (107) 844 | S: TEST (!=,#op=2) 845 | S: PUSH 0 846 | S: GET index:29 847 | S: PUSH 110 848 | S: GET index:4 849 | S: PUSH 106 850 | S: POP (106) 851 | S: POP (110) 852 | S: PUSH 216 853 | | operation: (+,#op=2) 854 | S: GET index:18 855 | S: PUSH 107 856 | S: GET index:21 857 | S: PUSH 105 858 | S: POP (105) 859 | S: POP (107) 860 | S: PUSH 11235 861 | | operation: (*,#op=2) 862 | S: POP (11235) 863 | S: POP (216) 864 | S: PUSH 11451 865 | | operation: (+,#op=2) 866 | S: PUSH 5630 867 | S: POP (5630) 868 | S: POP (11451) 869 | S: TEST (!=,#op=2) 870 | S: PUSH 0 871 | S: GET index:26 872 | S: PUSH 102 873 | S: GET index:25 874 | S: PUSH 113 875 | S: SWAP 876 | S: POP (102) 877 | S: POP (113) 878 | S: PUSH -11 879 | | operation: (-,#op=2) 880 | S: GET index:7 881 | S: PUSH 97 882 | S: GET index:13 883 | S: PUSH 99 884 | S: POP (99) 885 | S: POP (97) 886 | S: PUSH 196 887 | | operation: (+,#op=2) 888 | S: SWAP 889 | S: POP (4294967285) 890 | S: POP (196) 891 | S: PUSH 4294967089 892 | | operation: (-,#op=2) 893 | S: PUSH 4294967114 894 | S: POP (4294967114) 895 | S: POP (4294967089) 896 | S: TEST (!=,#op=2) 897 | S: PUSH 0 898 | S: GET index:9 899 | S: PUSH 111 900 | S: GET index:22 901 | S: PUSH 104 902 | S: SWAP 903 | S: POP (111) 904 | S: POP (104) 905 | S: PUSH 7 906 | | operation: (^,#op=2) 907 | S: GET index:30 908 | S: PUSH 114 909 | S: POP (114) 910 | S: POP (7) 911 | S: PUSH 798 912 | | operation: (*,#op=2) 913 | S: PUSH 7200 914 | S: POP (7200) 915 | S: POP (798) 916 | S: TEST (!=,#op=2) 917 | S: PUSH 0 918 | S: GET index:28 919 | S: PUSH 108 920 | S: GET index:27 921 | S: PUSH 103 922 | S: POP (103) 923 | S: POP (108) 924 | S: PUSH 11124 925 | | operation: (*,#op=2) 926 | S: GET index:15 927 | S: PUSH 100 928 | S: GET index:8 929 | S: PUSH 98 930 | S: POP (98) 931 | S: POP (100) 932 | S: PUSH 9800 933 | | operation: (*,#op=2) 934 | S: POP (9800) 935 | S: POP (11124) 936 | S: PUSH 20924 937 | | operation: (+,#op=2) 938 | S: PUSH 17872 939 | S: POP (17872) 940 | S: POP (20924) 941 | S: TEST (!=,#op=2) 942 | S: PUSH 0 943 | S: GET index:23 944 | S: PUSH 109 945 | S: GET index:1 946 | S: PUSH 112 947 | S: SWAP 948 | S: POP (109) 949 | S: POP (112) 950 | S: PUSH -3 951 | | operation: (-,#op=2) 952 | S: GET index:16 953 | S: PUSH 101 954 | S: GET index:17 955 | S: PUSH 115 956 | S: POP (115) 957 | S: POP (101) 958 | S: PUSH 11615 959 | | operation: (*,#op=2) 960 | S: SWAP 961 | S: POP (4294967293) 962 | S: POP (11615) 963 | S: PUSH 4294955678 964 | | operation: (-,#op=2) 965 | S: PUSH 4294961888 966 | S: POP (4294961888) 967 | S: POP (4294955678) 968 | S: TEST (!=,#op=2) 969 | S: PUSH 0 970 | S: GET index:1 971 | S: PUSH 112 972 | S: GET index:15 973 | S: PUSH 100 974 | S: POP (100) 975 | S: POP (112) 976 | S: PUSH 11200 977 | | operation: (*,#op=2) 978 | S: GET index:13 979 | S: PUSH 99 980 | S: GET index:28 981 | S: PUSH 108 982 | S: POP (108) 983 | S: POP (99) 984 | S: PUSH 10692 985 | | operation: (*,#op=2) 986 | S: POP (10692) 987 | S: POP (11200) 988 | S: PUSH 21892 989 | | operation: (+,#op=2) 990 | S: PUSH 18888 991 | S: POP (18888) 992 | S: POP (21892) 993 | S: TEST (!=,#op=2) 994 | S: PUSH 0 995 | S: GET index:29 996 | S: PUSH 110 997 | S: GET index:26 998 | S: PUSH 102 999 | S: POP (102) 1000 | S: POP (110) 1001 | S: PUSH 212 1002 | | operation: (+,#op=2) 1003 | S: GET index:25 1004 | S: PUSH 113 1005 | S: POP (113) 1006 | S: POP (212) 1007 | S: PUSH 23956 1008 | | operation: (*,#op=2) 1009 | S: PUSH 15049 1010 | S: POP (15049) 1011 | S: POP (23956) 1012 | S: TEST (!=,#op=2) 1013 | S: PUSH 0 1014 | S: GET index:18 1015 | S: PUSH 107 1016 | S: GET index:7 1017 | S: PUSH 97 1018 | S: POP (97) 1019 | S: POP (107) 1020 | S: PUSH 204 1021 | | operation: (+,#op=2) 1022 | S: GET index:30 1023 | S: PUSH 114 1024 | S: POP (114) 1025 | S: POP (204) 1026 | S: PUSH 23256 1027 | | operation: (*,#op=2) 1028 | S: PUSH 12150 1029 | S: POP (12150) 1030 | S: POP (23256) 1031 | S: TEST (!=,#op=2) 1032 | S: PUSH 0 1033 | S: GET index:9 1034 | S: PUSH 111 1035 | S: GET index:27 1036 | S: PUSH 103 1037 | S: SWAP 1038 | S: POP (111) 1039 | S: POP (103) 1040 | S: PUSH 8 1041 | | operation: (^,#op=2) 1042 | S: GET index:17 1043 | S: PUSH 115 1044 | S: POP (115) 1045 | S: POP (8) 1046 | S: PUSH 920 1047 | | operation: (*,#op=2) 1048 | S: PUSH 10080 1049 | S: POP (10080) 1050 | S: POP (920) 1051 | S: TEST (!=,#op=2) 1052 | S: PUSH 0 1053 | S: GET index:23 1054 | S: PUSH 109 1055 | S: GET index:22 1056 | S: PUSH 104 1057 | S: POP (104) 1058 | S: POP (109) 1059 | S: PUSH 213 1060 | | operation: (+,#op=2) 1061 | S: GET index:16 1062 | S: PUSH 101 1063 | S: SWAP 1064 | S: POP (213) 1065 | S: POP (101) 1066 | S: PUSH 112 1067 | | operation: (-,#op=2) 1068 | S: PUSH 132 1069 | S: POP (132) 1070 | S: POP (112) 1071 | S: TEST (!=,#op=2) 1072 | S: PUSH 0 1073 | S: GET index:4 1074 | S: PUSH 106 1075 | S: GET index:8 1076 | S: PUSH 98 1077 | S: POP (98) 1078 | S: POP (106) 1079 | S: PUSH 10388 1080 | | operation: (*,#op=2) 1081 | S: GET index:21 1082 | S: PUSH 105 1083 | S: POP (105) 1084 | S: POP (10388) 1085 | S: PUSH 10493 1086 | | operation: (+,#op=2) 1087 | S: PUSH 2453 1088 | S: POP (2453) 1089 | S: POP (10493) 1090 | S: TEST (!=,#op=2) 1091 | S: PUSH 0 1092 | S: POP (0) 1093 | S: TEST (!=,cte=1) 1094 | S: PUSH 0 1095 | S: POP (0) 1096 | S: TEST (!=,cte=1) 1097 | S: PUSH 0 1098 | S: POP (0) 1099 | S: TEST (!=,cte=1) 1100 | S: PUSH 0 1101 | S: POP (0) 1102 | S: TEST (!=,cte=1) 1103 | S: PUSH 0 1104 | S: POP (0) 1105 | S: TEST (!=,cte=1) 1106 | S: PUSH 0 1107 | S: POP (0) 1108 | S: TEST (!=,cte=1) 1109 | S: PUSH 0 1110 | S: POP (0) 1111 | S: TEST (!=,cte=1) 1112 | S: PUSH 0 1113 | S: POP (0) 1114 | S: TEST (!=,cte=1) 1115 | S: PUSH 0 1116 | S: POP (0) 1117 | S: TEST (!=,cte=1) 1118 | S: PUSH 0 1119 | S: POP (0) 1120 | S: TEST (!=,cte=1) 1121 | S: PUSH 0 1122 | S: POP (0) 1123 | S: TEST (!=,cte=1) 1124 | S: PUSH 0 1125 | S: POP (0) 1126 | S: TEST (!=,cte=1) 1127 | S: PUSH 0 1128 | S: POP (0) 1129 | S: TEST (!=,cte=1) 1130 | S: PUSH 0 1131 | S: POP (0) 1132 | S: TEST (!=,cte=1) 1133 | S: PUSH 0 1134 | S: POP (0) 1135 | S: TEST (!=,cte=1) 1136 | S: PUSH 0 1137 | S: POP (0) 1138 | S: TEST (!=,cte=1) 1139 | S: PUSH 0 1140 | S: POP (0) 1141 | S: TEST (!=,cte=1) 1142 | S: PUSH 0 1143 | S: POP (0) 1144 | S: TEST (!=,cte=1) 1145 | S: PUSH 0 1146 | S: POP (0) 1147 | S: TEST (!=,cte=1) 1148 | S: PUSH 0 1149 | S: POP (0) 1150 | S: TEST (!=,cte=1) 1151 | S: PUSH 0 1152 | S: POP (0) 1153 | S: TEST (!,#op=1) 1154 | S: PUSH 32 1155 | -------------------------------------------------------------------------------- /data/solve.py: -------------------------------------------------------------------------------- 1 | from z3 import * 2 | 3 | # initialize our symbols as 8-bit bitvectors 4 | c0 = BitVec('c0', 8) 5 | c1 = BitVec('c1', 8) 6 | c2 = BitVec('c2', 8) 7 | c3 = BitVec('c3', 8) 8 | c4 = BitVec('c4', 8) 9 | c5 = BitVec('c5', 8) 10 | c6 = BitVec('c6', 8) 11 | c7 = BitVec('c7', 8) 12 | c8 = BitVec('c8', 8) 13 | c9 = BitVec('c9', 8) 14 | c10 = BitVec('c10', 8) 15 | c11 = BitVec('c11', 8) 16 | c12 = BitVec('c12', 8) 17 | c13 = BitVec('c13', 8) 18 | c14 = BitVec('c14', 8) 19 | c15 = BitVec('c15', 8) 20 | c16 = BitVec('c16', 8) 21 | c17 = BitVec('c17', 8) 22 | c18 = BitVec('c18', 8) 23 | 24 | s = Solver() 25 | 26 | # allowed character range 27 | s.add(c0 > 32, c0 < 127) 28 | s.add(c0 > 32, c0 < 127) 29 | s.add(c1 > 32, c1 < 127) 30 | s.add(c2 > 32, c2 < 127) 31 | s.add(c3 > 32, c3 < 127) 32 | s.add(c4 > 32, c4 < 127) 33 | s.add(c5 > 32, c5 < 127) 34 | s.add(c6 > 32, c6 < 127) 35 | s.add(c7 > 32, c7 < 127) 36 | s.add(c8 > 32, c8 < 127) 37 | s.add(c9 > 32, c9 < 127) 38 | s.add(c10 > 32, c10 < 127) 39 | s.add(c11 > 32, c11 < 127) 40 | s.add(c12 > 32, c12 < 127) 41 | s.add(c13 > 32, c13 < 127) 42 | s.add(c14 > 32, c14 < 127) 43 | s.add(c15 > 32, c15 < 127) 44 | s.add(c16 > 32, c16 < 127) 45 | s.add(c17 > 32, c17 < 127) 46 | s.add(c18 > 32, c18 < 127) 47 | 48 | # constraints 49 | s.add((((c12-c10)^c13)*(c6*c14))==16335) 50 | s.add(((c18^c1)^(c15-c7))==83) 51 | s.add((((c9+c5)^c0)*(c17-c16))==4294961394) 52 | s.add((c3-c11)==11) 53 | s.add(((c2+c4)^c8)==3) 54 | s.add((c8+(c15-c4))==176) 55 | s.add((((c9^c10)-(c11+c18))^c6)==4294967097) 56 | s.add(((c1*(c0^c17))+(c2*c16))==9985) 57 | s.add(((c14*c13)-c7)==2083) 58 | s.add(((c12+c3)-c5)==110) 59 | s.add(((c8*c10)+(c9+c13))==5630) 60 | s.add(((c5-c16)-(c2+c0))==4294967114) 61 | s.add((c17*(c14^c7))==7200) 62 | s.add(((c1*c3)+(c6*c11))==17872) 63 | s.add(((c12-c15)-(c18*c4))==4294961888) 64 | s.add(((c11*c2)+(c3*c15))==18888) 65 | s.add((c16*(c5+c13))==15049) 66 | s.add((c17*(c0+c10))==12150) 67 | s.add((c18*(c14^c6))==10080) 68 | s.add(((c7+c12)-c4)==132) 69 | s.add((c8+(c1*c9))==2453) 70 | 71 | # result 72 | print(s.check()) 73 | m = s.model() 74 | result = '' 75 | result += chr(m[c0].as_long()) 76 | result += chr(m[c1].as_long()) 77 | result += chr(m[c2].as_long()) 78 | result += chr(m[c3].as_long()) 79 | result += chr(m[c4].as_long()) 80 | result += chr(m[c5].as_long()) 81 | result += chr(m[c6].as_long()) 82 | result += chr(m[c7].as_long()) 83 | result += chr(m[c8].as_long()) 84 | result += chr(m[c9].as_long()) 85 | result += chr(m[c10].as_long()) 86 | result += chr(m[c11].as_long()) 87 | result += chr(m[c12].as_long()) 88 | result += chr(m[c13].as_long()) 89 | result += chr(m[c14].as_long()) 90 | result += chr(m[c15].as_long()) 91 | result += chr(m[c16].as_long()) 92 | result += chr(m[c17].as_long()) 93 | result += chr(m[c18].as_long()) 94 | print(result) 95 | 96 | 97 | -------------------------------------------------------------------------------- /data/symbolic_exec_stack_machine.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | ''' 3 | Quick and dirty script to replay MarsAnalytica stack machine execution trace, 4 | gathered with the companion JEB decompiler plugin. 5 | 6 | Symbolic variables are introduced in lieu of concrete input characters, and 7 | operations are built over those variables. At the end of execution, 8 | constraints should be visible (or still be in stack depending if the 9 | trace is complete or not). 10 | 11 | Note that the execution trace should have been stripped from the initial 12 | SET operators putting in place concrete input characters. 13 | 14 | November 2020 - Joan Calvet - PNF Software 15 | ''' 16 | 17 | f = open('mars_analytica_stack_machine_trace.log') 18 | 19 | def buildOperation(operands, operator, constant): 20 | ''' 21 | Join operands/constant with provided operator 22 | ''' 23 | if len(operands) == 1: 24 | if constant != None: 25 | return operands[0] + operator + constant 26 | return operator + operands[0] 27 | else: 28 | return operator.join(operands) 29 | 30 | # 1. initialize data structures 31 | 32 | # fill stack with 'symbolic' variables (ie, characters) 33 | # at the initial offset retrieved from the trace 34 | stack = [None] * 50 35 | charCounter = 0 36 | stack[7] = 'c' + str(charCounter) # S: SET index:7 value:... 37 | charCounter+=1 38 | stack[8] = 'c' + str(charCounter) # S: SET index:8 value:... 39 | charCounter+=1 40 | stack[13] = 'c' + str(charCounter) 41 | charCounter+=1 42 | stack[15] = 'c' + str(charCounter) 43 | charCounter+=1 44 | stack[16] = 'c' + str(charCounter) 45 | charCounter+=1 46 | stack[26] = 'c' + str(charCounter) 47 | charCounter+=1 48 | stack[27] = 'c' + str(charCounter) 49 | charCounter+=1 50 | stack[22] = 'c' + str(charCounter) 51 | charCounter+=1 52 | stack[21] = 'c' + str(charCounter) 53 | charCounter+=1 54 | stack[4] = 'c' + str(charCounter) 55 | charCounter+=1 56 | stack[18] = 'c' + str(charCounter) 57 | charCounter+=1 58 | stack[28] = 'c' + str(charCounter) 59 | charCounter+=1 60 | stack[23] = 'c' + str(charCounter) 61 | charCounter+=1 62 | stack[29] = 'c' + str(charCounter) 63 | charCounter+=1 64 | stack[9] = 'c' + str(charCounter) 65 | charCounter+=1 66 | stack[1] = 'c' + str(charCounter) 67 | charCounter+=1 68 | stack[25] = 'c' + str(charCounter) 69 | charCounter+=1 70 | stack[30] = 'c' + str(charCounter) 71 | charCounter+=1 72 | stack[17] = 'c' + str(charCounter) 73 | charCounter+=1 74 | 75 | tempStorage = list() # will serve for poped values 76 | constraints = list() 77 | 78 | # 2. parse execution trace 79 | lineCounter = 0 80 | lines = f.readlines() 81 | isTrueConstraint = False 82 | try: 83 | while lineCounter < len(lines): 84 | curLine = lines[lineCounter] 85 | 86 | if curLine.startswith('S:'): 87 | print('--------------------------') 88 | print('> processing %s' % curLine) 89 | parsed_line = curLine.split() 90 | operator = parsed_line[1] 91 | value = parsed_line[2] if len(parsed_line) > 2 else None 92 | 93 | if operator == 'PUSH': 94 | nextLine = lines[lineCounter+1] 95 | # we are either pushing the last poped value... 96 | if len(tempStorage) == 1: 97 | print(' > pushing unique temporary value') 98 | stack.append(tempStorage[0]) 99 | tempStorage.clear() 100 | isTrueConstraint = False # so it was not a constraint 101 | else: 102 | # ...or an operation result 103 | if nextLine.startswith(' | operation: '): 104 | operator = nextLine[16:17] 105 | numberOfOperands = nextLine[-3:-2] 106 | if int(numberOfOperands) == len(tempStorage): 107 | print(' > building operation with temporary values') 108 | stack.append('(' + buildOperation(tempStorage,operator,None) + ')') 109 | tempStorage.clear() 110 | else: 111 | raise ValueError('operation without correct number of operands') 112 | else: 113 | # ...or a new constant value 114 | stack.append(value) 115 | print(' > pushing new value into stack') 116 | 117 | else: 118 | 119 | if isTrueConstraint: 120 | # a true constraint is a test whose result was not pushed for subsequent operations; 121 | # it serves to guide execution and these are the ones we want to solve 122 | print('> found a true constraint:') 123 | print(tempStorage) 124 | constraints.append(tempStorage[0]) 125 | tempStorage.clear() 126 | isTrueConstraint = False 127 | 128 | if operator == 'SWAP': 129 | last = stack.pop() 130 | beforeLast = stack.pop() 131 | stack.append(last) 132 | stack.append(beforeLast) 133 | print(' > stack swapping') 134 | 135 | elif operator == 'GET': 136 | index = curLine.split(':')[2][:-1] 137 | tempStorage.append(stack[int(index)]) 138 | print(' > get value in temporary storage') 139 | 140 | elif operator == 'SET': 141 | index = curLine[curLine.find('index:')+6:curLine.find('value:')-1] 142 | if len(tempStorage) >= 1: 143 | stack[int(index)] = tempStorage.pop() 144 | else: 145 | raise ValueError('SET problem') 146 | print(' > set last value from temporary storage') 147 | 148 | elif operator == 'POP': 149 | value = stack.pop() 150 | tempStorage.append(value) 151 | print(' > poped value in temporary storage') 152 | 153 | elif operator == 'TEST': 154 | operator = curLine[curLine.find('(')+1:curLine.find(',')] 155 | constante = None 156 | if curLine.find('cte=') != -1: 157 | constante = curLine[curLine.find('cte=')+4:-2] 158 | 159 | tempStorage.reverse() # revert operand order (depends on decompiled code, tested for <, >) 160 | newValue = '(' + buildOperation(tempStorage,operator,constante) + ')' 161 | tempStorage.clear() 162 | tempStorage.append(newValue) 163 | 164 | # test result might be a true constraint, 165 | # or an intermediary test that will be used for later operations 166 | isTrueConstraint = True 167 | 168 | print(' > building test operation with temporary value') 169 | 170 | else: 171 | raise ValueError('not implemented operator') 172 | 173 | print(' > current state') 174 | print('stack:') 175 | print(stack) 176 | print('tempStorage:') 177 | print(tempStorage) 178 | 179 | lineCounter+=1 180 | except: 181 | print('> end of emulation (trace incomplete?)') 182 | 183 | # 3. final print 184 | print('--- LOGGED CONSTRAINTS --') 185 | for c in constraints: 186 | print(c) 187 | print('--- STACKED EXPRESSIONS --') 188 | for c in stack: 189 | if c != None: 190 | print(c) 191 | 192 | -------------------------------------------------------------------------------- /out/CEmulatorPlugin-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnfsoftware/jeb-c-emulator-plugin/968cb876deab41609794a9a2b873473b474ceb14/out/CEmulatorPlugin-1.0.0.jar -------------------------------------------------------------------------------- /scripts/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 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 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /scripts/generateEclipseProjectFilesForPlugin.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | tplProject = ''' 5 | 6 | %s 7 | 8 | 9 | 10 | 11 | 12 | org.eclipse.jdt.core.javabuilder 13 | 14 | 15 | 16 | 17 | 18 | org.eclipse.jdt.core.javanature 19 | 20 | 21 | ''' 22 | 23 | tplClasspath = ''' 24 | 25 | 26 | 27 | %s 28 | 29 | 30 | ''' 31 | 32 | if __name__ == '__main__': 33 | prjname = os.path.split(os.path.abspath(os.path.dirname(sys.argv[0]) + '/..'))[-1] 34 | print('Project name: %s' % prjname) 35 | 36 | internal = len(sys.argv) > 1 and sys.argv[1] == '-i' 37 | 38 | if 'JEB_HOME' not in os.environ: 39 | print('Set an environment variable JEB_HOME pointing to your JEB folder') 40 | sys.exit(-1) 41 | 42 | jebhome = os.environ['JEB_HOME'] 43 | jebcorepath = os.path.join(jebhome, 'bin/app/jeb.jar') 44 | if not os.path.isfile(jebcorepath): 45 | print('Based on your value of JEB_HOME, jeb.jar was expected at this location, but it was not found: %s' % jebcorepath) 46 | sys.exit(-1) 47 | jebdocpath = os.path.join(jebhome, 'doc/apidoc.zip') 48 | 49 | _Project = tplProject % prjname 50 | with open('.project', 'w') as f: 51 | f.write(_Project) 52 | print('Generated: Eclipse .project file') 53 | 54 | jeblibentry = ''' 55 | 56 | ''' % (jebcorepath, jebdocpath) 57 | if internal: 58 | # FOR INTERNAL USE 59 | jeblibentry = '' 60 | _Classpath = tplClasspath % jeblibentry 61 | with open('.classpath', 'w') as f: 62 | f.write(_Classpath) 63 | print('Generated: Eclipse .classpath file') -------------------------------------------------------------------------------- /src/com/pnf/plugin/cemulator/CEmulatorPlugin.java: -------------------------------------------------------------------------------- 1 | package com.pnf.plugin.cemulator; 2 | 3 | import java.io.File; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import com.pnf.plugin.cemulator.EmulatorState.MemoryDump; 9 | import com.pnfsoftware.jeb.core.AbstractEnginesPlugin; 10 | import com.pnfsoftware.jeb.core.BooleanOptionDefinition; 11 | import com.pnfsoftware.jeb.core.IEnginesContext; 12 | import com.pnfsoftware.jeb.core.ILiveArtifact; 13 | import com.pnfsoftware.jeb.core.IOptionDefinition; 14 | import com.pnfsoftware.jeb.core.IPluginInformation; 15 | import com.pnfsoftware.jeb.core.IRuntimeProject; 16 | import com.pnfsoftware.jeb.core.OptionDefinition; 17 | import com.pnfsoftware.jeb.core.PluginInformation; 18 | import com.pnfsoftware.jeb.core.Version; 19 | import com.pnfsoftware.jeb.core.exceptions.JebRuntimeException; 20 | import com.pnfsoftware.jeb.core.units.INativeCodeUnit; 21 | import com.pnfsoftware.jeb.core.units.IUnit; 22 | import com.pnfsoftware.jeb.core.units.UnitUtil; 23 | import com.pnfsoftware.jeb.core.units.code.EntryPointDescription; 24 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.INativeDecompilerUnit; 25 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.INativeSourceUnit; 26 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICMethod; 27 | import com.pnfsoftware.jeb.core.util.DecompilerHelper; 28 | import com.pnfsoftware.jeb.util.logging.GlobalLog; 29 | import com.pnfsoftware.jeb.util.logging.ILogger; 30 | import com.pnfsoftware.jeb.util.format.Strings; 31 | 32 | /** 33 | * Plugin to run {@link SimpleCEmulator} on JEB's decompiled C code. 34 | * 35 | * @author Joan Calvet 36 | * 37 | */ 38 | public class CEmulatorPlugin extends AbstractEnginesPlugin { 39 | private static final ILogger logger = GlobalLog.getLogger(CEmulatorPlugin.class); 40 | 41 | private Long firstRtnAddress = null; 42 | // optional memory dumps 43 | private MemoryDump stackDump; 44 | private MemoryDump heapDump; 45 | 46 | private File logFile; 47 | private boolean tracerMode; 48 | private boolean marsAnalyticaMode; 49 | 50 | public CEmulatorPlugin() { 51 | } 52 | 53 | public CEmulatorPlugin(long firstRtnAddress) { 54 | this.firstRtnAddress = firstRtnAddress; 55 | } 56 | 57 | public CEmulatorPlugin(long firstRtnAddress, MemoryDump stackDump, MemoryDump heapDump) { 58 | this(firstRtnAddress); 59 | this.stackDump = stackDump; 60 | this.heapDump = heapDump; 61 | } 62 | 63 | @Override 64 | public void load(IEnginesContext context) { 65 | logger.info("Loading C emulator plugin"); 66 | } 67 | 68 | public void setLogFile(File logFile) { 69 | this.logFile = logFile; 70 | } 71 | 72 | private void parseParameters(Map params) { 73 | if(params == null || params.isEmpty()) { 74 | return; 75 | } 76 | firstRtnAddress = Long.decode(params.get("FirstRtnAddr")); 77 | String logFilePath = params.get("LogFilePath"); 78 | if(!logFilePath.isEmpty()) { 79 | logFile = new File(logFilePath); 80 | } 81 | tracerMode = Boolean.parseBoolean(params.get("TracerMode")); 82 | marsAnalyticaMode = Boolean.parseBoolean(params.get("MarsAnalyticaMode")); 83 | 84 | } 85 | 86 | @Override 87 | public void execute(IEnginesContext context, Map params) { 88 | logger.info("Executing C emulator plugin"); 89 | 90 | IRuntimeProject prj = context.getProject(0); 91 | ILiveArtifact a = prj.getLiveArtifact(0); 92 | IUnit topLevelUnit = a.getMainUnit(); 93 | 94 | INativeCodeUnit codeUnit = (INativeCodeUnit)UnitUtil 95 | .findDescendantsByType(topLevelUnit, INativeCodeUnit.class, false).get(0); 96 | logger.info("Code unit: %s", codeUnit); 97 | 98 | if(!codeUnit.process()) { 99 | logger.info("ERROR: Failed to process code unit"); 100 | return; 101 | } 102 | 103 | INativeDecompilerUnit decomp = (INativeDecompilerUnit)DecompilerHelper.getDecompiler(codeUnit); 104 | logger.info("Decompiler: %s", decomp); 105 | 106 | parseParameters(params); 107 | if(firstRtnAddress == null) { 108 | throw new JebRuntimeException("ERROR: address of routine to emulate is undefined"); 109 | } 110 | 111 | // initial emulator state 112 | EmulatorState emulatorState; 113 | if(stackDump != null && heapDump != null) { 114 | emulatorState = new EmulatorState(codeUnit, stackDump, heapDump); 115 | emulatorState.setRegisterValue(SimpleCEmulator.REG_RBP_ID, stackDump.basePointer); 116 | } 117 | else { 118 | emulatorState = new EmulatorState(codeUnit); 119 | emulatorState.setRegisterValue(SimpleCEmulator.REG_RBP_ID, 0x7fffffffdf90L); //dummy value 120 | } 121 | emulatorState.allocateStackSpace(); 122 | 123 | SimpleCEmulator emulator = marsAnalyticaMode ? new MarsAnalyticaCEmulator(): new SimpleCEmulator(); 124 | 125 | // analyze first handler 126 | Long handlerAddress = firstRtnAddress; 127 | ICMethod handlerMethod = disassembleAndDecompile(decomp, handlerAddress); 128 | 129 | // tracing loop 130 | while(true) { 131 | logger.info("> emulating method %s...", handlerMethod.getName()); 132 | 133 | // emulate handler 134 | EmulatorLog log = emulator.emulate(handlerMethod, emulatorState); 135 | emulatorState = log.getCurrentEmulatorState(); 136 | 137 | // get next handler address 138 | handlerAddress = emulatorState.getRegisterValue(SimpleCEmulator.REG_NEXT_METHOD_ID); 139 | if(handlerAddress == null) { 140 | logger.info(" >> STOP: no next entry-point address found"); 141 | break; 142 | } 143 | 144 | emulator.dumpLog(logFile); 145 | 146 | if(!tracerMode) { 147 | break; 148 | } 149 | 150 | logger.info(" >> done; found next method entry point to emulate: 0x%08x", handlerAddress); 151 | handlerMethod = disassembleAndDecompile(decomp, handlerAddress); 152 | } 153 | } 154 | 155 | /** 156 | * Disassemble and decompile (or use the existing decompiled unit). 157 | * 158 | * @param decomp 159 | * @param methodAddress 160 | * @return decompiled method 161 | */ 162 | private ICMethod disassembleAndDecompile(INativeDecompilerUnit decomp, long methodAddress) { 163 | // disassemble, if needed 164 | if(!decomp.getCodeUnit().getCodeModel().isRoutineHeader(methodAddress)) { 165 | EntryPointDescription nextHandlerEPD = decomp.getCodeUnit().getProcessor().createEntryPoint(methodAddress); 166 | decomp.getCodeUnit().getCodeAnalyzer().enqueuePointerForAnalysis(nextHandlerEPD); 167 | decomp.getCodeUnit().getCodeAnalyzer().analyze(); 168 | } 169 | 170 | // decompile, if needed 171 | String decompUnitId = Strings.ff("%x", methodAddress); 172 | return (ICMethod)((INativeSourceUnit)decomp.decompile(decompUnitId)).getASTItem(); 173 | } 174 | 175 | @Override 176 | public IPluginInformation getPluginInformation() { 177 | return new PluginInformation("CEmulator", "Plugin to emulate JEB's decompiled C code", 178 | "Joan Calvet (PNF Software)", Version.create(1, 0, 0)); 179 | } 180 | 181 | @Override 182 | public List getExecutionOptionDefinitions() { 183 | return Arrays.asList(new OptionDefinition("FirstRtnAddr", "Address of routine to emulate"), 184 | new BooleanOptionDefinition( 185 | "TracerMode", true, 186 | "Tracer mode enabled (emulator follows subroutine calls -- until it cannot anymore)"), 187 | new BooleanOptionDefinition( 188 | "MarsAnalyticaMode", true, 189 | "MarsAnalytica's specific logic enabled"), 190 | new OptionDefinition("LogFilePath", 191 | "Path to log file (optional -- if unspecified logs will be written as a sub unit in JEB project)")); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/com/pnf/plugin/cemulator/CFG.java: -------------------------------------------------------------------------------- 1 | package com.pnf.plugin.cemulator; 2 | 3 | import java.util.ArrayList; 4 | import java.util.IdentityHashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICBlock; 9 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICBreak; 10 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICCompound; 11 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICContinue; 12 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICControlBreaker; 13 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICForStm; 14 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICGoto; 15 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICIfStm; 16 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICJumpFar; 17 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICMethod; 18 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICReturn; 19 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICStatement; 20 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICWhileStm; 21 | import com.pnfsoftware.jeb.util.format.Strings; 22 | 23 | /** 24 | * Control-Flow Graph (CFG) representing the control-flow of an {@link ICMethod}. 25 | *

26 | * This class is a minimal implementation to be used by {@link SimpleCEmulator}. The computed 27 | * graph only provides the reachable statements from each statement (through 28 | * {@link #getNextStatement(ICStatement)} and {@link #getNthNextStatement(ICStatement, int)}). 29 | *

30 | * Known limitations/simplifications: 31 | *

    32 | *
  • statements are assumed to be unique within the method, and serves to identify graph nodes 33 | * (i.e. no basic blocks) 34 | *
  • for compound statements (see {@link ICCompound}), the embedded {@link ICBlock} are kept in 35 | * the CFG and transfer control to their first statement 36 | *
  • do-while loops are not specifically handled; they are ordered as while-loops 37 | *
  • switch-case are not handled 38 | *
  • predicates are not stored on their corresponding edges; it's the client responsibility to 39 | * retrieve them 40 | *
  • graph exit(s) are represented by 'null' nodes 41 | *
42 | * 43 | * @author Joan Calvet 44 | * 45 | */ 46 | public class CFG { 47 | 48 | /** 49 | * Reachable statements (from -> to) 50 | *

51 | * Implementation note: if there are several reachable statements, the first statement is the 52 | * fallthrough (see {@link #getNextStatement(ICStatement)}). Others statements correspond to 53 | * conditional branches (see {@link #getNthNextStatement(ICStatement, int)}. 54 | */ 55 | private Map> outEdges = new IdentityHashMap<>(); 56 | 57 | /** 58 | * CFG's entry point 59 | */ 60 | private ICStatement entryPoint; 61 | 62 | /** 63 | * Process method's AST and build the corresponding CFG. 64 | * 65 | * @param method 66 | * @return cfg of the method 67 | */ 68 | public static CFG buildCFG(ICMethod method) { 69 | CFG cfg = new CFG(); 70 | buildCFGRecursive(cfg, method.getBody(), null, null); 71 | cfg.entryPoint = method.getBody().isEmpty() ? null: method.getBody().get(0); 72 | return cfg; 73 | } 74 | 75 | /** 76 | * Build a CFG by recursively processing {@link ICBlock}. 77 | * 78 | * @param cfg 79 | * @param currentBlock the block to process 80 | * @param parentStatement the statement containing the block in the AST, null if none 81 | * @param parentLoop the loop containing the block in the AST, null if none 82 | */ 83 | private static void buildCFGRecursive(CFG cfg, ICBlock currentBlock, ICStatement parentStatement, 84 | ICStatement parentLoop) { 85 | int index = 0; 86 | while(index < currentBlock.size()) { 87 | ICStatement curStatement = currentBlock.get(index); 88 | ICStatement parentLoop_ = parentLoop; 89 | 90 | // add default fallthrough edge on all statements 91 | // (only if not already set) 92 | ICStatement ftStatement = null; 93 | if(index < currentBlock.size() - 1) { 94 | ftStatement = currentBlock.get(index + 1); 95 | } 96 | else { 97 | // last element of the block: fallthrough is parent's fallthrough 98 | ftStatement = cfg.getNextStatement(parentStatement); 99 | } 100 | cfg.setFallThrough(curStatement, ftStatement, false); 101 | 102 | if(curStatement instanceof ICGoto) { 103 | cfg.setFallThrough(curStatement, ((ICGoto)curStatement).getLabel()); 104 | } 105 | else if(curStatement instanceof ICWhileStm || curStatement instanceof ICForStm) { 106 | parentLoop_ = curStatement; 107 | ICBlock loopBody = ((ICCompound)curStatement).getBlocks().get(0); 108 | cfg.addConditionalTarget(curStatement, loopBody); 109 | 110 | // set backward edge 111 | if(loopBody.isEmpty()) { 112 | cfg.setFallThrough(loopBody, curStatement); 113 | } 114 | else { 115 | cfg.setFallThrough(loopBody.getLast(), curStatement); 116 | } 117 | } 118 | else if(curStatement instanceof ICIfStm) { 119 | // for each branch, add an edge 120 | List ifBlocks = ((ICIfStm)curStatement).getBlocks(); 121 | for(int i = 0; i < ((ICIfStm)curStatement).size(); i++) { 122 | cfg.addConditionalTarget(curStatement, ifBlocks.get(i)); 123 | } 124 | } 125 | else if(curStatement instanceof ICReturn || curStatement instanceof ICJumpFar) { 126 | // end of method 127 | cfg.setFallThrough(curStatement, null); 128 | } 129 | else if(curStatement instanceof ICControlBreaker) { 130 | if(((ICControlBreaker)curStatement).getLabel() != null) { 131 | cfg.setFallThrough(curStatement, ((ICControlBreaker)curStatement).getLabel()); 132 | } 133 | else { 134 | if(curStatement instanceof ICBreak) { 135 | // goto parent loop's fallthrough 136 | cfg.setFallThrough(curStatement, cfg.getNextStatement(parentLoop_)); 137 | } 138 | else if(curStatement instanceof ICContinue) { 139 | // goto parent loop 140 | cfg.setFallThrough(curStatement, parentLoop_); 141 | } 142 | } 143 | } 144 | else if(curStatement instanceof ICBlock) { 145 | cfg.setFallThrough(curStatement, ((ICBlock)curStatement).get(0)); 146 | } 147 | 148 | // recursion 149 | if(curStatement instanceof ICCompound) { 150 | for(ICBlock subBlock: ((ICCompound)curStatement).getBlocks()) { 151 | if(!subBlock.isEmpty()) { 152 | cfg.setFallThrough(subBlock, subBlock.get(0)); 153 | } 154 | buildCFGRecursive(cfg, subBlock, curStatement, parentLoop_); 155 | } 156 | } 157 | 158 | index += 1; 159 | } 160 | } 161 | 162 | 163 | /** 164 | * Get the statement reachable from the given statement, when its predicate is true. For 165 | * multi-predicates statement, see {@link #getNthNextStatement(ICStatement, int)}. 166 | * 167 | * @param from 168 | * @return statement conditionally reachable, null if none 169 | */ 170 | public ICStatement getNextTrueStatement(ICStatement from) { 171 | return outEdges.get(from) != null && outEdges.get(from).size() > 1 ? outEdges.get(from).get(1) 172 | : null; 173 | } 174 | 175 | /** 176 | * Get the statement reachable from the given statement, when its n-th predicate is true. This 177 | * method is the preferred one for multi-predicate statements (if-elseif-..), and the reachable 178 | * statements are ordered as they are in their container statement. 179 | * 180 | * @param from 181 | * @param n 182 | * @return the n-th conditionally reachable statement, null if none 183 | */ 184 | public ICStatement getNthNextStatement(ICStatement from, int n) { 185 | return outEdges.get(from) != null && outEdges.get(from).size() > n + 1 186 | ? outEdges.get(from).get(n + 1): null; 187 | } 188 | 189 | /** 190 | * Get next statement reachable from the given statement, defined as: 191 | *

192 | *

    193 | *
  • for unconditional statements: the next reachable statement (eg, target of goto statement 194 | * or next statement after an assignment) 195 | *
  • for conditional statements: the statement reachable when predicates are false 196 | *
197 | * 198 | * @param from 199 | * @return first next possible statement, might be null 200 | */ 201 | public ICStatement getNextStatement(ICStatement from) { 202 | return outEdges.get(from) != null ? outEdges.get(from).get(0): null; 203 | } 204 | 205 | private boolean setFallThrough(ICStatement from, ICStatement to) { 206 | return setFallThrough(from, to, true); 207 | } 208 | 209 | private boolean setFallThrough(ICStatement from, ICStatement to, boolean eraseExisting) { 210 | List nexts = outEdges.get(from); 211 | if(nexts == null) { 212 | nexts = new ArrayList<>(); 213 | outEdges.put(from, nexts); 214 | } 215 | if(eraseExisting && nexts.size() >= 1) { 216 | nexts.set(0, to); 217 | return true; 218 | } 219 | else if(nexts.isEmpty()) { 220 | nexts.add(to); 221 | return true; 222 | } 223 | return false; 224 | } 225 | 226 | /** 227 | * Add a new conditional reachable statement. 228 | *

229 | * Important: this method assumes the fallthrough statement has already been set. 230 | * 231 | * @param from 232 | * @param to 233 | * @return 234 | */ 235 | private boolean addConditionalTarget(ICStatement from, ICStatement to) { 236 | List nexts = outEdges.get(from); 237 | if(nexts == null) { 238 | nexts = new ArrayList<>(); 239 | outEdges.put(from, nexts); 240 | } 241 | nexts.add(to); 242 | return true; 243 | } 244 | 245 | /** 246 | * Get CFG entry point. 247 | * 248 | * @return cfg entry point, might be null 249 | */ 250 | public ICStatement getEntryPoint() { 251 | return entryPoint; 252 | } 253 | 254 | public void setEntryPoint(ICStatement entryPoint) { 255 | this.entryPoint = entryPoint; 256 | } 257 | 258 | @Override 259 | public String toString() { 260 | StringBuilder sb = new StringBuilder(); 261 | for(ICStatement from: outEdges.keySet()) { 262 | sb.append("--edge--"); 263 | sb.append(Strings.LINESEP); 264 | sb.append("> from:"); 265 | sb.append(from.getClass().getSimpleName().toString()); 266 | sb.append(Strings.LINESEP); 267 | sb.append(from); 268 | sb.append(Strings.LINESEP); 269 | for(ICStatement to: outEdges.get(from)) { 270 | if(to == null) { 271 | sb.append("> to:EXIT"); 272 | sb.append(Strings.LINESEP); 273 | } 274 | else { 275 | sb.append("> to:"); 276 | sb.append(to.getClass().getSimpleName().toString()); 277 | sb.append(Strings.LINESEP); 278 | sb.append(to); 279 | sb.append(Strings.LINESEP); 280 | } 281 | } 282 | } 283 | return sb.toString(); 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /src/com/pnf/plugin/cemulator/EmulatorException.java: -------------------------------------------------------------------------------- 1 | package com.pnf.plugin.cemulator; 2 | 3 | import com.pnfsoftware.jeb.core.exceptions.JebRuntimeException; 4 | 5 | /** 6 | * Exception for {@link SimpleCEmulator} 7 | * 8 | * @author Joan Calvet 9 | * 10 | */ 11 | public class EmulatorException extends JebRuntimeException { 12 | private static final long serialVersionUID = 1L; 13 | 14 | public EmulatorException() { 15 | super(); 16 | } 17 | 18 | public EmulatorException(String message) { 19 | super(message); 20 | } 21 | 22 | public EmulatorException(Throwable cause) { 23 | super(cause); 24 | } 25 | 26 | public EmulatorException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/com/pnf/plugin/cemulator/EmulatorLog.java: -------------------------------------------------------------------------------- 1 | package com.pnf.plugin.cemulator; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICStatement; 7 | 8 | /** 9 | * Log of a method emulation done by {@link SimpleCEmulator}. Provides access to the execution trace 10 | * and the current emulator state. 11 | * 12 | * @author Joan Calvet 13 | * 14 | */ 15 | public class EmulatorLog { 16 | 17 | private List executionTrace = new ArrayList<>(); 18 | private EmulatorState currentState; 19 | 20 | public void addExecutedStatement(ICStatement stmt) { 21 | executionTrace.add(stmt.toString()); 22 | } 23 | 24 | /** 25 | * Get the list of string representations of the executed statements 26 | */ 27 | public List getExecutionTrace() { 28 | return executionTrace; 29 | } 30 | 31 | public void setEmulatorState(EmulatorState state) { 32 | currentState = state; 33 | } 34 | 35 | public EmulatorState getCurrentEmulatorState() { 36 | return currentState; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/com/pnf/plugin/cemulator/EmulatorState.java: -------------------------------------------------------------------------------- 1 | package com.pnf.plugin.cemulator; 2 | 3 | 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.nio.file.Files; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import com.pnfsoftware.jeb.core.exceptions.JebRuntimeException; 11 | import com.pnfsoftware.jeb.core.units.INativeCodeUnit; 12 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.CIdentifierClass; 13 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICDecl; 14 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICElement; 15 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICIdentifier; 16 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICType; 17 | import com.pnfsoftware.jeb.core.units.code.asm.memory.IVirtualMemory; 18 | import com.pnfsoftware.jeb.core.units.code.asm.memory.MemoryException; 19 | import com.pnfsoftware.jeb.core.units.code.asm.memory.VirtualMemoryUtil; 20 | import com.pnfsoftware.jeb.core.units.code.asm.type.INativeType; 21 | import com.pnfsoftware.jeb.core.units.code.asm.type.ITypeManager; 22 | import com.pnfsoftware.jeb.util.base.Assert; 23 | import com.pnfsoftware.jeb.util.format.Strings; 24 | import com.pnfsoftware.jeb.util.logging.GlobalLog; 25 | import com.pnfsoftware.jeb.util.logging.ILogger; 26 | 27 | /** 28 | * State of the emulator (memory + registers) 29 | * 30 | * @author Joan Calvet 31 | * 32 | */ 33 | public class EmulatorState { 34 | private static final ILogger logger = GlobalLog.getLogger(EmulatorState.class); 35 | 36 | private IVirtualMemory memory; 37 | private Map registers = new HashMap<>(); // id -> value 38 | 39 | /** default pointer size, in bytes */ 40 | private Integer defaultPointerSize; 41 | private INativeCodeUnit nativeUnit; 42 | private ITypeManager typeManager; 43 | 44 | static class MemoryDump { 45 | long baseAddress; 46 | File dumpFile; 47 | long basePointer; // optional 48 | 49 | public MemoryDump(long baseAddress, File dumpFile) { 50 | this.baseAddress = baseAddress; 51 | this.dumpFile = dumpFile; 52 | } 53 | 54 | public MemoryDump(long baseAddress, File dumpFile, long basePointer) { 55 | this(baseAddress, dumpFile); 56 | this.basePointer = basePointer; 57 | } 58 | } 59 | 60 | public EmulatorState(INativeCodeUnit nativeUnit) { 61 | Assert.a(nativeUnit != null); 62 | this.nativeUnit = nativeUnit; 63 | typeManager = nativeUnit.getTypeManager(); 64 | 65 | // memory initialization 66 | defaultPointerSize = nativeUnit.getMemory().getSpaceBits() / 8; 67 | memory = nativeUnit.getMemory(); 68 | } 69 | 70 | /** 71 | * Initialize state from stack/heap memory dumps 72 | */ 73 | public EmulatorState(INativeCodeUnit nativeUnit, MemoryDump stackDump, MemoryDump heapDump) { 74 | Assert.a(nativeUnit != null); 75 | this.typeManager = nativeUnit.getTypeManager(); 76 | defaultPointerSize = nativeUnit.getMemory().getSpaceBits() / 8; 77 | this.memory = nativeUnit.getMemory().duplicate(); 78 | 79 | // stack allocation 80 | byte[] src = null; 81 | try { 82 | src = Files.readAllBytes(stackDump.dumpFile.toPath()); 83 | allocateMemory(stackDump.baseAddress, src.length); 84 | this.memory.write(stackDump.baseAddress, src.length, src, 0); 85 | } 86 | catch(IOException e) { 87 | throw new JebRuntimeException("error when reading stack dump"); 88 | } 89 | 90 | // heap allocation 91 | src = null; 92 | try { 93 | src = Files.readAllBytes(heapDump.dumpFile.toPath()); 94 | allocateMemory(heapDump.baseAddress, src.length); 95 | this.memory.write(heapDump.baseAddress, src.length, src, 0); 96 | } 97 | catch(IOException e) { 98 | throw new JebRuntimeException("error when reading heap dump"); 99 | } 100 | } 101 | 102 | public void allocateMemory(long baseAddress, int size) { 103 | VirtualMemoryUtil.allocateFillGaps(this.memory, baseAddress, size, IVirtualMemory.ACCESS_RW); 104 | } 105 | 106 | public boolean allocateStackSpace() { 107 | Long baseStackPointerValue = getRegisterValue(SimpleCEmulator.REG_RBP_ID); 108 | if(baseStackPointerValue != null) { 109 | // arbitrary size 110 | VirtualMemoryUtil.allocateFillGaps(memory, (baseStackPointerValue & 0xFFFFFFFFFFFFF000L) - 0x10_0000, 111 | 0x11_0000, IVirtualMemory.ACCESS_RW); 112 | return true; 113 | } 114 | return false; 115 | } 116 | 117 | public Long getVarValue(ICElement element) { 118 | ICIdentifier id = getIdentifier(element); 119 | if(id.getIdentifierClass() == CIdentifierClass.LOCAL || id.getIdentifierClass() == CIdentifierClass.GLOBAL) { 120 | return readMemory(getVarAddress(id), getTypeSize(id.getType())); 121 | } 122 | else { 123 | return registers.get(id.getId()); 124 | } 125 | } 126 | 127 | public void setVarValue(ICElement element, long value) { 128 | ICIdentifier id = getIdentifier(element); 129 | if(id.getIdentifierClass() == CIdentifierClass.LOCAL || id.getIdentifierClass() == CIdentifierClass.GLOBAL) { 130 | writeMemory(getVarAddress(id), value, getTypeSize(id.getType())); 131 | } 132 | else { 133 | int typeSize = getTypeSize(id.getType()); 134 | switch(typeSize) { 135 | case 8: 136 | registers.put(id.getId(), value); 137 | break; 138 | case 4: 139 | registers.put(id.getId(), value & 0xFFFFFFFFL); 140 | break; 141 | case 2: 142 | registers.put(id.getId(), value & 0xFFFFL); 143 | break; 144 | case 1: 145 | registers.put(id.getId(), value & 0xFFL); 146 | break; 147 | default: 148 | throw new EmulatorException(Strings.ff("TBI: register size %d", typeSize)); 149 | } 150 | } 151 | } 152 | 153 | private ICIdentifier getIdentifier(ICElement element) { 154 | if(element instanceof ICIdentifier) { 155 | return (ICIdentifier)element; 156 | } 157 | if(element instanceof ICDecl) { 158 | return ((ICDecl)element).getIdentifier(); 159 | } 160 | return null; 161 | } 162 | 163 | public Long getVarAddress(ICIdentifier var) { 164 | if(var.getIdentifierClass() == CIdentifierClass.LOCAL) { 165 | return var.getAddress() + getRegisterValue(SimpleCEmulator.REG_RBP_ID) + 8; // we assume stack does not change 166 | } 167 | else if(var.getIdentifierClass() == CIdentifierClass.GLOBAL) { 168 | return var.getAddress(); 169 | } 170 | else { 171 | throw new EmulatorException(Strings.ff("TBI: get address for var (%s)", var)); 172 | } 173 | } 174 | 175 | /** 176 | * Get type size in bytes. 177 | * 178 | * @param type 179 | * @return type size in bytes 180 | */ 181 | public int getTypeSize(ICType type) { 182 | String typeName = type.getSignature(); 183 | INativeType typeItem = typeManager.getType(typeName); 184 | if(typeItem == null) { 185 | throw new EmulatorException(Strings.ff("ERROR: unknown type (%s)", typeName)); 186 | } 187 | return typeItem.getSize(); 188 | } 189 | 190 | /** 191 | * Get base type size in bytes, i.e. the size of TYPE in 'TYPE *' 192 | */ 193 | public int getBaseTypeSize(ICType type) { 194 | String typeName = type.getSignature(); 195 | if(typeName.endsWith("*")) { 196 | INativeType typeItem = typeManager.getType(type.getBaseTypeSignature()); 197 | if(typeItem == null) { 198 | throw new EmulatorException(Strings.ff("unknown base type (%s)", typeName)); 199 | } 200 | return typeItem.getSize(); 201 | } 202 | else { 203 | if(defaultPointerSize != null) { 204 | return defaultPointerSize; 205 | } 206 | throw new EmulatorException(Strings.ff("not a pointer type (%s)", typeName)); 207 | } 208 | } 209 | 210 | /** 211 | * Copy n bytes from source to destination 212 | * 213 | * @param src source address 214 | * @param dst destination address 215 | * @param n number of bytes to copy 216 | */ 217 | public void copyMemory(long src, long dst, int n) { 218 | byte[] toCopy = new byte[n]; 219 | try { 220 | memory.read(src, n, toCopy, 0); 221 | memory.write(dst, n, toCopy, 0); 222 | } 223 | catch(MemoryException e) { 224 | throw new EmulatorException("ERROR: memory copy failed"); 225 | } 226 | } 227 | 228 | /** 229 | * Read memory with default endianness. Default value (0L) is returned when memory read failed. 230 | * 231 | * @param address 232 | * @param bytesToRead number of bytes to read 233 | * @return read value, upper-casted as long, if memory couldn't be read return 0 234 | */ 235 | public Long readMemorySafe(long address, int bytesToRead) { 236 | try { 237 | return readMemory(address, bytesToRead); 238 | } 239 | catch(EmulatorException e) { 240 | logger.info("> warning: cannot read memory at 0x%08x -- returning 0L", address); 241 | return 0L; 242 | } 243 | } 244 | 245 | /** 246 | * Read memory with default endianness. 247 | * 248 | * @param address 249 | * @param bytesToRead number of bytes to read 250 | * @return read value, upper-casted as long 251 | */ 252 | public Long readMemory(long address, int bytesToRead) { 253 | Long value = 0L; 254 | try { 255 | switch(bytesToRead) { 256 | case 8: 257 | value = memory.readLong(address); 258 | break; 259 | case 4: 260 | value = memory.readInt(address) & 0xFFFFFFFFL; 261 | break; 262 | case 2: 263 | value = memory.readShort(address) & 0xFFFFL; 264 | break; 265 | case 1: 266 | value = memory.readByte(address) & 0xFFL; 267 | break; 268 | default: 269 | throw new EmulatorException(Strings.ff("TBI: read memory size (%d)", bytesToRead)); 270 | } 271 | } 272 | catch(MemoryException e) { 273 | throw new EmulatorException("ERROR: cant read memory"); 274 | } 275 | return value; 276 | } 277 | 278 | /** 279 | * Write memory with default endianness. 280 | * 281 | * @param address 282 | * @param value 283 | * @param bytesToWrite 284 | */ 285 | public void writeMemory(long address, long value, int bytesToWrite) { 286 | try { 287 | switch(bytesToWrite) { 288 | case 8: 289 | memory.writeLong(address, value); 290 | break; 291 | case 4: 292 | memory.writeInt(address, (int)value); 293 | break; 294 | case 2: 295 | memory.writeShort(address, (short)value); 296 | break; 297 | case 1: 298 | memory.writeByte(address, (byte)value); 299 | break; 300 | default: 301 | throw new EmulatorException(Strings.ff("TBI: write memory size (%d)", bytesToWrite)); 302 | } 303 | } 304 | catch(MemoryException e) { 305 | throw new EmulatorException("ERROR: cant write memory"); 306 | } 307 | } 308 | 309 | public void setRegisterValue(int id, long value) { 310 | registers.put(id, value); 311 | } 312 | 313 | /** 314 | * Get register value 315 | * 316 | * @param id 317 | * @return register value, null if not set 318 | */ 319 | public Long getRegisterValue(int id) { 320 | return registers.get(id); 321 | } 322 | 323 | public Integer getDefaultPointerSize() { 324 | return defaultPointerSize; 325 | } 326 | 327 | public void setDefaultPointerSize(Integer defaultPointedSize) { 328 | this.defaultPointerSize = defaultPointedSize; 329 | } 330 | 331 | public INativeCodeUnit getNativeCodeUnit() { 332 | return nativeUnit; 333 | } 334 | 335 | public String toRegisterString() { 336 | return "registers=" + registers; 337 | } 338 | 339 | @Override 340 | public String toString() { 341 | return "[memory=" + memory + ", registers=" + registers + "]"; 342 | } 343 | 344 | } 345 | -------------------------------------------------------------------------------- /src/com/pnf/plugin/cemulator/HeadlessClient.java: -------------------------------------------------------------------------------- 1 | package com.pnf.plugin.cemulator; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import com.pnf.plugin.cemulator.EmulatorState.MemoryDump; 7 | import com.pnfsoftware.jeb.client.HeadlessClientContext; 8 | import com.pnfsoftware.jeb.core.Artifact; 9 | import com.pnfsoftware.jeb.core.IEnginesContext; 10 | import com.pnfsoftware.jeb.core.IRuntimeProject; 11 | import com.pnfsoftware.jeb.core.exceptions.JebException; 12 | import com.pnfsoftware.jeb.core.input.FileInput; 13 | import com.pnfsoftware.jeb.util.base.Assert; 14 | import com.pnfsoftware.jeb.util.logging.GlobalLog; 15 | import com.pnfsoftware.jeb.util.logging.ILogger; 16 | 17 | /** 18 | * Headless JEB client to run the companion {@link CEmulatorPlugin} 19 | *

20 | * Refer to jeb-template-plugin for 21 | * a more complete example of a headless client. 22 | * 23 | * @author Joan Calvet 24 | * 25 | */ 26 | public class HeadlessClient { 27 | private static final ILogger logger = GlobalLog.getLogger(HeadlessClient.class); 28 | 29 | // arguments 30 | static File targetExecutablePath = null; 31 | static Long targetRoutineAddress = null; 32 | static File logPath = null; 33 | static Long stackBaseAddress = null; 34 | static File stackDump = null; 35 | static Long stackBasePointer = null; 36 | static Long heapBaseAddress = null; 37 | static File heapDump = null; 38 | 39 | public static void main(String[] args) throws JebException, IOException { 40 | HeadlessClientContext client = new HeadlessClientContext() { 41 | @Override 42 | public boolean isDevelopmentMode() { 43 | return true; 44 | } 45 | }; 46 | 47 | parseArguments(args); 48 | 49 | // initialize and start the client 50 | client.initialize(args); 51 | client.start(); 52 | 53 | IEnginesContext engctx = client.getEnginesContext(); 54 | 55 | // process target file 56 | IRuntimeProject prj = engctx.loadProject("ProjectTest"); 57 | prj.processArtifact(new Artifact(targetExecutablePath.getName(), new FileInput(targetExecutablePath))); 58 | 59 | // execute plugin 60 | try { 61 | CEmulatorPlugin plugin; 62 | if(stackDump != null && heapDump != null) { 63 | plugin = new CEmulatorPlugin(targetRoutineAddress, 64 | new MemoryDump(stackBaseAddress, stackDump, stackBasePointer), 65 | new MemoryDump(heapBaseAddress, heapDump)); 66 | } 67 | else { 68 | plugin = new CEmulatorPlugin(targetRoutineAddress); 69 | } 70 | if(logPath != null) { 71 | plugin.setLogFile(logPath); 72 | } 73 | plugin.execute(client.getEnginesContext()); 74 | } 75 | catch(Exception e) { 76 | logger.catching(e); 77 | } 78 | 79 | client.stop(); 80 | } 81 | 82 | private static void parseArguments(String[] args) { 83 | for(int i = 0; i < args.length; i += 2) { 84 | if(args[i].equals("--stack-dump")) { 85 | stackDump = new File(args[i + 1]); 86 | Assert.a(stackDump.isFile(), "cannot find stack-dump"); 87 | } 88 | else if(args[i].equals("--stack-base-adr")) { 89 | stackBaseAddress = Long.decode(args[i + 1]); 90 | } 91 | else if(args[i].equals("--stack-base-ptr")) { 92 | stackBasePointer = Long.decode(args[i + 1]); 93 | } 94 | else if(args[i].equals("--heap-dump")) { 95 | heapDump = new File(args[i + 1]); 96 | Assert.a(heapDump.isFile(), "cannot find heap-dump"); 97 | } 98 | else if(args[i].equals("--heap-base-adr")) { 99 | heapBaseAddress = Long.decode(args[i + 1]); 100 | } 101 | else if(args[i].equals("--target")) { 102 | targetExecutablePath = new File(args[i + 1]); 103 | Assert.a(targetExecutablePath.isFile(), "cannot find target exec"); 104 | } 105 | else if(args[i].equals("--rtn")) { 106 | targetRoutineAddress = Long.decode(args[i + 1]); 107 | } 108 | else if(args[i].equals("--log")) { 109 | logPath = new File(args[i + 1]); 110 | Assert.a(logPath.isFile(), "cannot find log file"); 111 | } 112 | else { 113 | logger.i("> ERROR: invalid argument (%s)", args[i]); 114 | usage(); 115 | return; 116 | } 117 | } 118 | if(targetExecutablePath == null || targetRoutineAddress == null) { 119 | logger.i("> ERROR: missing arguments"); 120 | usage(); 121 | return; 122 | } 123 | } 124 | 125 | private static void usage() { 126 | //@formatter:off 127 | logger.i("Usage: " + 128 | "--target path : path to executable file to emulate" + 129 | "--rtn 0xAAAAAAAA : address of first routine to emulate" + 130 | "--log path : path to logfile (optional)" + 131 | "--stack-dump path : path to stack dump file (optional)" + 132 | "--stack-base-adr 0xAAAAAAAA : stack dump base address (optional)" + 133 | "--stack-base-ptr 0xAAAAAAAA : stack base pointer (optional)" + 134 | "--heap-dump path : path to heap dump file (optional)" + 135 | "--heap-base-adr 0xAAAAAAAA : heap dump base address (optional)"); 136 | //@formatter:on 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/com/pnf/plugin/cemulator/MarsAnalyticaCEmulator.java: -------------------------------------------------------------------------------- 1 | package com.pnf.plugin.cemulator; 2 | 3 | import java.util.List; 4 | 5 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICConstantInteger; 6 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICExpression; 7 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICIfStm; 8 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICMethod; 9 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICOperation; 10 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICStatement; 11 | import com.pnfsoftware.jeb.util.format.Strings; 12 | 13 | /** 14 | * MarsAnalytica's crackme specific emulation logic 15 | * 16 | * @author Joan Calvet 17 | * 18 | */ 19 | public class MarsAnalyticaCEmulator extends SimpleCEmulator { 20 | 21 | private char currentChar = 0x61; 22 | 23 | /** Stack machine memory layout */ 24 | private final int CHUNK_SIZE = 16; 25 | private long curFreeChunkAddr = 0x1000000; // arbitrary address 26 | 27 | private int popCounter; 28 | 29 | @Override 30 | protected void initEmulation() { 31 | super.initEmulation(); 32 | popCounter = 0; 33 | defaultLogging = false; // MA emulator does its own logging for stack machine operations 34 | } 35 | 36 | @Override 37 | protected void preEmulateMethodCallback(ICMethod method, EmulatorState inputState) { 38 | if(method.getName().equals("sub_402AB2")) { 39 | outputLog.append(Strings.ff("S: SWAP")); 40 | outputLog.append(Strings.LINESEP); 41 | } 42 | } 43 | 44 | @Override 45 | protected void preEmulateStatementCallback(CFG cfg, ICStatement currentStatement) { 46 | if(popCounter > 0) { 47 | if(currentStatement instanceof ICIfStm) { 48 | ICIfStm ifStm = ((ICIfStm)currentStatement); 49 | // MarsAnalytica ifs are always if/else or if 50 | if(ifStm.getBlocks().size() <= 2) { 51 | 52 | // we log the 'true' predicate 53 | ICOperation pred = (ICOperation)((ICIfStm)currentStatement).getBranchPredicate(0).getExpression() 54 | .duplicate(); 55 | if(evaluateExpression(pred) == 0) { 56 | pred.reverse(method.getOperatorFactory()); 57 | } 58 | if(pred.getSecondOperand() instanceof ICConstantInteger) { 59 | outputLog.append(String.format("S: TEST (%s,cte=%d)", pred.getOperator(), 60 | ((ICConstantInteger)pred.getSecondOperand()).getValueAsLong())); 61 | outputLog.append(Strings.LINESEP); 62 | } 63 | else { 64 | outputLog.append( 65 | Strings.ff("S: TEST (%s,#op=%d)", pred.getOperator(), pred.getCountOfOperands())); 66 | outputLog.append(Strings.LINESEP); 67 | } 68 | popCounter = 0; 69 | } 70 | } 71 | } 72 | return; 73 | } 74 | 75 | @Override 76 | protected Long simulateWellKnownMethods(ICMethod calledMethod, List parameters) { 77 | Long defaultEmulationResult = super.simulateWellKnownMethods(calledMethod, parameters); 78 | if(defaultEmulationResult != null) { 79 | return defaultEmulationResult; 80 | } 81 | 82 | /** MarsAnalytica's specific emulation */ 83 | 84 | /** Inject dummy characters */ 85 | if(calledMethod.getName().equals("→getchar")) { 86 | return (long)currentChar++; 87 | } 88 | if(calledMethod.getName().equals("→putchar")) { 89 | logger.i("putchar"); 90 | return 0L; 91 | } 92 | 93 | /** 94 | * Stack machine handlers 95 | */ 96 | 97 | /** PUSH(STACK_PTR, VALUE) */ 98 | else if(calledMethod.getName().equals("sub_400AAE")) { 99 | Long pStackPtr = evaluateExpression(parameters.get(0)); 100 | Long pValue = evaluateExpression(parameters.get(1)); 101 | 102 | long newChunkAddr = allocateNewChunk(); 103 | 104 | // write value 105 | state.writeMemory(newChunkAddr + 8, pValue, 4); 106 | 107 | // link new chunk to existing stack 108 | Long stackAdr = state.readMemory(pStackPtr, 8); 109 | state.writeMemory(newChunkAddr, stackAdr, 8); 110 | 111 | // make new chunk the new stack head 112 | state.writeMemory(pStackPtr, newChunkAddr, 8); 113 | 114 | outputLog.append(Strings.ff("S: PUSH %d", pValue)); 115 | outputLog.append(Strings.LINESEP); 116 | 117 | if(popCounter == 2) { 118 | // parameters.get(1) is an operation? 119 | ICExpression expr = parameters.get(1); 120 | if(expr instanceof ICOperation) { 121 | // cast + operation 122 | while(expr instanceof ICOperation && ((ICOperation)expr).getOperator().isCast()) { 123 | expr = ((ICOperation)expr).getFirstOperand(); 124 | } 125 | } 126 | if(expr instanceof ICOperation) { 127 | outputLog.append(Strings.ff(" | operation: (%s,#op=%d)", ((ICOperation)expr).getOperator(), 128 | ((ICOperation)expr).getCountOfOperands())); 129 | outputLog.append(Strings.LINESEP); 130 | popCounter = 0; 131 | } 132 | } 133 | 134 | return 0L; 135 | } 136 | /** SET(STACK_PTR, INDEX, VALUE) */ 137 | else if(calledMethod.getName().equals("sub_400D55")) { 138 | Long pStackPtr = evaluateExpression(parameters.get(0)); 139 | Long pIndex = evaluateExpression(parameters.get(1)); 140 | Long pValue = evaluateExpression(parameters.get(2)); 141 | Long retVal = emulateSetElementFromEnd(pStackPtr, pIndex, pValue); 142 | 143 | outputLog.append(Strings.ff("S: SET index:%d value:%d", pIndex, pValue)); 144 | outputLog.append(Strings.LINESEP); 145 | 146 | return retVal; 147 | } 148 | /** GET(STACK_PTR, INDEX) */ 149 | else if(calledMethod.getName().equals("sub_400D08")) { 150 | Long pStackPtr = evaluateExpression(parameters.get(0)); 151 | Long pIndex = evaluateExpression(parameters.get(1)); 152 | Long retVal = emulateGetElementFromEnd(pStackPtr, pIndex); 153 | 154 | outputLog.append(Strings.ff("S: GET index:%d", pIndex)); 155 | outputLog.append(Strings.LINESEP); 156 | 157 | return retVal; 158 | } 159 | /** POP(STACK_PTR) */ 160 | else if(calledMethod.getName().equals("sub_4009D7")) { 161 | Long pStackPtr = evaluateExpression(parameters.get(0)); 162 | Long retVal = emulateUnlink(pStackPtr); 163 | 164 | outputLog.append(Strings.ff("S: POP (%d)", retVal)); 165 | outputLog.append(Strings.LINESEP); 166 | 167 | popCounter++; 168 | 169 | return retVal; 170 | } 171 | else if(calledMethod.getName().equals("sub_402AB2")) { 172 | outputLog.append(Strings.ff("S: SWAP")); 173 | outputLog.append(Strings.LINESEP); 174 | 175 | return 0L; 176 | } 177 | 178 | return null; 179 | } 180 | 181 | 182 | private long allocateNewChunk() { 183 | long freeChunkAddr = curFreeChunkAddr; 184 | curFreeChunkAddr += CHUNK_SIZE; 185 | state.allocateMemory(freeChunkAddr, CHUNK_SIZE); 186 | return freeChunkAddr; 187 | } 188 | 189 | private Long emulateSetElementFromEnd(Long param1, Long param2, Long param3) { 190 | long lastIndex = emulateGetLength(param1) - 1; 191 | long curElement = param1; 192 | for(int i = 0; i != lastIndex - param2; i++) { 193 | curElement = state.readMemory(curElement, 8); 194 | } 195 | state.writeMemory(curElement + 8, param3, 4); 196 | return curElement; 197 | } 198 | 199 | private Long emulateGetElementFromEnd(Long param1, Long param2) { 200 | long lastIndex = emulateGetLength(param1) - 1; 201 | long curElement = param1; 202 | for(int i = 0; i != lastIndex - param2; i++) { 203 | curElement = state.readMemory(curElement, 8); 204 | } 205 | return state.readMemory(curElement + 8, 4); 206 | } 207 | 208 | private Long emulateGetLength(Long param1) { 209 | long length = 0; 210 | long current = param1; 211 | while(current != 0) { 212 | current = state.readMemory(current, 8); 213 | length++; 214 | } 215 | return length; 216 | } 217 | 218 | private Long emulateUnlink(Long param1) { 219 | Long nextChunkAddress = state.readMemory(param1, 8); 220 | Long nextChunkValue = state.readMemory(nextChunkAddress + 8, 4); 221 | Long nextNextChunkAddress = state.readMemory(nextChunkAddress, 8); 222 | state.writeMemory(param1, nextNextChunkAddress, 8); 223 | // note: we do not free memory 224 | return nextChunkValue; 225 | } 226 | 227 | 228 | 229 | } 230 | -------------------------------------------------------------------------------- /src/com/pnf/plugin/cemulator/SimpleCEmulator.java: -------------------------------------------------------------------------------- 1 | package com.pnf.plugin.cemulator; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.List; 6 | 7 | import com.pnfsoftware.jeb.core.exceptions.JebRuntimeException; 8 | import com.pnfsoftware.jeb.core.input.BytesInput; 9 | import com.pnfsoftware.jeb.core.units.AbstractBinaryUnit; 10 | import com.pnfsoftware.jeb.core.units.INativeCodeUnit; 11 | import com.pnfsoftware.jeb.core.units.IUnit; 12 | import com.pnfsoftware.jeb.core.units.WellKnownUnitTypes; 13 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.COperatorType; 14 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICAssignment; 15 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICBlock; 16 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICCall; 17 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICConstantInteger; 18 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICConstantPointer; 19 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICControlBreaker; 20 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICDecl; 21 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICElement; 22 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICExpression; 23 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICGoto; 24 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICIdentifier; 25 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICIfStm; 26 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICJumpFar; 27 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICLabel; 28 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICMethod; 29 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICOperation; 30 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICOperator; 31 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICPredicate; 32 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICReturn; 33 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICStatement; 34 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ast.ICWhileStm; 35 | import com.pnfsoftware.jeb.util.logging.GlobalLog; 36 | import com.pnfsoftware.jeb.util.logging.ILogger; 37 | import com.pnfsoftware.jeb.util.math.MathUtil; 38 | import com.pnfsoftware.jeb.util.format.Strings; 39 | import com.pnfsoftware.jeb.util.io.IO; 40 | 41 | /** 42 | * Simple emulator for {@link ICMethod} (JEB's AST). Originally implemented to be used with 43 | * {@link CEmulatorPlugin}. 44 | *

45 | * Limitations: 46 | *

    47 | *
  • Emulator relies on a minimalist CFG implementation (see {@link CFG}), and hence has the same 48 | * limitations (in particular switch and do-while statements are not emulated) 49 | *
  • Called subroutines are either simulated (ie, reimplemented in Java) or emulated as 50 | * non-retourning routines (see {{@link #evaluateCall(ICCall)}) 51 | *
  • Emulator is tailored for x64 machine code, as it assumes calls' returned values are passed 52 | * through RAX register 53 | *
54 | * 55 | * @author Joan Calvet 56 | * 57 | */ 58 | public class SimpleCEmulator { 59 | protected static final ILogger logger = GlobalLog.getLogger(SimpleCEmulator.class); 60 | 61 | /** IDs for register variables (mirrors) */ 62 | public static final int REG_RAX_ID = -65536; 63 | public static final int REG_RBP_ID = -65856; 64 | public static final int REG_RBX_ID = -65728; 65 | /** synthetic register to store next method to emulate */ 66 | public static final int REG_NEXT_METHOD_ID = 1; 67 | 68 | protected ICMethod method; 69 | protected EmulatorState state; 70 | 71 | protected boolean defaultLogging = true; 72 | protected StringBuilder outputLog = new StringBuilder(); 73 | 74 | private IUnit logUnit; 75 | 76 | private int logSize = 0; 77 | 78 | /** 79 | * Emulate the given method using the given input state. 80 | * 81 | * @param method 82 | * @param inputState 83 | * @return log of the emulation 84 | */ 85 | public EmulatorLog emulate(ICMethod method, EmulatorState inputState) { 86 | preEmulateMethodCallback(method, inputState); 87 | 88 | state = inputState; 89 | this.method = method; 90 | 91 | initEmulation(); 92 | EmulatorLog log = new EmulatorLog(); 93 | 94 | CFG cfg = CFG.buildCFG(method); 95 | ICStatement currentStatement = cfg.getEntryPoint(); 96 | 97 | if(defaultLogging) { 98 | outputLog.append("> emulator trace:"); 99 | outputLog.append(Strings.LINESEP); 100 | } 101 | 102 | while(currentStatement != null) { 103 | log.addExecutedStatement(currentStatement); 104 | 105 | if(defaultLogging) { 106 | outputLog.append(Strings.ff(" %s", currentStatement)); 107 | outputLog.append(Strings.LINESEP); 108 | } 109 | 110 | currentStatement = emulateStatement(cfg, currentStatement); 111 | 112 | // uncomment to see register + memory state 113 | // if(defaultLogging) { 114 | // outputLog.append(Strings.ff(" > output registers: %s", state.toString())); 115 | // outputLog.append(Strings.LINESEP); 116 | // } 117 | } 118 | 119 | log.setEmulatorState(state); 120 | return log; 121 | } 122 | 123 | protected void preEmulateMethodCallback(ICMethod method, EmulatorState inputState) { 124 | // default implementation does nothing - override with specific logic 125 | return; 126 | } 127 | 128 | protected void initEmulation() { 129 | // default implementation does nothing - override with specific logic 130 | return; 131 | } 132 | 133 | protected void preEmulateStatementCallback(CFG cfg, ICStatement currentStatement) { 134 | // default implementation does nothing - override with specific logic 135 | return; 136 | } 137 | 138 | private ICStatement emulateStatement(CFG cfg, ICStatement currentStatement) { 139 | preEmulateStatementCallback(cfg, currentStatement); 140 | 141 | if(currentStatement instanceof ICGoto) { 142 | return cfg.getNextStatement(currentStatement); 143 | } 144 | else if(currentStatement instanceof ICLabel) { 145 | return cfg.getNextStatement(currentStatement); 146 | } 147 | else if(currentStatement instanceof ICReturn) { 148 | ICExpression retExpression = ((ICReturn)currentStatement).getExpression(); 149 | if(retExpression != null) { 150 | // note: would need to check calling convention 151 | state.setRegisterValue(REG_RAX_ID, 152 | evaluateExpression(retExpression)); 153 | } 154 | return cfg.getNextStatement(currentStatement); 155 | } 156 | else if(currentStatement instanceof ICAssignment) { 157 | evaluateAssignment((ICAssignment)currentStatement); 158 | return cfg.getNextStatement(currentStatement); 159 | } 160 | else if(currentStatement instanceof ICIfStm) { 161 | ICIfStm ifStm = (ICIfStm)currentStatement; 162 | List predicates = ifStm.getBranchPredicates(); 163 | for(int i = 0; i < predicates.size(); i++) { 164 | if(evaluateExpression(predicates.get(i)) != 0) { 165 | return cfg.getNthNextStatement(currentStatement, i); 166 | } 167 | } 168 | // ...or go to else block if present (last conditional target) 169 | if(ifStm.hasDefaultBlock()) { 170 | return cfg.getNthNextStatement(currentStatement, predicates.size()); 171 | } 172 | // ...or to fallthrough 173 | return cfg.getNextStatement(currentStatement); 174 | } 175 | else if(currentStatement instanceof ICWhileStm) { 176 | ICWhileStm wStm = (ICWhileStm)currentStatement; 177 | if(evaluateExpression(wStm.getPredicate()) != 0) { 178 | return cfg.getNextTrueStatement(wStm); 179 | } 180 | else { 181 | return cfg.getNextStatement(wStm); 182 | } 183 | } 184 | else if(currentStatement instanceof ICBlock) { 185 | return cfg.getNextStatement(currentStatement); 186 | } 187 | else if(currentStatement instanceof ICControlBreaker) { 188 | return cfg.getNextStatement(currentStatement); 189 | } 190 | else if(currentStatement instanceof ICDecl) { 191 | return cfg.getNextStatement(currentStatement); 192 | } 193 | else if(currentStatement instanceof ICCall) { 194 | evaluateCall((ICCall)currentStatement); 195 | return cfg.getNextStatement(currentStatement); 196 | } 197 | else if(currentStatement instanceof ICJumpFar) { 198 | long targetAddr = evaluateExpression(((ICJumpFar)currentStatement).getJumpsite()); 199 | state.setRegisterValue(REG_NEXT_METHOD_ID, targetAddr); 200 | return cfg.getNextStatement(currentStatement); 201 | } 202 | else { 203 | throw new EmulatorException( 204 | Strings.ff("ERROR: unimplemented statement emulation (%s)", currentStatement)); 205 | } 206 | } 207 | 208 | private void evaluateCall(ICCall ccall) { 209 | if(ccall.getMethod() != null) { // resolved calls 210 | Long returnValue = simulateWellKnownMethods(ccall.getMethod(), 211 | ccall.getArguments()); 212 | if(returnValue == null) { 213 | // simulation failed, we need to emulate callee 214 | throw new EmulatorException( 215 | Strings.ff("ERROR: unimplemented recursive emulation (%s)", ccall)); 216 | } 217 | else { 218 | state.setRegisterValue(REG_RAX_ID, returnValue); 219 | } 220 | } 221 | else { // calls whose target is not resolved yet 222 | // here we need to go emulate target, and we assume such call is non-returning 223 | Long nextHandlerAddr = evaluateExpression(ccall.getCallsite()); 224 | if(nextHandlerAddr == null) { 225 | throw new EmulatorException(Strings.ff("ERROR: cannot resolve target (%s)", ccall)); 226 | } 227 | else { 228 | state.setRegisterValue(REG_NEXT_METHOD_ID, nextHandlerAddr); 229 | } 230 | } 231 | } 232 | 233 | private void evaluateAssignment(ICAssignment assign) { 234 | if(assign.isSimpleAssignment()) { 235 | // right hand side eval 236 | Long rightValue = evaluateExpression(assign.getRight()); 237 | if(rightValue == null){ 238 | throw new EmulatorException(Strings.ff("right value evaluation (%s)", assign)); 239 | } 240 | // left hand side eval 241 | ICExpression leftDerefExpr = getDereferencedExpression(assign.getLeft()); 242 | if(leftDerefExpr != null) { 243 | Long leftExprValue = evaluateExpression(leftDerefExpr); 244 | if(leftExprValue != null) { 245 | // memory access 246 | if(((ICOperation)assign.getLeft()).getFirstOperand() instanceof ICOperation) { 247 | ICOperation leftFirstOperand = (ICOperation)(((ICOperation)assign.getLeft()).getFirstOperand()); 248 | if(leftFirstOperand.getOperator().isCast()) { 249 | int derefCastSize = state 250 | .getBaseTypeSize(((ICOperation)leftFirstOperand).getOperator().getCastType()); 251 | state.writeMemory(leftExprValue, rightValue, derefCastSize); 252 | } 253 | else { 254 | state.writeMemory(leftExprValue, rightValue, 8); 255 | } 256 | } 257 | } 258 | } 259 | else { 260 | // identifier (possibly within a definition) 261 | state.setVarValue(assign.getLeft(), rightValue); 262 | } 263 | } 264 | else { 265 | throw new EmulatorException("ERROR: not implemented: non simple assignments"); 266 | } 267 | } 268 | 269 | private ICExpression getDereferencedExpression(ICElement element) { 270 | if(element instanceof ICOperation) { 271 | if(((ICOperation)element).getOperatorType() == COperatorType.PTR) { 272 | return ((ICOperation)element).getFirstOperand(); 273 | } 274 | } 275 | return null; 276 | } 277 | 278 | protected Long evaluateExpression(ICExpression expr) { 279 | if(expr instanceof ICConstantInteger) { 280 | return ((ICConstantInteger)expr).getValueAsLong(); 281 | } 282 | else if(expr instanceof ICConstantPointer) { 283 | return ((ICConstantPointer)expr).getValue(); 284 | } 285 | else if(expr instanceof ICOperation) { 286 | return evaluateOperation((ICOperation)expr); 287 | } 288 | else if(expr instanceof ICIdentifier) { 289 | Long varValue = state.getVarValue((ICIdentifier)expr); 290 | if(varValue == null) { 291 | logger.info("> warning: non initialized identifier (%s) -- defining it to 0L", expr); 292 | varValue = 0L; 293 | } 294 | return varValue; 295 | } 296 | else if(expr instanceof ICPredicate) { 297 | return evaluateExpression(((ICPredicate)expr).getExpression()) != 0 ? 1L: 0L; 298 | } 299 | else if(expr instanceof ICCall) { 300 | evaluateCall(((ICCall)expr)); 301 | return state.getRegisterValue(REG_RAX_ID); 302 | } 303 | else { 304 | throw new EmulatorException(Strings.ff("ERROR: unimplemented expression eval (%s)", expr)); 305 | } 306 | } 307 | 308 | /** 309 | * Simulate well known methods; ie, rather than emulating their actual code, we simulate it in 310 | * Java. 311 | * 312 | * @return method return value, null if failure 313 | */ 314 | protected Long simulateWellKnownMethods(ICMethod calledMethod, 315 | List parameters) { 316 | /** 317 | * libc APIs 318 | */ 319 | if(calledMethod.getName().equals("→time")) { 320 | return 42L; 321 | } 322 | else if(calledMethod.getName().equals("→srand")) { 323 | return 37L; 324 | } 325 | else if(calledMethod.getName().equals("→memcpy")) { 326 | ICExpression dst = parameters.get(0); 327 | ICExpression src = parameters.get(1); 328 | ICExpression n = parameters.get(2); 329 | if(src instanceof ICOperation && ((ICOperation)src).checkOperatorType(COperatorType.CAST) 330 | && n instanceof ICConstantInteger) { 331 | long src_ = ((ICConstantPointer)((ICOperation)src).getFirstOperand()).getValue(); 332 | int n_ = (int)((ICConstantInteger)n).getValueAsLong(); 333 | long dst_ = evaluateExpression(dst); 334 | state.copyMemory(src_, dst_, n_); 335 | return dst_; 336 | } 337 | else { 338 | throw new EmulatorException("TBI: memcpy"); 339 | } 340 | } 341 | 342 | 343 | return null; 344 | } 345 | 346 | private Long evaluateOperation(ICOperation operation) { 347 | Long value = null; 348 | 349 | ICExpression opnd1 = operation.getFirstOperand(); 350 | ICExpression opnd2 = operation.getSecondOperand(); 351 | ICExpression opnd3 = operation.getThirdOperand(); 352 | ICOperator operator = operation.getOperator(); 353 | 354 | switch(operator.getType()) { 355 | case ADD: 356 | value = evaluateExpression(opnd1) + evaluateExpression(opnd2); 357 | break; 358 | case AND: 359 | value = evaluateExpression(opnd1) & evaluateExpression(opnd2); 360 | break; 361 | case CAST: 362 | int castSize = state.getTypeSize(operator.getCastType()); 363 | long castOperand = MathUtil.makeMask(castSize * 8); 364 | value = evaluateExpression(opnd1) & castOperand; 365 | break; 366 | case COND: 367 | value = evaluateExpression(opnd1) != 0 ? evaluateExpression(opnd2): evaluateExpression(opnd3); 368 | break; 369 | case CUSTOM: 370 | break; 371 | case DIV: 372 | value = evaluateExpression(opnd1) / evaluateExpression(opnd2); 373 | break; 374 | case EQ: 375 | value = evaluateExpression(opnd1).equals(evaluateExpression(opnd2)) ? 1L: 0L; 376 | break; 377 | case GE: 378 | value = evaluateExpression(opnd1) >= evaluateExpression(opnd2) ? 1L: 0L; 379 | break; 380 | case GT: 381 | value = evaluateExpression(opnd1) > evaluateExpression(opnd2) ? 1L: 0L; 382 | break; 383 | case LE: 384 | value = evaluateExpression(opnd1) <= evaluateExpression(opnd2) ? 1L: 0L; 385 | break; 386 | case LOG_AND: 387 | value = (evaluateExpression(opnd1) != 0 && evaluateExpression(opnd2) != 0) ? 1L: 0L; 388 | break; 389 | case LOG_IDENT: 390 | value = evaluateExpression(opnd1); 391 | break; 392 | case LOG_NOT: 393 | value = evaluateExpression(opnd1) != 0 ? 0L: 1L; 394 | break; 395 | case LOG_OR: 396 | value = (evaluateExpression(opnd1) != 0 || evaluateExpression(opnd2) != 0) ? 1L: 0L; 397 | break; 398 | case LT: 399 | value = evaluateExpression(opnd1) < evaluateExpression(opnd2) ? 1L: 0L; 400 | break; 401 | case MUL: 402 | value = evaluateExpression(opnd1) * evaluateExpression(opnd2); 403 | break; 404 | case NE: 405 | value = evaluateExpression(opnd1) != evaluateExpression(opnd2) ? 1L: 0L; 406 | break; 407 | case NEG: 408 | value = -evaluateExpression(opnd1); 409 | break; 410 | case NOT: 411 | value = ~evaluateExpression(opnd1); 412 | break; 413 | case OR: 414 | value = evaluateExpression(opnd1) | evaluateExpression(opnd2); 415 | break; 416 | case PTR: 417 | if(opnd1 instanceof ICIdentifier) { 418 | value = state.readMemorySafe(evaluateExpression(opnd1), 419 | state.getBaseTypeSize(((ICIdentifier)opnd1).getType())); 420 | } 421 | else if(opnd1 instanceof ICOperation) { 422 | ICIdentifier basePointer = getBasePointer((ICOperation)opnd1); 423 | if(((ICOperation)opnd1).getOperator().isCast()) { 424 | int derefCastSize = state 425 | .getBaseTypeSize(((ICOperation)opnd1).getOperator().getCastType()); 426 | value = state.readMemorySafe( 427 | evaluateExpression(((ICExpression)((ICOperation)opnd1).getFirstOperand())), 428 | derefCastSize); 429 | value = MathUtil.signExtend(value, derefCastSize * 8); 430 | } 431 | else if(basePointer != null) { 432 | value = state.readMemorySafe(evaluateExpression(opnd1), 433 | state.getBaseTypeSize(basePointer.getType())); 434 | } 435 | else { 436 | if(state.getDefaultPointerSize() != null) { 437 | value = state.readMemorySafe(evaluateExpression(opnd1), 438 | state.getDefaultPointerSize()); 439 | } 440 | else { 441 | throw new EmulatorException("cant find size to read for PTR operation"); 442 | } 443 | } 444 | } 445 | else if(opnd1 instanceof ICConstantInteger) { 446 | logger.info("> warning: read with fixed size (%d) at address %x", 447 | state.getDefaultPointerSize(), 448 | ((ICConstantInteger)opnd1).getValueAsLong()); 449 | value = state.readMemorySafe(((ICConstantInteger)opnd1).getValueAsLong(), 450 | state.getDefaultPointerSize()); 451 | } 452 | else { 453 | throw new EmulatorException(Strings.ff("PTR invalid (%s)", opnd1)); 454 | } 455 | break; 456 | case REF: 457 | if(!(opnd1 instanceof ICIdentifier)) { 458 | throw new EmulatorException(Strings.ff("REF on non id (%s)", opnd1)); 459 | } 460 | value = state.getVarAddress((ICIdentifier)opnd1); 461 | break; 462 | case REM: 463 | value = evaluateExpression(opnd1) % evaluateExpression(opnd2); 464 | break; 465 | case SHL: 466 | value = evaluateExpression(opnd1) << evaluateExpression(opnd2); 467 | break; 468 | case SHR: 469 | value = evaluateExpression(opnd1) >> evaluateExpression(opnd2); 470 | break; 471 | case SIZEOF: 472 | break; 473 | case SUB: 474 | value = evaluateExpression(opnd1) - evaluateExpression(opnd2); 475 | break; 476 | case USHR: 477 | value = evaluateExpression(opnd1) >>> evaluateExpression(opnd2); 478 | break; 479 | case XOR: 480 | value = evaluateExpression(opnd1) ^ evaluateExpression(opnd2); 481 | break; 482 | default: 483 | break; 484 | } 485 | if(value == null) { 486 | throw new EmulatorException(Strings.ff("TBI: operator (%s)", operator)); 487 | } 488 | return value; 489 | } 490 | 491 | /** 492 | * Get the base pointer in a pointer arithmetic operation. Simple check for the first identifier 493 | * in the operation. 494 | * 495 | * @param operation 496 | * @return base pointer, null if cannot be found 497 | */ 498 | public ICIdentifier getBasePointer(ICOperation operation) { 499 | ICIdentifier basePointer = null; 500 | if(operation.getFirstOperand() instanceof ICIdentifier) { 501 | basePointer = (ICIdentifier)operation.getFirstOperand(); 502 | } 503 | else if(operation.getSecondOperand() instanceof ICIdentifier) { 504 | basePointer = (ICIdentifier)operation.getSecondOperand(); 505 | } 506 | else if(operation.getThirdOperand() instanceof ICIdentifier) { 507 | basePointer = (ICIdentifier)operation.getThirdOperand(); 508 | } 509 | return basePointer; 510 | } 511 | 512 | public void dumpLog(File logFile) { 513 | if(logFile != null) { 514 | if(outputLog.length() != logSize) { // if something new... 515 | try { 516 | IO.writeFile(logFile, Strings.encodeUTF8(outputLog.toString())); 517 | logSize = outputLog.length(); 518 | } 519 | catch(IOException e) { 520 | throw new JebRuntimeException("failed to write log file"); 521 | } 522 | } 523 | } 524 | else { 525 | // dump as text unit 526 | INativeCodeUnit codeUnit = state.getNativeCodeUnit(); 527 | if(codeUnit != null && codeUnit.getCodeObjectContainer() != null) { 528 | if(logUnit == null) { 529 | logUnit = codeUnit.getUnitProcessor().process("C emulator log", 530 | new BytesInput(Strings.encodeUTF8(outputLog.toString())), codeUnit.getCodeObjectContainer(), 531 | WellKnownUnitTypes.typeGeneric); 532 | codeUnit.getCodeObjectContainer().addChild(logUnit); 533 | } 534 | else { 535 | ((AbstractBinaryUnit)logUnit).setInput(new BytesInput(Strings.encodeUTF8(outputLog.toString()))); 536 | } 537 | } 538 | } 539 | } 540 | 541 | } 542 | --------------------------------------------------------------------------------