├── .gitignore ├── Pipfile ├── Pipfile.lock ├── driver_analysis.py └── simulation ├── __init__.py ├── engines.py ├── jump_resolvers.py ├── procedures ├── __init__.py ├── instructions.py └── windows_kernel.py ├── project.py ├── pyvex_lifters.py └── structures.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.json 2 | *.py[co] 3 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | angr = "*" 8 | archinfo = "*" 9 | claripy = "*" 10 | smoke-zephyr = "*" 11 | ipdb = "*" 12 | ipython = "*" 13 | pyvex = "*" 14 | boltons = "*" 15 | tabulate = "*" 16 | idalink = "==0.11" 17 | 18 | [dev-packages] 19 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "cd007efbd2aa2079b706797de2c4acd4b39b8deadd83f45ef9541d9fc6038d67" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": {}, 8 | "sources": [ 9 | { 10 | "name": "pypi", 11 | "url": "https://pypi.org/simple", 12 | "verify_ssl": true 13 | } 14 | ] 15 | }, 16 | "default": { 17 | "ailment": { 18 | "hashes": [ 19 | "sha256:4552730c368a241ba10fe132ca265080098ff293dd129072ed7b0cf154b81f76" 20 | ], 21 | "version": "==8.19.4.5" 22 | }, 23 | "ana": { 24 | "hashes": [ 25 | "sha256:5fa16e511773a0efc6ac9294f93eee583612ffb580859737eed07e703947d6f8" 26 | ], 27 | "version": "==0.6" 28 | }, 29 | "angr": { 30 | "hashes": [ 31 | "sha256:386efa0b110883059e5bf195fea25223a859d42075e60582ed95bb64ad7086fa", 32 | "sha256:3b24763494f85d1af600eb39fae6e42f0fa4583dfd8f17c069c62c6bda5e9a56", 33 | "sha256:87f271fe749395cb772dda135e5e458db8d9cc49d724597e0e0f7c70d2a96380", 34 | "sha256:b3bbcbc0fc140146f076c087d4efc8681ac579e46251d5ff8b086249a3fddf1c", 35 | "sha256:d8c94e27dc1a017e5fe39034b8f6af53527fbf44509a81b09dd93f79af56a2f7" 36 | ], 37 | "index": "pypi", 38 | "version": "==8.19.4.5" 39 | }, 40 | "archinfo": { 41 | "hashes": [ 42 | "sha256:97e80aacc8ae7dcd3e5cdb932ef97cbb4b55014be7052e05f2a95cc3553734be" 43 | ], 44 | "index": "pypi", 45 | "version": "==8.19.4.5" 46 | }, 47 | "backcall": { 48 | "hashes": [ 49 | "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", 50 | "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2" 51 | ], 52 | "version": "==0.1.0" 53 | }, 54 | "bitstring": { 55 | "hashes": [ 56 | "sha256:c163a86fcef377c314690051885d86b47419e3e1770990c212e16723c1c08faa" 57 | ], 58 | "version": "==3.1.5" 59 | }, 60 | "boltons": { 61 | "hashes": [ 62 | "sha256:7aa10b0f5b015678458a7d0422961fc0c7e823c05e644094c0e564931ce0b0df", 63 | "sha256:c32b2d121331a9bc7c220050d4273f3aa359b7569cb4794188e71524603113dc" 64 | ], 65 | "index": "pypi", 66 | "version": "==19.1.0" 67 | }, 68 | "cachetools": { 69 | "hashes": [ 70 | "sha256:428266a1c0d36dc5aca63a2d7c5942e88c2c898d72139fca0e97fdd2380517ae", 71 | "sha256:8ea2d3ce97850f31e4a08b0e2b5e6c34997d7216a9d2c98e0f3978630d4da69a" 72 | ], 73 | "version": "==3.1.1" 74 | }, 75 | "capstone": { 76 | "hashes": [ 77 | "sha256:5857accc0de1e769b0ec0a0ca985715bfa96e5a66a2ebb3aaed43a8e3655377f", 78 | "sha256:5d97df74a8ffdd02706bc67155b7d41dd3ca8ff2caa034f8e54b99044b96037e", 79 | "sha256:a85763eccae63c3b7a1123a17afcda03303771b18db38a44453055ff360edd94", 80 | "sha256:dae578f97b24212fb97a5c833342e56c69e7fb71502187a0b51a4326381e4204", 81 | "sha256:ee7f55aa4326b1e345b1fb28d1b0798974503e914b006cc9806431e8c3e95549" 82 | ], 83 | "version": "==4.0.1" 84 | }, 85 | "cffi": { 86 | "hashes": [ 87 | "sha256:041c81822e9f84b1d9c401182e174996f0bae9991f33725d059b771744290774", 88 | "sha256:046ef9a22f5d3eed06334d01b1e836977eeef500d9b78e9ef693f9380ad0b83d", 89 | "sha256:066bc4c7895c91812eff46f4b1c285220947d4aa46fa0a2651ff85f2afae9c90", 90 | "sha256:066c7ff148ae33040c01058662d6752fd73fbc8e64787229ea8498c7d7f4041b", 91 | "sha256:2444d0c61f03dcd26dbf7600cf64354376ee579acad77aef459e34efcb438c63", 92 | "sha256:300832850b8f7967e278870c5d51e3819b9aad8f0a2c8dbe39ab11f119237f45", 93 | "sha256:34c77afe85b6b9e967bd8154e3855e847b70ca42043db6ad17f26899a3df1b25", 94 | "sha256:46de5fa00f7ac09f020729148ff632819649b3e05a007d286242c4882f7b1dc3", 95 | "sha256:4aa8ee7ba27c472d429b980c51e714a24f47ca296d53f4d7868075b175866f4b", 96 | "sha256:4d0004eb4351e35ed950c14c11e734182591465a33e960a4ab5e8d4f04d72647", 97 | "sha256:4e3d3f31a1e202b0f5a35ba3bc4eb41e2fc2b11c1eff38b362de710bcffb5016", 98 | "sha256:50bec6d35e6b1aaeb17f7c4e2b9374ebf95a8975d57863546fa83e8d31bdb8c4", 99 | "sha256:55cad9a6df1e2a1d62063f79d0881a414a906a6962bc160ac968cc03ed3efcfb", 100 | "sha256:5662ad4e4e84f1eaa8efce5da695c5d2e229c563f9d5ce5b0113f71321bcf753", 101 | "sha256:59b4dc008f98fc6ee2bb4fd7fc786a8d70000d058c2bbe2698275bc53a8d3fa7", 102 | "sha256:73e1ffefe05e4ccd7bcea61af76f36077b914f92b76f95ccf00b0c1b9186f3f9", 103 | "sha256:a1f0fd46eba2d71ce1589f7e50a9e2ffaeb739fb2c11e8192aa2b45d5f6cc41f", 104 | "sha256:a2e85dc204556657661051ff4bab75a84e968669765c8a2cd425918699c3d0e8", 105 | "sha256:a5457d47dfff24882a21492e5815f891c0ca35fefae8aa742c6c263dac16ef1f", 106 | "sha256:a8dccd61d52a8dae4a825cdbb7735da530179fea472903eb871a5513b5abbfdc", 107 | "sha256:ae61af521ed676cf16ae94f30fe202781a38d7178b6b4ab622e4eec8cefaff42", 108 | "sha256:b012a5edb48288f77a63dba0840c92d0504aa215612da4541b7b42d849bc83a3", 109 | "sha256:d2c5cfa536227f57f97c92ac30c8109688ace8fa4ac086d19d0af47d134e2909", 110 | "sha256:d42b5796e20aacc9d15e66befb7a345454eef794fdb0737d1af593447c6c8f45", 111 | "sha256:dee54f5d30d775f525894d67b1495625dd9322945e7fee00731952e0368ff42d", 112 | "sha256:e070535507bd6aa07124258171be2ee8dfc19119c28ca94c9dfb7efd23564512", 113 | "sha256:e1ff2748c84d97b065cc95429814cdba39bcbd77c9c85c89344b317dc0d9cbff", 114 | "sha256:ed851c75d1e0e043cbf5ca9a8e1b13c4c90f3fbd863dacb01c0808e2b5204201" 115 | ], 116 | "version": "==1.12.3" 117 | }, 118 | "claripy": { 119 | "hashes": [ 120 | "sha256:22fcb0821a15869d3a4e26976cf73af1180d75eb0614b51769f4630cf66184dd" 121 | ], 122 | "index": "pypi", 123 | "version": "==8.19.4.5" 124 | }, 125 | "cle": { 126 | "hashes": [ 127 | "sha256:bea0028a02bd4fcc8d9781114a5ecb306d904d666ea945c15320c1b0b458fedd" 128 | ], 129 | "version": "==8.19.4.5" 130 | }, 131 | "cooldict": { 132 | "hashes": [ 133 | "sha256:c43c9e0b4ad4e21ec8efae9f10e640c3785ac1a69ed32db9ba7f92cf52a91159" 134 | ], 135 | "version": "==1.4" 136 | }, 137 | "decorator": { 138 | "hashes": [ 139 | "sha256:86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de", 140 | "sha256:f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6" 141 | ], 142 | "version": "==4.4.0" 143 | }, 144 | "dpkt": { 145 | "hashes": [ 146 | "sha256:472c8fbf992f913cee2760fed8fb4253a600d7fb2210c793e2c0c0c23eb3b629", 147 | "sha256:52a92ecd5ca04d5bd852bb11cb2eac4bbe38b42a7c472e0d950eeb9f82a81e54" 148 | ], 149 | "version": "==1.9.2" 150 | }, 151 | "future": { 152 | "hashes": [ 153 | "sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8" 154 | ], 155 | "version": "==0.17.1" 156 | }, 157 | "gitdb2": { 158 | "hashes": [ 159 | "sha256:83361131a1836661a155172932a13c08bda2db3674e4caa32368aa6eb02f38c2", 160 | "sha256:e3a0141c5f2a3f635c7209d56c496ebe1ad35da82fe4d3ec4aaa36278d70648a" 161 | ], 162 | "version": "==2.0.5" 163 | }, 164 | "gitpython": { 165 | "hashes": [ 166 | "sha256:563221e5a44369c6b79172f455584c9ebbb122a13368cc82cb4b5addff788f82", 167 | "sha256:8237dc5bfd6f1366abeee5624111b9d6879393d84745a507de0fda86043b65a8" 168 | ], 169 | "version": "==2.1.11" 170 | }, 171 | "idalink": { 172 | "hashes": [ 173 | "sha256:1f745fe7055ca3560187cbc6c855009a2f4a1ae874c10b4c071b0f4eb162314f" 174 | ], 175 | "index": "pypi", 176 | "version": "==0.11" 177 | }, 178 | "ipdb": { 179 | "hashes": [ 180 | "sha256:dce2112557edfe759742ca2d0fee35c59c97b0cc7a05398b791079d78f1519ce" 181 | ], 182 | "index": "pypi", 183 | "version": "==0.12" 184 | }, 185 | "ipython": { 186 | "hashes": [ 187 | "sha256:54c5a8aa1eadd269ac210b96923688ccf01ebb2d0f21c18c3c717909583579a8", 188 | "sha256:e840810029224b56cd0d9e7719dc3b39cf84d577f8ac686547c8ba7a06eeab26" 189 | ], 190 | "index": "pypi", 191 | "version": "==7.5.0" 192 | }, 193 | "ipython-genutils": { 194 | "hashes": [ 195 | "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", 196 | "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8" 197 | ], 198 | "version": "==0.2.0" 199 | }, 200 | "itanium-demangler": { 201 | "hashes": [ 202 | "sha256:bca0fae4cb0ad582a3c7ede97d66b325d13445fdf87c28947233d8559430967c" 203 | ], 204 | "version": "==1.0" 205 | }, 206 | "jedi": { 207 | "hashes": [ 208 | "sha256:49ccb782651bb6f7009810d17a3316f8867dde31654c750506970742e18b553d", 209 | "sha256:79d0f6595f3846dffcbe667cc6dc821b96e5baa8add125176c31a3917eb19d58" 210 | ], 211 | "version": "==0.14.0" 212 | }, 213 | "mulpyplexer": { 214 | "hashes": [ 215 | "sha256:ca930b229e21fe0ed259788e2d871eb13e54d17e29d7f25347573ae87768c5fe" 216 | ], 217 | "version": "==0.8" 218 | }, 219 | "networkx": { 220 | "hashes": [ 221 | "sha256:8311ddef63cf5c5c5e7c1d0212dd141d9a1fe3f474915281b73597ed5f1d4e3d" 222 | ], 223 | "version": "==2.3" 224 | }, 225 | "parso": { 226 | "hashes": [ 227 | "sha256:5052bb33be034cba784193e74b1cde6ebf29ae8b8c1e4ad94df0c4209bfc4826", 228 | "sha256:db5881df1643bf3e66c097bfd8935cf03eae73f4cb61ae4433c9ea4fb6613446" 229 | ], 230 | "version": "==0.5.0" 231 | }, 232 | "pefile": { 233 | "hashes": [ 234 | "sha256:a5d6e8305c6b210849b47a6174ddf9c452b2888340b8177874b862ba6c207645" 235 | ], 236 | "version": "==2019.4.18" 237 | }, 238 | "pexpect": { 239 | "hashes": [ 240 | "sha256:2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", 241 | "sha256:9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb" 242 | ], 243 | "markers": "sys_platform != 'win32'", 244 | "version": "==4.7.0" 245 | }, 246 | "pickleshare": { 247 | "hashes": [ 248 | "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", 249 | "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56" 250 | ], 251 | "version": "==0.7.5" 252 | }, 253 | "plumbum": { 254 | "hashes": [ 255 | "sha256:d143f079bfb60b11e9bec09a49695ce2e55ce5ca0246877bdb0818ab7c7fc312", 256 | "sha256:df96a5facf621db4a6d682bdc93afa5ed6b107a8667c73c3f0a0f0fab4217c81" 257 | ], 258 | "version": "==1.6.7" 259 | }, 260 | "progressbar": { 261 | "hashes": [ 262 | "sha256:5d81cb529da2e223b53962afd6c8ca0f05c6670e40309a7219eacc36af9b6c63" 263 | ], 264 | "version": "==2.5" 265 | }, 266 | "prompt-toolkit": { 267 | "hashes": [ 268 | "sha256:11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780", 269 | "sha256:2519ad1d8038fd5fc8e770362237ad0364d16a7650fb5724af6997ed5515e3c1", 270 | "sha256:977c6583ae813a37dc1c2e1b715892461fcbdaa57f6fc62f33a528c4886c8f55" 271 | ], 272 | "version": "==2.0.9" 273 | }, 274 | "psutil": { 275 | "hashes": [ 276 | "sha256:028a1ec3c6197eadd11e7b46e8cc2f0720dc18ac6d7aabdb8e8c0d6c9704f000", 277 | "sha256:503e4b20fa9d3342bcf58191bbc20a4a5ef79ca7df8972e6197cc14c5513e73d", 278 | "sha256:863a85c1c0a5103a12c05a35e59d336e1d665747e531256e061213e2e90f63f3", 279 | "sha256:954f782608bfef9ae9f78e660e065bd8ffcfaea780f9f2c8a133bb7cb9e826d7", 280 | "sha256:b6e08f965a305cd84c2d07409bc16fbef4417d67b70c53b299116c5b895e3f45", 281 | "sha256:bc96d437dfbb8865fc8828cf363450001cb04056bbdcdd6fc152c436c8a74c61", 282 | "sha256:cf49178021075d47c61c03c0229ac0c60d5e2830f8cab19e2d88e579b18cdb76", 283 | "sha256:d5350cb66690915d60f8b233180f1e49938756fb2d501c93c44f8fb5b970cc63", 284 | "sha256:eba238cf1989dfff7d483c029acb0ac4fcbfc15de295d682901f0e2497e6781a" 285 | ], 286 | "version": "==5.6.3" 287 | }, 288 | "ptyprocess": { 289 | "hashes": [ 290 | "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", 291 | "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f" 292 | ], 293 | "version": "==0.6.0" 294 | }, 295 | "pycparser": { 296 | "hashes": [ 297 | "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3" 298 | ], 299 | "version": "==2.19" 300 | }, 301 | "pyelftools": { 302 | "hashes": [ 303 | "sha256:89c6da6f56280c37a5ff33468591ba9a124e17d71fe42de971818cbff46c1b24" 304 | ], 305 | "version": "==0.25" 306 | }, 307 | "pygments": { 308 | "hashes": [ 309 | "sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", 310 | "sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297" 311 | ], 312 | "version": "==2.4.2" 313 | }, 314 | "pysmt": { 315 | "hashes": [ 316 | "sha256:6ccac89f22052f0b12f3847382efe94d0fbda95f33978af29f4f3aee5ef0e270" 317 | ], 318 | "version": "==0.8.0" 319 | }, 320 | "pyvex": { 321 | "hashes": [ 322 | "sha256:39ede1025194ffdf29de7e839d0c216756c4d4b57c3e61239783035e4fce892d", 323 | "sha256:67292ba8b2f4e8da31adb3728d29c19ab23730c8cd40c0cafce3d5267426c2b0", 324 | "sha256:a40d8f1041e428b7dbce969b1a4d3496d079282daeb3b19c95b01e8156a76934", 325 | "sha256:c9675e1c6d799aa1901122ed84809031ba9c9db6c9f7279f860ea39df86a1bf8", 326 | "sha256:ee5f3529f232035e0a81176c598b241b5425be29fd6b6bb262feeeb27cad41de" 327 | ], 328 | "index": "pypi", 329 | "version": "==8.19.4.5" 330 | }, 331 | "rpyc": { 332 | "hashes": [ 333 | "sha256:05e704c0502932571960812f5cfaac3ae69149edcd96ce4644961c170404e9df", 334 | "sha256:9dca0b761948c603adbcf8d0d84f82435ec040f7df54243d88d304ce2b9f1bf9", 335 | "sha256:aa361fd5f2a95560571777069cca9691701e2e5676eee07dbc0fe97f995b0109" 336 | ], 337 | "version": "==4.1.0" 338 | }, 339 | "six": { 340 | "hashes": [ 341 | "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", 342 | "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" 343 | ], 344 | "version": "==1.12.0" 345 | }, 346 | "smmap2": { 347 | "hashes": [ 348 | "sha256:0555a7bf4df71d1ef4218e4807bbf9b201f910174e6e08af2e138d4e517b4dde", 349 | "sha256:29a9ffa0497e7f2be94ca0ed1ca1aa3cd4cf25a1f6b4f5f87f74b46ed91d609a" 350 | ], 351 | "version": "==2.0.5" 352 | }, 353 | "smoke-zephyr": { 354 | "hashes": [ 355 | "sha256:3dc79db4639cf357fada50fc063ea1b0578939712b6491904047997fbb320d5c" 356 | ], 357 | "index": "pypi", 358 | "version": "==1.4.1" 359 | }, 360 | "sortedcontainers": { 361 | "hashes": [ 362 | "sha256:974e9a32f56b17c1bac2aebd9dcf197f3eb9cd30553c5852a3187ad162e1a03a", 363 | "sha256:d9e96492dd51fae31e60837736b38fe42a187b5404c16606ff7ee7cd582d4c60" 364 | ], 365 | "version": "==2.1.0" 366 | }, 367 | "tabulate": { 368 | "hashes": [ 369 | "sha256:8af07a39377cee1103a5c8b3330a421c2d99b9141e9cc5ddd2e3263fea416943" 370 | ], 371 | "index": "pypi", 372 | "version": "==0.8.3" 373 | }, 374 | "traitlets": { 375 | "hashes": [ 376 | "sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835", 377 | "sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9" 378 | ], 379 | "version": "==4.3.2" 380 | }, 381 | "unicorn": { 382 | "hashes": [ 383 | "sha256:2a0a43ebf73da8e79e91e89b4e72ab2413a410167db4dff25008721ec117cda1", 384 | "sha256:3a8ad7a7f4be7583e77ca2f7f921a26081eb4987a37afbd8c91850eb6a8a673c", 385 | "sha256:66bada80960b2d7da45408acd10d2ea8fdf2c51781543d4e9401d10480b6a574", 386 | "sha256:6f25eef8119620d54cf17472be7fbd3566143a72ff5ff5a6e3639171e026ab28", 387 | "sha256:7e827ac975f5f1ad0022009df22c6af7db7e5229e7878835ee059d4dc16217c4" 388 | ], 389 | "version": "==1.0.1" 390 | }, 391 | "wcwidth": { 392 | "hashes": [ 393 | "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", 394 | "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c" 395 | ], 396 | "version": "==0.1.7" 397 | }, 398 | "z3-solver": { 399 | "hashes": [ 400 | "sha256:474a22a1c6b26a89fc0fe563a9e0738bf1ff6b6f645f3b1d7a4beda18b3f44bc", 401 | "sha256:6b10b317f056890a341304071fb3ab220f0adb2c87439a04eba9e69028a7e3ff", 402 | "sha256:c185d05d236c6c9756e914756c73f797cb618d81b42e694166639cce5bcfdb1f", 403 | "sha256:c802dbe5368743dd30dd2a684c15b83b17c3c95df54b66f97611a5988ae0f696", 404 | "sha256:cf57c53f1e366f3f6bc806fd83ad288b9c82ee8d2bbdfdc64d53767aaf500209", 405 | "sha256:e41001b7f43ecb9eb9bedf6762bd0e002561590487cc78c0b48f608a85ce02ac", 406 | "sha256:f472f1d0d04856cfaf15d0ebab5ff39b2b1bc09b2f4d2119c0ba0540121b5265" 407 | ], 408 | "version": "==4.5.1.0.post2" 409 | } 410 | }, 411 | "develop": {} 412 | } 413 | -------------------------------------------------------------------------------- /driver_analysis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding: utf-8 -*- 3 | # 4 | # driver_analysis.py 5 | # 6 | # Copyright 2018 Spencer McIntyre 7 | # 8 | # Redistribution and use in source and binary forms, with or without 9 | # modification, are permitted provided that the following conditions are 10 | # met: 11 | # 12 | # * Redistributions of source code must retain the above copyright 13 | # notice, this list of conditions and the following disclaimer. 14 | # * Redistributions in binary form must reproduce the above 15 | # copyright notice, this list of conditions and the following disclaimer 16 | # in the documentation and/or other materials provided with the 17 | # distribution. 18 | # * Neither the name of the nor the names of its 19 | # contributors may be used to endorse or promote products derived from 20 | # this software without specific prior written permission. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | # 34 | 35 | # this script must be run with Python 2.7 (for the angr library) and is thus not 36 | # meant to use the same 3.x runtime environment as the larger project 37 | 38 | # phase 1 - setup 39 | from __future__ import division 40 | from __future__ import print_function 41 | from __future__ import unicode_literals 42 | 43 | import argparse 44 | import collections 45 | import datetime 46 | import hashlib 47 | import json 48 | import logging 49 | import os 50 | import re 51 | import signal 52 | import sys 53 | 54 | import angr 55 | import archinfo 56 | import boltons.timeutils 57 | import claripy 58 | import smoke_zephyr.argparse_types 59 | import tabulate 60 | 61 | import simulation 62 | 63 | __version__ = '1.0' 64 | 65 | DOS_DEVICES = '\\DosDevices\\'.encode('utf-16le') 66 | 67 | # https://msdn.microsoft.com/en-us/library/cc704588.aspx 68 | STATUS_SUCCESS = 0 69 | STATUS_NOT_IMPLEMENTED = 0xc0000002 70 | STATUS_INVALID_HANDLE = 0xc0000008 71 | STATUS_INVALID_PARAMETER = 0xc000000d 72 | STATUS_NOT_SUPPORTED = 0xc00000bb 73 | 74 | HASH_ALGORITHMS = ('md5', 'sha1', 'sha256') 75 | 76 | def ast_repr(node): 77 | if not isinstance(node, claripy.ast.Base): 78 | raise TypeError('node must be an instance of claripy.ast.Base not: ' + repr(node)) 79 | return re.sub(r'([^a-zA-Z][a-zA-Z]+)_\d+_\d+([^\d]|$)', r'\1\2', node.__repr__(inner=True)) 80 | 81 | def ioctl_state_to_dict(project, state, IoStackLocation): 82 | in_var = ast_repr(IoStackLocation.fields['InputBufferLength']) 83 | out_var = ast_repr(IoStackLocation.fields['OutputBufferLength']) 84 | 85 | in_constraints = [] 86 | out_constraints = [] 87 | for constraint in state.solver.constraints: 88 | str_constraint = ast_repr(constraint) 89 | if 'InputBufferLength' in str_constraint: 90 | in_constraints.append(str_constraint) 91 | elif 'OutputBufferLength' in str_constraint: 92 | out_constraints.append(str_constraint) 93 | trace_addresses = tuple(project.address_to_offset(address) for address in state.history.bbl_addrs) 94 | trace_addresses = tuple(filter(lambda address: address is not None, trace_addresses)) 95 | 96 | value = { 97 | 'constraints': in_constraints + out_constraints, 98 | 'ioctl': state.solver.eval(IoStackLocation.fields['IoControlCode']), 99 | 'trace': { 100 | 'address-type': { 101 | 'data': 'basic-block', 102 | 'relation': 'offset', 103 | }, 104 | 'addresses': trace_addresses, 105 | } 106 | } 107 | return value 108 | 109 | def find_utf_16le_str(data, string): 110 | cursor = 0 111 | found = collections.deque() 112 | while cursor < len(data): 113 | cursor = data.find(string, cursor) 114 | if cursor == -1: 115 | break 116 | terminator = data.find(b'\x00\x00', cursor) 117 | if (terminator - cursor) % 2: 118 | terminator += 1 119 | match = data[cursor:terminator].decode('utf-16le') 120 | if not match in found: 121 | yield match 122 | found.append(match) 123 | cursor += len(string) 124 | 125 | def find_device_names(path): 126 | with open(path, 'rb') as file_h: 127 | data = file_h.read() 128 | return tuple(find_utf_16le_str(data, DOS_DEVICES)) 129 | 130 | def find_driver_object(project): 131 | arg_driverobject = 0xdead0000 132 | arg_registrypath = 0xdead1000 133 | 134 | # create our state for DriverEntry(pDriveObject, pRegistryPath) 135 | # with a return address of 0 so it's easily identifiable 136 | entry_state = project.factory.call_state_analysis( 137 | project.entry, 138 | arg_driverobject, 139 | arg_registrypath, 140 | ) 141 | 142 | driver_object = simulation.structures.DRIVER_OBJECT(entry_state, arg_driverobject) 143 | entry_state.solver.add(driver_object.fields['DeviceObject'] == 0) 144 | entry_state.solver.add(driver_object.fields['DriverStart'] == project.loader.main_object.mapped_base) 145 | entry_state.solver.add(driver_object.fields['DriverInit'] == project.loader.main_object.entry) 146 | entry_state.solver.add(driver_object.fields['DriverStartIo'] == 0) 147 | entry_state.solver.add(driver_object.fields['DriverUnload'] == 0) 148 | 149 | simgr = project.factory.simulation_manager_analysis(entry_state) 150 | 151 | # http://angr.io/api-doc/angr.html#angr.exploration_techniques.spiller.Spiller 152 | simgr.use_technique(angr.exploration_techniques.spiller.Spiller(min=50, max=100, staging_stash='overflow', staging_min=100, staging_max=200)) 153 | simgr.explore( 154 | engines=simulation.engines.factory(project), find=0, num_find=float('inf') 155 | ) 156 | if simgr.errored: 157 | print("[-] {0:,} states resulted in an error condition".format(len(simgr.errored))) 158 | import ipdb; ipdb.set_trace() 159 | 160 | # iterate though the found states and check the value of 161 | # pDriverObject->MajorFunction[14] (DeviceControl) 162 | if not isinstance(project.arch, archinfo.ArchAMD64): 163 | raise RuntimeError('must update offsets for non AMD64 architectures') 164 | 165 | major_functions = {} 166 | if not simgr.found: 167 | return None 168 | for state in simgr.found: 169 | for idx in range(28): 170 | value = state.mem[arg_driverobject + 0x70 + (8 * idx)].qword 171 | if value.resolved.symbolic: 172 | continue 173 | value = project.address_to_offset(value.concrete) 174 | if value is None: 175 | continue 176 | if idx in major_functions and major_functions[idx] != value: 177 | major_functions[idx] = None 178 | else: 179 | major_functions[idx] = value 180 | major_functions = [major_functions.get(idx) for idx in range(28)] 181 | driver_object = driver_object.to_dict(state) 182 | driver_object['fields'].append({'name': 'MajorFunction', 'type': 'void* MajorFunction[28]', 'value': major_functions}) 183 | return driver_object 184 | 185 | def find_valid_ioctl_states(project, mj_device_control): 186 | arg_deviceobject = 0xdead0000 187 | arg_irp = 0xdead8000 188 | entry_state = project.factory.call_state_analysis( 189 | project.loader.main_object.mapped_base + mj_device_control, 190 | arg_deviceobject, 191 | arg_irp, 192 | ) 193 | 194 | io_stack_location = simulation.structures.IO_STACK_LOCATION(entry_state, 0xdeadc000) 195 | irp = simulation.structures.IRP(entry_state, arg_irp) 196 | entry_state.solver.add(irp.fields['Type'] == 6) 197 | entry_state.solver.add(irp.fields['MdlAddress'] == 0) 198 | entry_state.solver.add(irp.fields['IoStatus.Status'] == STATUS_SUCCESS) 199 | entry_state.solver.add(irp.fields['IoStatus.Information'] == 0) 200 | entry_state.solver.add(irp.fields['RequestorMode'] == 1) 201 | entry_state.solver.add(irp.fields['PendingReturned'] == 0) 202 | entry_state.solver.add(irp.fields['StackCount'] == 1) 203 | entry_state.solver.add(irp.fields['CurrentLocation'] == 1) 204 | entry_state.solver.add(irp.fields['Cancel'] == 0) 205 | entry_state.solver.add(irp.fields['CancelIrql'] == 0) 206 | entry_state.solver.add(irp.fields['ApcEnvironment'] == 0) 207 | entry_state.solver.add(irp.fields['AllocationFlags'] == 6) 208 | entry_state.solver.add(irp.fields['UserEvent'] == 0) 209 | entry_state.solver.add(irp.fields['CancelRoutine'] == 0) 210 | entry_state.solver.add(irp.fields['Tail.Overlay.CurrentStackLocation'] == io_stack_location.address) 211 | 212 | entry_state.solver.add(io_stack_location.fields['MajorFunction'] == 14) 213 | entry_state.solver.add(io_stack_location.fields['MinorFunction'] == 0) 214 | entry_state.solver.add(io_stack_location.fields['Flags'] == 5) 215 | entry_state.solver.add(io_stack_location.fields['Control'] == 0) 216 | entry_state.solver.add(io_stack_location.fields['DeviceObject'] == arg_deviceobject) 217 | entry_state.solver.add(io_stack_location.fields['CompletionRoutine'] == 0) 218 | entry_state.solver.add(io_stack_location.fields['Context'] == 0) 219 | 220 | simgr = project.factory.simulation_manager_analysis(entry_state) 221 | # http://angr.io/api-doc/angr.html#angr.exploration_techniques.spiller.Spiller 222 | simgr.use_technique(angr.exploration_techniques.spiller.Spiller(min=50, max=100, staging_stash='overflow', staging_min=100, staging_max=200)) 223 | 224 | def _avoid(state): 225 | status = irp.read_field_single_valued('IoStatus.Status', state=state) 226 | return status is not None and status != 0 227 | 228 | def _find(state): 229 | eval_ = state.solver.eval 230 | unique_ = state.solver.unique 231 | if eval_(state.regs.rip) != 0: 232 | return False 233 | 234 | if irp.read_field_single_valued('IoStatus.Status', state=state): 235 | return False 236 | # check if IoControlCode is concrete or symbolic 237 | if not unique_(io_stack_location.fields['IoControlCode']): 238 | return False 239 | return True 240 | 241 | def _step_func(lsm): 242 | lsm = lsm.drop(stash='avoid') 243 | return lsm 244 | 245 | simgr.explore( 246 | avoid=_avoid, 247 | engines=simulation.engines.factory(project), 248 | find=_find, 249 | num_find=float('inf'), 250 | step_func=_step_func 251 | ) 252 | if simgr.errored: 253 | print("[-] {0:,} states resulted in an error condition".format(len(simgr.errored))) 254 | import ipdb; ipdb.set_trace() 255 | 256 | found_states = sorted(simgr.found, key=lambda state: state.solver.eval(io_stack_location.fields['IoControlCode'])) 257 | return tuple(ioctl_state_to_dict(project, state, io_stack_location) for state in found_states) 258 | 259 | def print_valid_ioctl_states(found_states): 260 | table = [] 261 | for idx, state in enumerate(found_states, 1): 262 | constraints = ' && '.join(state['constraints']) 263 | table.append((idx, "0x{0:08x}".format(state['ioctl']), '', constraints)) 264 | table = sorted(table, key=lambda row: row[0]) 265 | print(tabulate.tabulate(table, headers=('#', 'IOCTL Code', 'Name', 'Constraints'), tablefmt='pipe')) 266 | 267 | summary = "Summary: {0:,} states found ({1:,} unique IOCTL values)".format( 268 | len(table), 269 | len(set(state['ioctl'] for state in found_states)) 270 | ) 271 | print('-' * len(summary)) 272 | print(summary) 273 | 274 | def setup_logging(args): 275 | level = getattr(logging, args.loglvl) 276 | root_logger = logging.getLogger('') 277 | for handler in root_logger.handlers: 278 | root_logger.removeHandler(handler) 279 | 280 | logging.getLogger(args.logger).setLevel(logging.DEBUG) 281 | console_log_handler = logging.StreamHandler() 282 | console_log_handler.setLevel(level) 283 | console_log_handler.setFormatter(logging.Formatter('%(levelname)-8s %(message)s')) 284 | logging.getLogger(args.logger).addHandler(console_log_handler) 285 | logging.captureWarnings(True) 286 | 287 | def sigalrm_handler(*args, **kwargs): 288 | raise TimeoutError('sigalrm raised') 289 | 290 | def main(): 291 | start_time = datetime.datetime.utcnow() 292 | parser = argparse.ArgumentParser(description='Automatic Driver Analysis', conflict_handler='resolve') 293 | parser.add_argument('driver', help='the driver to analyze') 294 | parser.add_argument('-o', '--output', default=None, help='the file to write the analysis data to') 295 | parser.add_argument('--timeout', type=smoke_zephyr.argparse_types.timespan_type, help='an optional operation timeout') 296 | parser.add_argument('-L', '--log', default='FATAL', dest='loglvl', choices=('DEBUG', 'INFO', 'WARNING', 'ERROR', 'FATAL'), help='set the logging level') 297 | parser.add_argument('--logger', default='', help='specify the root logger') 298 | parser.add_argument('-v', '--version', action='version', version='%(prog)s Version: ' + __version__) 299 | args = parser.parse_args() 300 | 301 | setup_logging(args) 302 | if not os.path.isfile(args.driver): 303 | print('[-] invalid driver file: ' + args.driver) 304 | return os.EX_DATAERR 305 | driver_offset = lambda offset: "{0}+0x{1:x}".format(os.path.splitext(os.path.basename(args.driver))[0], offset) 306 | 307 | if args.timeout: 308 | signal.signal(signal.SIGALRM, sigalrm_handler) 309 | signal.alarm(args.timeout) 310 | 311 | print('[*] basic driver file information:') 312 | with open(args.driver, 'rb') as file_h: 313 | data = file_h.read() 314 | hashes = collections.OrderedDict((algo, hashlib.new(algo, data).hexdigest()) for algo in HASH_ALGORITHMS) 315 | for algo, digest in hashes.items(): 316 | print(" * {0: <8} {1}".format(algo + ':', digest)) 317 | 318 | device_names = find_device_names(args.driver) 319 | print("[*] identified {0:,} device names".format(len(device_names))) 320 | for device_name in device_names: 321 | print(' * ' + device_name) 322 | 323 | project = simulation.WindowsDriverProject(args.driver) 324 | analysis = { 325 | 'binary': { 326 | 'architecture': project.arch.name, 327 | 'base-address': project.loader.main_object.mapped_base, 328 | 'hashes': hashes, 329 | 'name': os.path.basename(args.driver), 330 | 'size': os.stat(args.driver).st_size, 331 | }, 332 | 'created': start_time.isoformat() + '+00:00', 333 | 'device-names': device_names, 334 | } 335 | 336 | driver_object = find_driver_object(project) 337 | if driver_object is None: 338 | print('[-] failed to identify the driver object') 339 | return os.EX_SOFTWARE 340 | analysis['driver-object'] = driver_object 341 | mj_device_control = next(field for field in driver_object['fields'] if field['name'] == 'MajorFunction') 342 | mj_device_control = mj_device_control['value'][14] 343 | if mj_device_control is None: 344 | print('[-] failed to identify a single concrete value for the control routine') 345 | else: 346 | print("[+] identified {0} as the control routine".format(driver_offset(mj_device_control))) 347 | 348 | ioctl_states = find_valid_ioctl_states(project, mj_device_control) 349 | analysis['ioctl-states'] = ioctl_states 350 | print_valid_ioctl_states(ioctl_states) 351 | 352 | results_file = args.output or os.path.splitext(os.path.basename(args.driver))[0] + '-analysis.json' 353 | with open(results_file, 'w') as file_h: 354 | json.dump(analysis, file_h, indent=2, separators=(',', ': '), sort_keys=True) 355 | elapsed = boltons.timeutils.decimal_relative_time(start_time, datetime.datetime.utcnow()) 356 | print("[*] completed in: {0:.1f} {1}".format(*elapsed)) 357 | return os.EX_OK 358 | 359 | if __name__ == '__main__': 360 | sys.exit(main()) 361 | -------------------------------------------------------------------------------- /simulation/__init__.py: -------------------------------------------------------------------------------- 1 | from . import engines 2 | from . import procedures 3 | from . import pyvex_lifters 4 | from . import structures 5 | from .project import WindowsDriverProject 6 | -------------------------------------------------------------------------------- /simulation/engines.py: -------------------------------------------------------------------------------- 1 | import angr 2 | 3 | from . import procedures 4 | 5 | def factory(project): 6 | engines = ( 7 | SimEngineINT(project), 8 | angr.engines.SimEngineFailure(project), 9 | angr.engines.SimEngineSyscall(project), 10 | angr.engines.SimEngineHook(project), 11 | SimEngineMSR(project), 12 | angr.engines.SimEngineUnicorn(project), 13 | angr.engines.SimEngineVEX(project), 14 | ) 15 | return engines 16 | 17 | def factory_names(): 18 | return ('int', 'failure', 'syscall', 'hook', 'msr', 'unicorn', 'vex') 19 | 20 | class SimEngineINT(angr.SimEngine): 21 | name = 'int' 22 | def check(self, state, *args, **kwargs): 23 | jumpkind = state.history.jumpkind 24 | if not jumpkind.startswith('Ijk_Sys_int'): 25 | return False 26 | return self._get_handler(jumpkind) is not None 27 | 28 | def process(self, state, force_addr=None, **kwargs): 29 | handler = self._get_handler(state.history.jumpkind) 30 | return handler(state, force_addr=force_addr, **kwargs) 31 | 32 | def _get_handler(self, jumpkind): 33 | return getattr(self, "_int_0x{0:x}".format(int(jumpkind[11:])), None) 34 | 35 | def _int_0x29(self, state, force_addr=None, **kwargs): 36 | # https://doar-e.github.io/blog/2013/10/12/having-a-look-at-the-windows-userkernel-exceptions-dispatcher/ 37 | terminator = angr.procedures.SIM_PROCEDURES['stubs']['PathTerminator'](project=self.project) 38 | return self.project.factory.procedure_engine.process(state, terminator, force_addr=state.addr) 39 | 40 | class SimEngineMSR(angr.SimEngine): 41 | name = 'msr' 42 | def check(self, state, *args, **kwargs): 43 | return self.process(state, *args, **kwargs) is not None 44 | 45 | def process(self, state, force_addr=None, **kwargs): 46 | addr = state.addr if force_addr is None else force_addr 47 | rip_word = state.mem[addr].word 48 | procedure = None 49 | if rip_word.concrete == 0x300f: # wrmsr 50 | procedure = procedures.SimProcedureWRMSR 51 | if rip_word.concrete == 0x320f: # rdmsr 52 | procedure = procedures.SimProcedureRDMSR 53 | if procedure is None: 54 | return None 55 | procedure = procedure(project=state.project) 56 | return self.project.factory.procedure_engine.process(state, procedure, force_addr=force_addr, **kwargs) 57 | 58 | angr.engines.basic_preset.add_default_plugin(SimEngineINT.name, SimEngineINT) 59 | angr.engines.basic_preset.add_default_plugin(SimEngineMSR.name, SimEngineMSR) 60 | -------------------------------------------------------------------------------- /simulation/jump_resolvers.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | import angr.analyses.cfg.indirect_jump_resolvers.default_resolvers 4 | import angr.analyses.cfg.indirect_jump_resolvers.resolver 5 | import smoke_zephyr.utilities 6 | 7 | def factory(project, obj=None): 8 | obj = obj or project.loader.main_object 9 | indirect_jump_resolvers = tuple( 10 | angr.analyses.cfg.indirect_jump_resolvers.default_resolvers.default_indirect_jump_resolvers( 11 | obj, 12 | project 13 | )) + (SimulationResolver(project),) 14 | return indirect_jump_resolvers 15 | 16 | class SimulationResolver(angr.analyses.cfg.indirect_jump_resolvers.resolver.IndirectJumpResolver): 17 | logger = logging.getLogger('simulation.jump_resolvers.SimulationResolver') 18 | def __init__(self, *args, **kwargs): 19 | self.__cache = {} 20 | super(SimulationResolver, self).__init__(*args, **kwargs) 21 | 22 | def filter(self, cfg, addr, func_addr, block, jumpkind): 23 | return jumpkind == 'Ijk_Boring' 24 | 25 | def _resolve(self, cfg, addr, func_addr, block, jumpkind): 26 | call_state = self.project.factory.call_state(func_addr, symbolic_register_arguments=True) 27 | simgr = self.project.factory.simulation_manager(call_state) 28 | simgr.explore(find=addr) 29 | if not simgr.found: 30 | return False, None 31 | simgr.move('active', 'pruned') 32 | simgr.move('found', 'active') 33 | simgr.step() 34 | return True, sorted(smoke_zephyr.utilities.unique(tuple(state.addr for state in simgr.active))) 35 | 36 | def resolve(self, cfg, addr, func_addr, block, jumpkind): 37 | cache_key = (id(cfg), addr, func_addr) 38 | results = self.__cache.get(cache_key) 39 | if results is None: 40 | results = self._resolve(cfg, addr, func_addr, block, jumpkind) 41 | self.__cache[cache_key] = results 42 | return results 43 | -------------------------------------------------------------------------------- /simulation/procedures/__init__.py: -------------------------------------------------------------------------------- 1 | from .instructions import * 2 | from .windows_kernel import * 3 | -------------------------------------------------------------------------------- /simulation/procedures/instructions.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | import angr 4 | 5 | _reg_repr = lambda state, reg: "{0:08x}".format(state.solver.eval(reg)) if state.solver.unique(reg) else '????????' 6 | 7 | class SimProcedureInstructionBase(angr.SimProcedure): 8 | IS_FUNCTION = False 9 | NO_RET = True 10 | 11 | class SimProcedureRDMSR(SimProcedureInstructionBase): 12 | logger = logging.getLogger('simulation.procedures.instructions.rdmsr') 13 | def run(self, *args, **kwargs): 14 | if self.state.arch.name != 'AMD64': 15 | raise angr.errors.SimUnsupportedError('SimProcedureRDMSR is only implemented for AMD64') 16 | state = self.state 17 | state.regs.rax = state.solver.BVS('msr_lo_32', 32) 18 | state.regs.rdx = state.solver.BVS('msr_hi_32', 32) 19 | self.logger.info("Simulating 0x{0:x} rdmsr[0x{1}]".format(state.addr, _reg_repr(state, state.regs.rcx))) 20 | self.successors.add_successor(state, state.addr + 2, state.solver.true, 'Ijk_Boring') 21 | 22 | class SimProcedureWRMSR(SimProcedureInstructionBase): 23 | logger = logging.getLogger('simulation.procedures.instructions.wrmsr') 24 | def run(self, *args, **kwargs): 25 | if self.state.arch.name != 'AMD64': 26 | raise angr.errors.SimUnsupportedError('SimProcedureWRMSR is only implemented for AMD64') 27 | state = self.state 28 | self.logger.info("Simulating 0x{0:x} wrmsr[0x{1}] = 0x{2}{3}".format(state.addr, _reg_repr(state, state.regs.rcx), _reg_repr(state, state.regs.rdx), _reg_repr(state, state.regs.rax))) 29 | self.successors.add_successor(state, state.addr + 2, state.solver.true, 'Ijk_Boring') 30 | -------------------------------------------------------------------------------- /simulation/procedures/windows_kernel.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | import angr 4 | 5 | STATUS_SUCCESS = 0 6 | 7 | procedures = {} 8 | def register(procedure): 9 | procedures[procedure.__name__] = procedure 10 | return procedure 11 | 12 | class WindowsKernelSimProcedure(angr.SimProcedure): 13 | def __init__(self, *args, **kwargs): 14 | super(WindowsKernelSimProcedure, self).__init__(*args, **kwargs) 15 | self.logger = logging.getLogger('simulation.procedures.windows.kernel.' + self.__class__.__name__) 16 | 17 | @register 18 | class IoCreateDevice(WindowsKernelSimProcedure): 19 | def run(self, driver_object, device_extension_size, device_name, device_type, device_characteristics, exclusive, device_object): 20 | self.logger.warning("IoCreateDevice(%s, %s, %s, %s, %s, %s, %s)", driver_object, device_extension_size, device_name, device_type, device_characteristics, exclusive, device_object) 21 | return STATUS_SUCCESS 22 | 23 | @register 24 | class IoCreateSymbolicLink(WindowsKernelSimProcedure): 25 | def run(self, symbolic_link_name, device_name): 26 | self.logger.warning("IoCreateSymbolicLink(%s, %s)", symbolic_link_name, device_name) 27 | return STATUS_SUCCESS 28 | 29 | @register 30 | class IofCompleteRequest(WindowsKernelSimProcedure): 31 | def run(self, irp, priority_boost): 32 | self.logger.warning("IofCompleteRequest(%s, %s)", irp, priority_boost) 33 | return self.state.solver.Unconstrained('IofCompleteRequest', self.state.arch.bits) 34 | 35 | @register 36 | class PsGetVersion(WindowsKernelSimProcedure): 37 | def run(self, major_version, minor_version, build_number, csd_version): 38 | self.logger.warning("PsGetVersion(%s, %s, %s, %s)", major_version, minor_version, build_number, csd_version) 39 | if major_version: 40 | self.state.mem[major_version].dword = 10 41 | if minor_version: 42 | self.state.mem[minor_version].dword = 0 43 | if build_number: 44 | self.state.mem[build_number].dword = 0x42ee 45 | return self.state.solver.BVV(0, 8) 46 | 47 | @register 48 | class PsSetCreateProcessNotifyRoutine(WindowsKernelSimProcedure): 49 | def run(self, notify_routine, remove): 50 | self.logger.warning("PsSetCreateProcessNotifyRoutine(%s, %s)", notify_routine, remove) 51 | return STATUS_SUCCESS 52 | -------------------------------------------------------------------------------- /simulation/project.py: -------------------------------------------------------------------------------- 1 | import angr 2 | import archinfo 3 | import claripy 4 | 5 | from . import engines 6 | from . import jump_resolvers 7 | from .procedures import windows_kernel 8 | 9 | class WindowsDriverObjectFactory(angr.factory.AngrObjectFactory): 10 | def __init__(self, *args, **kwargs): 11 | super(WindowsDriverObjectFactory, self).__init__(*args, **kwargs) 12 | # set the default calling convention 13 | if isinstance(self.project.arch, archinfo.ArchAMD64): 14 | self._default_cc = angr.calling_conventions.SimCCMicrosoftAMD64(self.project.arch) 15 | else: 16 | raise ValueError('unsupported project architecture') 17 | 18 | def call_state(self, addr, *args, **kwargs): 19 | kwargs['add_options'] = kwargs.pop('add_options', angr.options.unicorn) 20 | cc = kwargs.pop('cc', self._default_cc) 21 | kwargs['cc'] = cc 22 | if kwargs.pop('symbolic_register_arguments', False): 23 | args = list(args) 24 | while len(args) < len(cc.ARG_REGS): 25 | args.append(claripy.BVS('arg_' + str(len(args) + 1), cc.ARCH.bits)) 26 | return super(WindowsDriverObjectFactory, self).call_state(addr, *args, **kwargs) 27 | 28 | def call_state_analysis(self, *args, **kwargs): 29 | kwargs['ret_addr'] = kwargs.pop('ret_addr', 0) 30 | state = self.call_state(*args, **kwargs) 31 | state.register_plugin('loop_data', angr.state_plugins.SimStateLoopData()) 32 | return state 33 | 34 | def simulation_manager_analysis(self, state, *args, **kwargs): 35 | simgr = self.simulation_manager(state.copy(), *args, **kwargs) 36 | cfg = self.project.analyses.CFGEmulated( 37 | indirect_jump_resolvers=jump_resolvers.factory(self.project), 38 | keep_state=False, 39 | max_iterations=5, 40 | normalize=True, 41 | starts=(state.copy(),), 42 | ) 43 | simgr.use_technique(angr.exploration_techniques.LoopSeer(cfg=cfg)) 44 | return simgr 45 | 46 | class WindowsDriverProject(angr.Project): 47 | def __init__(self, *args, **kwargs): 48 | kwargs['auto_load_libs'] = kwargs.pop('auto_load_libs', False) 49 | kwargs['use_sim_procedures'] = kwargs.pop('use_sim_procedures', False) 50 | super(WindowsDriverProject, self).__init__(*args, **kwargs) 51 | #self.engines.register_plugin(engines.SimEngineINT.name, engines.SimEngineINT(project=self)) 52 | #self.engines.register_plugin(engines.SimEngineMSR.name, engines.SimEngineMSR(project=self)) 53 | #self.engines.order = engines.factory_names() 54 | self.factory = WindowsDriverObjectFactory(self) 55 | for symbol, procedure in windows_kernel.procedures.items(): 56 | self.hook_symbol(symbol, procedure(cc=self.default_cc)) 57 | 58 | @property 59 | def default_cc(self): 60 | return self.factory._default_cc 61 | 62 | def address_to_offset(self, address): 63 | main_obj = self.loader.main_object 64 | if main_obj.contains_addr(address): 65 | return address - main_obj.mapped_base 66 | return None 67 | -------------------------------------------------------------------------------- /simulation/pyvex_lifters.py: -------------------------------------------------------------------------------- 1 | import pyvex 2 | import pyvex.lifting.util 3 | 4 | _Type = pyvex.lifting.util.Type 5 | _VexValue = pyvex.lifting.util.syntax_wrapper.VexValue 6 | 7 | class Instruction_INT(pyvex.lifting.util.instr_helper.Instruction): 8 | name = 'int' 9 | bin_format = '11001101xxxxxxxx' 10 | def compute_result(self): 11 | number = int(self.data['x'], 2) 12 | # https://github.com/angr/vex/blob/4bdf4da8e0208e8ebf0a728d0477aebfba890f93/pub/libvex_ir.h#L2285-L2352 13 | self.jump(None, self.irsb_c.irsb.addr + 2, jumpkind='Ijk_Sys_int' + str(number)) 14 | 15 | 16 | class Instruction_RDMSR(pyvex.lifting.util.instr_helper.Instruction): 17 | name = 'rdmsr' 18 | bin_format = '0000111100110010' 19 | def compute_result(self): 20 | self.get('ecx', _Type.int_32) 21 | # TODO: these shouldn't be using constants 22 | self.put(_VexValue.Constant(self.irsb_c, 1, _Type.int_32), 'eax') 23 | self.put(_VexValue.Constant(self.irsb_c, 1, _Type.int_32), 'edx') 24 | return True 25 | 26 | def commit_result(self, retval): 27 | ir_const_class = pyvex.const.vex_int_class(self.irsb_c.arch.bits) 28 | self.irsb_c.irsb.next = pyvex.expr.Const(ir_const_class(self.irsb_c.irsb.addr + 2)) 29 | 30 | class Instruction_WRMSR(pyvex.lifting.util.instr_helper.Instruction): 31 | name = 'wrmsr' 32 | bin_format = '0000111100110000' 33 | def compute_result(self): 34 | #self.get('ecx', _Type.int_32) 35 | #self.get('eax', _Type.int_32) 36 | #self.get('edx', _Type.int_32) 37 | return True 38 | 39 | class AMD64Spotter(pyvex.lifting.util.lifter_helper.GymratLifter): 40 | instrs = ( 41 | Instruction_INT, 42 | Instruction_RDMSR, 43 | #Instruction_WRMSR 44 | ) 45 | 46 | pyvex.lifting.register(AMD64Spotter, 'AMD64') 47 | -------------------------------------------------------------------------------- /simulation/structures.py: -------------------------------------------------------------------------------- 1 | import collections 2 | 3 | class SymbolicStructureBase(object): 4 | FieldDetails = collections.namedtuple('FieldDetails', ('name', 'size', 'offset', 'ctype')) 5 | 6 | def __init__(self, state, address): 7 | self.state = state 8 | self.address = address 9 | self.fields = collections.OrderedDict() 10 | for spec in self._fields_: 11 | spec = self.FieldDetails(*spec) 12 | symbol = state.solver.BVS(spec.name, spec.size * 8) 13 | # setattr(self, name, symbol) 14 | type_ = "uint{0}_t".format(spec.size * 8) 15 | setattr(state.mem[address + spec.offset], type_, symbol) 16 | self.fields[spec.name] = symbol 17 | 18 | def eval_field(self, field_name, state=None): 19 | state = state or self.state 20 | symbol = self.fields[field_name] 21 | return state.solver.eval(symbol) 22 | 23 | def get_field_details(self, field_name): 24 | for spec in self._fields_: 25 | spec = self.FieldDetails(*spec) 26 | if spec.name == field_name: 27 | return spec 28 | raise RuntimeError('invalid field: ' + field_name) 29 | 30 | def read_field(self, field_name, state=None): 31 | state = state or self.state 32 | spec = self.get_field_details(field_name) 33 | return getattr(state.mem[self.address + spec.offset], "uint{0}_t".format(spec.size * 8)) 34 | 35 | def read_field_single_valued(self, field_name, state=None): 36 | state = state or self.state 37 | field = self.read_field(field_name, state=state) 38 | if not state.solver.single_valued(field.resolved): 39 | return None 40 | return field.concrete 41 | 42 | def pp(self, state=None): 43 | state = state or self.state 44 | print("0x{0:08x} {1}".format(self.address, self.__class__.__name__)) 45 | for spec in self._fields_: 46 | spec = self.FieldDetails(*spec) 47 | symbol = self.fields[spec.name] 48 | if state.solver.unique(symbol): 49 | value = "0x{0:x}".format(state.solver.eval(symbol)) 50 | else: 51 | value = '???' 52 | print("0x{0:08x} {1: <20} {2}".format(self.address + spec.offset, spec.name, value)) 53 | 54 | def to_dict(self, state=None): 55 | state = state or self.state 56 | fields = [] 57 | for spec in self._fields_: 58 | spec = self.FieldDetails(*spec) 59 | field = self.read_field(spec.name, state=state) 60 | fields.append({ 61 | 'name': spec.name, 62 | 'type': spec.ctype, 63 | 'value': None if field.resolved.symbolic else field.concrete 64 | }) 65 | return {'name': self.__class__.__name__, 'fields': fields} 66 | 67 | class DRIVER_OBJECT(SymbolicStructureBase): 68 | _fields_ = ( 69 | ('Type', 2, 0x00, 'uint16_t Type'), 70 | ('Size', 2, 0x02, 'uint16_t Size'), 71 | ('DeviceObject', 8, 0x08, 'void* DeviceObject'), 72 | ('Flags', 8, 0x10, 'uint64_t Flags'), 73 | ('DriverStart', 8, 0x18, 'void* DriverStart'), 74 | ('DriverSize', 8, 0x20, 'uint64_t DriverSize'), 75 | ('DriverSection', 8, 0x28, 'void* DriverSection'), 76 | ('DriverExtension', 8, 0x30, 'void* DriverExtension'), 77 | ('DriverName.Length', 2, 0x38, 'uint16_t Length'), 78 | ('DriverName.MaximumLength', 2, 0x3a, 'uint16_t MaximumLength'), 79 | ('DriverName.Buffer', 8, 0x40, 'uint16_t* Buffer'), 80 | # UNICODE_STRING DriverName; 81 | ('HardwareDatabase', 8, 0x48, 'void* HardwareDatabase'), 82 | ('FastIoDispatch', 8, 0x50, 'void* FastIoDispatch'), 83 | ('DriverInit', 8, 0x58, 'void* DriverInit'), 84 | ('DriverStartIo', 8, 0x60, 'void* DriverStartIo'), 85 | ('DriverUnload', 8, 0x68, 'void* DriverUnload'), 86 | # void* MajorFunction[28]; 87 | ) 88 | 89 | class IO_STACK_LOCATION(SymbolicStructureBase): 90 | _fields_ = ( 91 | ('MajorFunction', 1, 0x00, 'uint8_t MajorFunction'), 92 | ('MinorFunction', 1, 0x01, 'uint8_t MinorFunction'), 93 | ('Flags', 1, 0x02, 'uint8_t Flags'), 94 | ('Control', 1, 0x03, 'uint8_t Control'), 95 | ('OutputBufferLength', 4, 0x08, 'uint32_t OutputBufferLength'), 96 | ('InputBufferLength', 4, 0x10, 'uint32_t InputBufferLength'), 97 | ('IoControlCode', 4, 0x18, 'uint32_t IoControlCode'), 98 | ('Type3InputBuffer', 8, 0x20, 'void* Type3InputBuffer'), 99 | ('DeviceObject', 8, 0x28, 'void* DeviceObject'), 100 | ('FileObject', 8, 0x30, 'void* FileObject'), 101 | ('CompletionRoutine', 8, 0x38, 'void* CompletionRoutine'), 102 | ('Context', 8, 0x40, 'void* Context'), 103 | ) 104 | 105 | class IRP(SymbolicStructureBase): 106 | _fields_ = ( 107 | ('Type', 2, 0x00, 'uint16_t Type'), 108 | ('Size', 2, 0x02, 'uint16_t Size'), 109 | ('AllocationProcessorNumber', 2, 0x04, 'uint16_t AllocationProcessorNumber'), 110 | ('MdlAddress', 8, 0x08, 'void* MdlAddress'), 111 | ('Flags', 8, 0x10, 'uint64_t Flags'), 112 | ('AssociatedIrp.SystemBuffer', 8, 0x18, 'void* SystemBuffer'), 113 | ('IoStatus.Status', 4, 0x30, 'uint32_t Status'), 114 | ('IoStatus.Information', 8, 0x38, 'uint64_t Information'), 115 | ('RequestorMode', 1, 0x40, 'int8_t RequestorMode'), 116 | ('PendingReturned', 1, 0x41, 'uint8_t PendingReturned'), 117 | ('StackCount', 1, 0x42, 'int8_t StackCount'), 118 | ('CurrentLocation', 1, 0x43, 'int8_t CurrentLocation'), 119 | ('Cancel', 1, 0x44, 'uint8_t Cancel'), 120 | ('CancelIrql', 1, 0x45, 'uint8_t CancelIrql'), 121 | ('ApcEnvironment', 1, 0x46, 'int8_t ApcEnvironment'), 122 | ('AllocationFlags', 1, 0x47, 'uint8_t AllocationFlags'), 123 | ('UserIosb', 8, 0x48, 'void* UserIosb'), 124 | ('UserEvent', 8, 0x58, 'void* UserEvent'), 125 | ('CancelRoutine', 8, 0x58, 'void* CancelRoutine'), 126 | ('Tail.Overlay.CurrentStackLocation', 8, 0xb8, 'void* CurrentStackLocation'), 127 | ) 128 | --------------------------------------------------------------------------------