I have been meaning to learn about SMT based program analysis for a long time and recently I started learning about the topic. There are so many frameworks, articles and tutorials out there that I shall explore as time goes by.
Since I am still learning, I do not claim that the following material is rocket science or advanced by any means, instead, it is very basic and should be approachable enough by absolute beginners to Z3 (a theorem prover from Microsoft Research). All I know so far comes from reading Dennis Yurichev‘s e-book “Quick introduction into SAT/SMT solvers and symbolic execution” in addition to the Z3Py introductory material by Eric (https://github.com/ericpony/z3py-tutorial).
In last week’s blog post, I illustrated how to write a basic emulator to evaluate a function’s return value without running it. In today’s blog post, I am going to show how to convert thousands of arithmetic operations from x86 assembly code into simplified Z3 expressions.
Introduction
The test program is similar to last week’s test program, where it has a table of 12 challenge functions that get called from the main function. Each challenge function was randomly generated so it contains a random sequence of add/sub/dec/inc instructions that work with the eax/ebx/ecx/edx registers (and immediate values).
Here’s a snippet of the first challenge function (at 0x401000):
//------------------------------------------------------------------------- uint32_t challenge_1(uint32_t c1, uint32_t c2, uint32_t c3, uint32_t c4) // 1953 operation(s) { uint32_t result; __asm { pushad mov eax, [c1] mov edx, [c2] mov ecx, [c3] mov ebx, [c4] sub eax, ebx inc ebx sub ebx, ecx sub ecx, 0x852e4867 add ebx, ebx inc ecx add eax, edx add ecx, ebx sub ecx, ebx inc ecx sub ebx, edx add eax, 0x7a20f9e6 add ebx, 0xaa5a1584 add edx, edx sub ebx, 0x1ca0a567 sub eax, 0xf94f7d8c inc ecx inc eax add edx, eax sub ebx, edx inc ebx sub edx, 0xd68a9fa7 inc ebx inc eax inc eax . . ...1000+ instructions later... . sub ebx, edx inc eax sub ebx, edx sub ecx, eax add eax, ebx add ecx, 0xd2cb013d add ecx, 0xda9d6a2e add edx, eax sub edx, 0x25ebd85d add ebx, ebx add ebx, 0x936e2259 inc eax add eax, ecx add ebx, 0xc0c1aa inc ebx add edx, 0x921ee6d5 add edx, edx add ecx, eax add ecx, eax inc ebx sub ebx, edx add ebx, eax inc ebx sub eax, 0xd9d2f9c2 add edx, eax inc ecx add ecx, 0xad2e6bb0 add ecx, eax sub ecx, ebx add ebx, eax sub ecx, 0xe2786d0c add eax, ebx add eax, ecx add eax, edx mov dword ptr [result], eax popad } return result; }
The disassembly listing of the function above is going to look similar, so instead of showing the disassembly, I am going to show you the output of the Hex-Rays decompiler:
int __cdecl challenge_1(unsigned int c1, unsigned int c2, unsigned int c3, unsigned int c4) { unsigned int v4; // edx unsigned int v5; // ebx unsigned int v6; // ecx unsigned int v7; // eax int v8; // ecx int v9; // eax int v10; // edx int v11; // ebx int v12; // ecx int v13; // eax int v14; // ebx int v15; // edx int v16; // eax int v17; // ebx int v18; // ecx int v19; // edx int v20; // ecx int v21; // edx int v22; // eax int v23; // ebx int v24; // edx int v25; // eax int v26; // ecx int v27; // edx int v28; // ecx int v29; // edx int v30; // eax int v31; // ebx int v32; // ecx int v33; // eax int v34; // edx int v35; // ecx int v36; // ebx int v37; // edx int v38; // ecx int v39; // eax int v40; // ebx int v41; // ecx int v42; // eax int v43; // edx int v44; // ebx int v45; // ebx int v46; // edx int v47; // ecx int v48; // eax int v49; // edx int v50; // ebx int v51; // eax int v52; // edx int v53; // ebx int v54; // eax int v55; // edx int v56; // ecx int v57; // ecx int v58; // eax int v59; // edx int v60; // eax int v61; // ebx int v62; // eax int v63; // ebx int v64; // ecx int v65; // eax int v66; // edx int v67; // ecx int v68; // ebx int v69; // edx int v70; // ebx int v71; // edx int v72; // ecx int v73; // eax int v74; // ecx int v75; // edx int v76; // ebx int v77; // edx int v78; // edx int v79; // eax int v80; // ebx int v81; // ecx int v82; // ebx int v83; // eax int v84; // ebx int v85; // edx int v86; // ebx int v87; // eax int v88; // edx int v89; // ecx int v90; // eax int v91; // edx int v92; // ebx int v93; // ecx int v94; // ebx int v95; // eax int v96; // ecx int v97; // ecx int v98; // ebx int v99; // ecx int v100; // eax int v101; // edx int v102; // ebx int v103; // edx int v104; // edx int v105; // ecx int v106; // eax int v107; // ecx int v108; // edx int v109; // eax int v110; // edx int v111; // eax int v112; // ebx int v113; // ecx int v114; // edx int v115; // eax int v116; // edx int v117; // ecx int v118; // ebx int v119; // eax int v120; // ecx int v121; // edx int v122; // edx int v123; // eax int v124; // edx int v125; // ebx int v126; // eax int v127; // edx int v128; // ecx int v129; // ebx int v130; // edx int v131; // ebx int v132; // ecx int v133; // ebx int v134; // ebx int v135; // eax int v136; // ecx int v137; // ebx int v138; // ebx int v139; // edx int v140; // ecx int v141; // ebx int v142; // eax int v143; // ecx int v144; // ebx int v145; // edx int v146; // ebx int v147; // edx int v148; // ecx int v149; // eax int v150; // ebx int v151; // ecx int v152; // ebx int v153; // edx int v154; // eax int v155; // edx int v156; // ebx int v157; // edx int v158; // ebx int v159; // ecx int v160; // ebx int v161; // eax int v162; // ecx int v163; // ebx int v164; // edx int v165; // eax int v166; // ecx int v167; // eax int v168; // edx int v169; // ebx int v170; // ecx int v171; // eax int v172; // ecx int v173; // ebx int v174; // ecx int v175; // edx int v176; // ebx int v177; // ebx int v178; // eax int v179; // edx int v180; // ecx int v181; // eax int v182; // edx int v183; // eax int v184; // eax int v185; // ebx int v186; // ecx int v187; // ebx int v188; // edx int v189; // ecx int v190; // ebx int v191; // eax int v192; // ecx int v193; // eax int v194; // edx int v195; // ecx int v196; // edx int v197; // ecx int v198; // eax int v199; // edx int v200; // eax int v201; // ecx int v202; // ebx int v203; // eax int v204; // ebx int v205; // eax int v206; // ecx int v207; // edx int v208; // ebx int v209; // eax int v210; // edx int v211; // eax int v212; // eax int v213; // ecx int v214; // ebx int v215; // edx int v216; // ecx int v217; // edx int v218; // ebx int v219; // eax int v220; // ecx int v221; // eax int v222; // ebx int v223; // eax int v224; // ecx int v225; // ecx int v226; // ebx int v227; // eax int v228; // ebx int v229; // ecx int v230; // edx int v231; // eax int v232; // ecx int v233; // edx int v234; // ecx int v235; // edx int v236; // edx int v237; // ecx int v238; // eax int v239; // ebx int v240; // edx int v241; // ebx int v242; // edx int v243; // ebx int v244; // eax int v245; // ecx int v246; // eax int v247; // edx int v248; // eax int v249; // ebx int v250; // ecx int v251; // eax int v252; // ecx int v253; // ebx int v254; // ecx int v255; // edx int v256; // eax int v257; // edx int v258; // ecx int v259; // eax int v260; // edx int v261; // ebx int v262; // ecx int v263; // ecx int v264; // eax int v265; // edx int v266; // eax int v267; // ecx int v268; // ebx int v269; // edx int v270; // eax int v271; // ebx int v272; // edx int v273; // eax int v274; // ecx int v275; // ebx int v276; // ecx int v277; // eax int v278; // edx int v279; // ecx int v280; // edx int v281; // eax int v282; // ecx int v283; // ebx int v284; // eax int v285; // edx int v286; // ebx int v287; // eax int v288; // ecx int v289; // eax int v290; // ebx int v291; // eax int v292; // ecx int v293; // ebx int v294; // edx int v295; // eax int v296; // ebx int v297; // ecx int v298; // edx int v299; // ebx int v300; // eax int v301; // ebx int v302; // eax int v303; // ebx int v304; // edx int v305; // eax int v306; // ecx int v307; // eax int v308; // ebx int v309; // ecx int v310; // ebx int v311; // ecx int v312; // ebx int v313; // ecx int v314; // edx int v315; // eax int v316; // edx int v317; // ebx int v318; // ecx int v319; // eax int v320; // edx int v321; // ebx int v322; // ecx int v323; // edx int v324; // ebx int v325; // edx int v326; // eax int v327; // edx int v328; // ebx int v329; // eax int v330; // eax int v331; // eax int v332; // edx int v333; // ebx int v334; // eax int v335; // ecx int v336; // ebx int v337; // ecx int v338; // eax int v339; // ecx int v340; // ebx int v341; // ecx int v342; // eax int v343; // ecx int v344; // eax int v345; // ebx int v346; // eax int v347; // eax int v348; // ecx int v349; // eax int v350; // ecx int v351; // ebx int v352; // edx int v353; // eax int v354; // ecx int v355; // ebx int v356; // edx int v357; // ebx int v358; // edx int v359; // eax int v360; // ebx int v361; // edx int v362; // ecx int v363; // eax int v364; // ebx int v365; // ecx int v366; // ebx int v367; // ecx int v368; // ebx int v369; // ecx int v370; // edx int v371; // eax int v372; // edx int v373; // ecx int v374; // eax int v375; // edx int v376; // ecx int v377; // ebx int v378; // eax int v379; // edx int v380; // ebx int v381; // edx int v382; // ebx int v383; // edx int v384; // eax int v385; // ebx int v386; // edx int v387; // ecx int v388; // eax int v389; // edx int v390; // ecx int v391; // ebx int v392; // edx int v393; // eax int v394; // ecx int v395; // ebx int v396; // edx int v397; // ecx int v398; // edx int v399; // eax int v400; // ecx int v401; // eax int v402; // ebx int v403; // ecx int v404; // eax int v405; // edx int v406; // ebx int v407; // eax int v408; // ecx int v409; // edx int v410; // ebx int v411; // ebx int v412; // ecx int v413; // eax int v414; // ebx int v415; // ecx int v416; // edx int v417; // ecx int v418; // ebx int v419; // eax int v420; // ecx int v421; // eax v4 = c2 + c1 - c4 - 2133754790 + 1 + 2 * c2 - 1785093898; v5 = 2 * (c4 + 1 - c3) - c2 - 1917226979 - (c2 + c1 - c4 - 2133754790 + 2 * c2) + 2 - (c3 + 539193617) - v4 - 350898193; v6 = c3 + 539193617 - v5 - 879839410; v5 += 2; v6 += 3; v4 += 2109602273; v7 = 2 * (2 * (c2 + c1 - c4 - 2133754790 + 3 - (c3 + 539193617) - 1164434189 - v5 - v6) + 1); v8 = 2 * v6 - 1873426435 - v7; v9 = v7 - v4 + 1; v10 = v4 - 971527202 + 730640080; v11 = v5 + 1150557381 - v10 - 1412696239 + 1; v12 = v11 + v8 - 396431529 + 1; v13 = v9 + 2 - v12 + 1; v14 = v12 + v11 + 1; v15 = v10 + 204474460 - v13 - 1432203755; v16 = v14 + v13 + 884313224 + 813147417; v17 = v15 + v14 + 2; v18 = v15 + v12 - 451236562 + 3; v19 = v16 + v15 + 1; v17 += 138511611; v16 += 953411192; v20 = v16 + v18; v21 = v17 + 2 * (v19 + 1) + 119463169; v17 -= 738693819; v22 = v16 + 594867870 - v17 + 1413353867; v23 = v17 - v22; v22 += 520753425; v24 = v22 + v21 + 144745048; v25 = v24++ + v22 - 1828520841 + 1; v26 = v20 + 3 - v24; v27 = v24 - v26; v28 = v27 + v26 + 763465995; v29 = 2 * (v27 + 2); v30 = v29 + v25 + 1017115747; v31 = v23 - 879256061 + 1336943267 - v30; v32 = v30 + v30 + v28 + 1689547303 - 1018772533 + 1; v33 = v30 - v31 + 1; v34 = v31 + v29 - v33 + 909973850; v35 = v34 + v32 + 228062414; v36 = v35 + v31 + 347278668; v35 -= 720946967; v37 = 2 * (v34 + 1 - v35); v38 = v37 + v35 + 1; v37 += 1888994439; v39 = 2 * (v33 + 579771010) + 2103615418; v40 = v36 - 276265002 - v37 + 1864035437; v41 = v38 + 1 - v39; v37 += 1786144130; v42 = v39 + 1600574700 - v37; v43 = v37 - v40; v44 = v43 + v40 - v41 + 2105473564; v41 *= 2; v45 = 2 * (v44 - v41); v46 = v43 - 1150894576 + 3; v47 = v45 + v41 + 313221810 - v46 + 807301504; v48 = v42 - 124125674 + 1 - v46 + 1 - v46 + 1; v49 = v48 + v46; v50 = v49 + v45 - 468305613 + 3 - 2100928955; v51 = 2 * (v48 - v49 - ++v47) + 1; v52 = v49 + 1 - v47++ + 3; v53 = v52 + v50; v54 = v51 - v53; v55 = v52 - v47 + 1; v53 -= 446157988; v54 += 1553282976; v56 = v54 + v53 + v47 + 1; v53 *= 2; v57 = v53 + v56 - 1230516346 + 1 + 1205548791; v58 = v54 - v53 + 2128637547; v59 = v58 + v55 + 1; v60 = v58 - v57; v57 += 377513439; v61 = v53 - 799999952 - v57; v62 = v61 + v60 + 1; v63 = v61 - v57; v59 += 848132728; v64 = v57 - v63 - v59; v65 = v59 + v62 - 2142680737 + 1764150285; v63 += 2087876122; v66 = v59 + 1814013069 - v63 - v64; v67 = 2 * v64 - v65 + 1132472947; v68 = v63 - 788948114 + 1 - v67; v69 = v68 + v68 + v66 + 1553607352; v67 += 2; v70 = v69 + 2 * v68 + 1518868433; v71 = v67 + v67 + v69 - v70; v72 = v70 + v67; v70 += 713535814; v73 = 2 * v65 + 1429126951 - v70; v70 -= 173942082; v74 = v70 + v72 - 1888550847 + 1 - 394102299; v75 = v71 + 256237465 - v74; v76 = v75 + v70 + 1; v77 = v75 - v74++; v76 += 2140073780; v78 = 2 * (v77 - 1454905092) - 1933992509; v79 = v76 + 2 * (v73 + 1866717529) - v74 - 1310766122 - v78; v80 = v76 - v74; v81 = v80 + v74; v82 = v79 + v80; ++v81; v83 = v82 + v79 + 1; v78 += 1083862846; v84 = v82 + 1 - v81 - v78; v85 = v81 + v78; v81 -= 614253581; v86 = v85 + v84 - 515607244 + 238772881; v87 = v83 + 141351837 - v81 + 1; v88 = v86++ + v85 - 543286513 + 1674408964 - 794464384; v89 = v81 - 623767744 + 215241888; v90 = 2 * (v89++ + v87 + 1710998538); v91 = v86 + v86 + v88 + 1 + 1 - v89++; v92 = v89 + v86; v90 -= 885178085; v91 += 1677704898; v93 = v90++ + v89 - 940635716; v94 = v92 + 1 - v90; v95 = v91 + v90 + 1 + 1841924206; v96 = v91 + v93 + 941760921; v91 += 2; v97 = v96 + 1 - v91 + 1530834091; v98 = v94 - v97 + 1; v99 = v95 + v97 + 1699993484; v100 = v98 + v95 + 1; v101 = v98 + v91 - 523060265 + 1789589531; v102 = v98 + 1281582157 - v100; v100 += 146514254; v103 = v101 - v99 - v100++; v99 += 2080302551; v104 = 2 * (v99 + v103 + 1512882559 + 1); v105 = v99 - v100; v104 -= 784717007; v106 = v104++ + v100 - 1584810020 + 2; v107 = v104 + v105 + 1; v106 += 1065502423; v102 += 3; v108 = v102 + v104 - v107 - v106 + 342809982; v107 -= 1412780444; v109 = v108 + v106 + 1 - 858330204; v110 = v109 + v108; v102 -= 664953144; v111 = v109 - 1329716196 - v102; v112 = v102 - v107; v111 += 1373514701; v113 = v107 - 1346592359 + 216683527 - v111; v114 = v111 + v110 - 288276575 + 1500011784; v115 = v113 + v111; v116 = v115++ + v114; v117 = 2 * (v113 - 1163128426); v118 = v117 + v112 - 818961183 - v115 - 593940334; v119 = v118 + v115 + 2; v118 += 428412235; v120 = v117 + 3 - v118; v121 = v116 + 4 - v118 + 1; v118 += 894601604; v122 = v118 + v121; v123 = v122 + v119 + 1 + 443477999; v124 = v123 + v122; v125 = 2 * v118 - v123; v126 = v124 + v123 - 2061231162; v127 = v124 - v125; v128 = v126 + v120 + 1485909680 + 1483310720; v129 = v128 + v127 + v125 - 1355157173 + 1; v130 = v128 + v127; v128 += 2; v131 = v129 - v128; v130 += 1683851829; v132 = v128 - v131 - 354913611; v133 = v131 - v132; v132 -= 198220312; v134 = v133 + 172443045 - v132; v135 = v126 + 3 - v132 - v134; v136 = v130 + 2 * (v132 - v134) - v135 + 471821392; v137 = v130++ + v134 - v136; v136 += 923861112; v138 = v130 + 2 * (v137 + 1) + 1 - 1146928935 + 1 - v136; v139 = v138 + 2 * v130 + 1; v140 = v136 - 1156737329 - v138 + 2 - v138 - v139; v141 = v140 + v138 + 1; v142 = v135 - 608570200 + 1 - v141 + 1; v139 += 2; v143 = v140 - 1777203220 + 1; v144 = v139 + v141 - v142 - 440487739 + 182778494 - v143; v145 = v139 + 966597185 - v144; v142 += 967980219; v146 = v144 - 1652140998 + 1; v147 = v142 + v145 - 1363945608 + 1 - v146; v148 = v143 - v146 + 1350186086; v149 = 2 * (v148 + 2 * v142); v150 = v146 + 1 - v147 - 457990213; v151 = v148 + 1 - v150; v152 = v150 - 504705392 + 1; v153 = v147 + 1193758906 - v152; v154 = v149 + 1 - v152 + 144039938 - v153; v155 = v154 + v153; v154 += 2; v156 = v152 + 2078215581 - v154 + 1; v157 = v156 + v155 - 122946150 + 301662336; v158 = v156 - v157; v154 += 2; v159 = v157++ + v158 + v151 - 958001904 + 1284137460 + 1; v160 = v154 + v158 + 1002156873 - v157 + 170108160; v161 = v154 - 1014383826 + 161227700; v162 = v159 - v161; v163 = v160 - 255510393 + 376777367; v164 = v157 - v162 + 2; v165 = v163 + v161 - 1912551381 + 1; v166 = v165 + v162 - v163 + 1 + 1; v167 = v166 + v165 + 1; v168 = v163 + v164 + 201934410 + 968132783; v169 = v163 - v168++; v170 = v166 - v167; v171 = v168 + v167; v172 = v170 - v168; v168 += 2029379458; v173 = v168 + v169 - 1763166604 + 1 + 1; v171 -= 1188417209; v174 = v172 + 1 - v173 + 1; v175 = v174 + v168 + 2140747580 - v171 + 668304081; v176 = v173 - 26185106 + 474549714 - v174++; v177 = v176 - v174; v178 = v175 + v171 + 1; v179 = v178 + v175; v180 = v178 + v177 + v174 + 1 + 2141394379; v181 = v178 - 826788372 + 3; v182 = v181 + v179 + 1; v177 += 741838009; v183 = v177 + v181; v177 *= 2; v184 = v183 - 238554347 - v177 + 932383584; v185 = v184 + v177 + 2100277479; v186 = v185 + v180 + 54142085 - v182 - 1632592373; v187 = v185 - v184 + 579181258; v188 = 2 * (v182 + 1383200762) + 1; v189 = v187 + v188 + v186 - v184 + 1 + 1172965920 + 1; v190 = v187 - 101123714 - v189 + 1 - v189 - 96237627; v188 += 2; v191 = 2 * (v189 + v184 - 207227160 + 4) + 1; v192 = v191 + v189 - v188; v190 += 4; v193 = v191 - 1353895842 + 1; v194 = v190 + v188 + 2123750079 - v193; v190 += 1696689707; v195 = 2 * (v192 + 1 - v190); v196 = v194 + 1 - v195 - 78101511; v190 += 540662868; v197 = v190 + v195 - 1145799797 - v196; v198 = 2 * (v193 - 185780694) + 1; v199 = v198 + v196; v190 += 1255424563; v200 = 2 * (v199 + v198) - 1727929676; v199 += 2; v201 = v190 + v199 + v197; v202 = v190 - 1214148504 + 1; v199 += 401187067; v203 = v200 - 1564098266 + 917389966 - v202 - 1198776331 + 1 - v199; v204 = v201 + v202 + 2 - v203; v205 = v203 - 318781264 - v199 - 1605668317 + 2; v206 = 2 * (v201 + 1844554225) - 1604774369; v207 = 2 * (v205 + v199 + 790825996 - v206) + 1650229900; v208 = v204 - 490598907 + 1; v206 += 282040833; v209 = v206 + v205 - 2006766853 - v208; v206 += 2; v210 = v207 + 1511399432 - v208 - 1551102207; v211 = v206 + v206 + v209 - v210 - v208; v206 += 1215172648; v210 -= 959608047; v212 = v211 + 1 - v206; v213 = v206 - v212 - v210; v214 = v212 + v208 + 1869175045 - v213 - 1424027273; v215 = v210 + 1620160695 - v214; v216 = v214 + v214 + v213 - v215 - 1065981445; v217 = 2 * (v215 - 1244977230) + 1747029779; v216 -= 1257866941; v218 = v214 + 2143814783 - v216 - 1398907650; v219 = 2 * v212 + 2 - v217++; v220 = v219 + v216 + 1 - v218 + 1 - v217 - v217; v221 = v219 - v217++; v222 = v218 - 1855122676 + 1; v223 = v222 + v221 + 2; v224 = v223 + v217 + v220 - 1317237096; v217 += 2; v225 = v222 + v224; v226 = v222 - v225 - 777710099; v227 = v223 - 730911683 - v226; v228 = v227 + v226 + 1; v229 = v217 + v227 + v225 - 1217941265; v230 = v217 - v228; v231 = v230 + v228 + v227 - 1682643877; v228 += 2; v232 = v230 + v229 + 1938596261 + 1 - v228; v228 += 584042825; v233 = v230 - 2139100084 + 2; v234 = v233 + v232 + 1; v235 = 2 * (v228 + v233) + 1; v228 += 1437309881; v236 = v228 + v234 + v235 + 1 + 1; v228 -= 716828805; v237 = 2 * (v234 + 1 - v228) - 685322476; v238 = v236 + v231 - 1381742058 + 1995963757 + 2; v239 = v228 - 1516409973 + 1147924830; v240 = v236 + 1 - v238 - v239 - 2104005844; v241 = v239 + 1 - v240; v238 -= 759057394; v242 = v240 - v238; v238 -= 623914540; v243 = v241 - v238; v244 = v238 - v243; v243 += 237287396; v245 = v243 + 2 * (v237 + 1002096745) - 2048248416 + 1892930438; v246 = 2 * v244 + 1294486749; v247 = v242 + 1612687194 - v243 - 660996117 - v246; v248 = v247 + v246 + 720558110; v247 += 977714025; v248 -= 1491378659; v249 = v243 + 1945659396 - v247; v250 = v245 + 1 - v248; v251 = v249 + v248 + 1185773403; v252 = v250 - v249 + 1; v253 = v251 + v249 + 401286047; v254 = v252 - 998849865 + 1; v255 = 2 * v247 + 754645442 - v254; v256 = v251 - 1424315697 + 2 - v255; v257 = v255 - v256; v256 -= 1309666088; v258 = v256 + v254 - v253; v259 = v256 + 1 - v253 - 2033562943; v260 = v257 + 1650643934 - v259 - 1415290431; v261 = v260++ + v253 + 524627955; v262 = v260 + v258 + 2013559893; v260 -= 824578413; v261 -= 446217575; v263 = v262 + 508608480 - v261 + 1345436449; v264 = v259 + 1403184861 - v260 + 1284484219; v265 = v263 + v260; v263 -= 242016614; v266 = v264 + 1347235185 - v263; v267 = v266 + v263 + 1 + 787180614; v266 += 606099305; v268 = 2 * (v261 + 520953472) + 165941725; v269 = v268 + v265 - 1534490202 - v266 + 1; v267 += 2120509468; v270 = v266 + 689980400 - v269 - 2044475833 - v269; v269 -= 1625687532; v271 = v268 + 1 - v269 + 1; v272 = v270 + v269 + 1252726713; v273 = v270 - v267 + 1; v274 = 2 * v267 - v271 + 1; v272 += 531933468; v275 = v271 - v274 - v274 + 2039136993; v276 = v274 - v272; v277 = v273 - 1600087378 + 1; v278 = v275 + 2 * (v272 + 1); v279 = v276 + 2 - v278; v280 = v277 + v278; ++v275; v281 = v277 - 1762733020 - v279 + 1; v280 += 1278825738; v282 = v279 + 147538177 - v275; v283 = v275 - v281; v284 = v281 + 693844065 - v280; v285 = v280 - ++v282; v286 = v282 + v283 + 1 - v284; v287 = v284 - v285 - 74089317; v288 = v282 - v287 - v286 - 681820438; v289 = v287 - 1256120859 + 149723392 - v288; v290 = v286 - 1421591606 - v289 + 2; v285 += 1989232579; v291 = v289 + 1 - v290; v292 = 2 * (v288 - 685621057) + 1; v293 = v290 - v291 - v285; v294 = v285 - v292; v295 = v291 - 1661767175 + 42969351; v296 = v294 + v294 + v293 - 1972384502 + 2 - 1576459347 + 1; v297 = v295 + v292 - 396668767 - 1534437557; v298 = v296 + v294 - 1645192742 - v295 - 1479631423 + 2 - 331301694; v299 = v296 - 106622097 + 668588646; v300 = 2 * (v299 + v297 + v295 + 1 + 2) - 1263581112; v297 += 1979779660; v301 = v297 + v300 + v299 + 1343345468 + 481569519; v298 += 861842343; v302 = v298 + v300 - 1650922112 + 1803040625 - 1103549091 + 1; v303 = v298 + v301 - 263499248; v304 = v303 + v298; v305 = v304++ + v302 - 438171503; v306 = v297 - 2076009387 + 1524090740 - v304 + 1; v304 += 575953311; v307 = v305 + 1306759242 - v306; v308 = v303 - 429496975 + 1812284714 - v307++; v309 = 2 * (v306 + 1523384106) - 1468869015; v310 = v304 + v308 + 1260443893 - v309; v311 = v309 - 1158775838 - v307++; v312 = 2 * v310 - 441360349; v313 = v311 - v312; v314 = v313++ + v307 + v304 - 1384238736; v315 = 2 * v307 - v313 + 496097820; v316 = v315 + v314 + 2 + 1; v315 += 3; v317 = 2 * (2 * v312 + 3 - v316 + 2070720611) - 1285251516 + 88029981; v318 = v315 + v313 - 1389710860; v319 = v315 - v318; v320 = v319 + v319 + v316 - 589472948 + 1; ++v319; v321 = 2 * v317 + 942166371; v322 = v320 + v318 - 344804349 + 849785358; v323 = v320 - v321; v322 += 53013894; v324 = v321 - v319; v325 = v322 + v323 + 1; v326 = 4 * (v325 + v319 - v322 - 2049097191 + 1); v322 -= 1029516387; v327 = v325 + 473722879 - v322; v326 -= 1652737171; v328 = v324 + 1 - v326; v329 = v326 - v327; v322 += 1088562794; v327 += 78577575; v330 = v329 - v322 - v327 + 132302044; v328 -= 771106090; v322 += 2; v331 = v330 + 1 - v328; v332 = v327 - v331; v333 = v332 + v328; v332 += 1152138250; v334 = v322 + v331 - 1557943841 + 1; v335 = v332 + v322 - v334; v336 = v333 + 1293271530 - v332 - v335; v334 += 245975965; v337 = v334 + v335 + 2098061773; v332 += 1210065134; v338 = v334 - v336 + 1; v339 = v337 + 1 - v332 + 1042845593; v332 += 1017773432; v340 = v336 - v338 + 544855734; v341 = v340 + 2 * v339 + 1636319835; v340 -= 2122376282; v342 = v332 + v332 + v338 - 213405862; v332 += 1914409404; v343 = 2 * (v341 + 1) - v340 + 1026384791; v344 = v332 + v342 - 207594250 + 1367733505 - v340; v345 = v340 - v343++ + 1434173388; v346 = v344 - 1373169356 - v332++; v347 = v332 + v346 - 1698350246 + 807585909 - v343 - 1616726979; v348 = v343 - v332; v349 = v348 + v347; v350 = v349++ + v348 + 1; v351 = 2 * (v345 - 28630427 + 560310549 - v350 + 1) + 1587875006 - v349 - 258344410; v352 = v332 - 390257379 + 1 - v349; v353 = v352 + v349; v354 = v352++ + v350 + 4; v355 = v351 + 1 - v352; v354 -= 1828963202; v356 = v354 + 2 * v352 + 1; v357 = v356 + v355 - 1265518153 + 1354618067; v358 = v354 + v356 - v357; v354 += 24457593; v358 += 2081985567; v359 = v354 + v353 + 1624829730 + 1; v360 = 2 * (v358 + v357 - 1949374989) + 781522725; v361 = ++v354 + v358; v362 = v354 + 1493767541 - v359; v361 += 4; v363 = 2 * v359 - v361 - 1727523967; v364 = v362 + v361 + v360 + 218569202 - v363 + 449916241; v363 += 327109260; v365 = v362 + 1549803007 - v363 + 957128236; v363 += 916862246; v366 = 2 * (v364 + 414246669) + 2040411505; v367 = v365 + 536918145 - v366; v368 = v366 - v367 + 1; v369 = 2 * (v367 - 129161079 - v363 + 1) - 1900300983; v370 = 2 * (v361 - 798140456) + 4; v371 = v370 + v363 - 665700202 + 2; v369 += 30341174; v372 = v370 + 1 - v369 - 1952101394; v373 = v372 + v369 - v371; v374 = v373 + v371; v375 = 2 * (v374 + v372 + 1 - 1317292497); v376 = v373 - 2112199059 + 419983391; v377 = v376 + 2 * (v368 - 1343830370 + 442537035) - 1033591519 + 1879391070 - v375 - 2060553041 + 1; v378 = v375 + v375 + v374 + 738693954 + 1 - v377; v379 = v375 + 1 - v377; v380 = v377 - 1930629742 - v379 - 1928040188 + 1102478597; v381 = v380 + 2 * v379 + 1 - 448866693; v376 -= 2055000006; v382 = 2 * (2 * (v380 + 604066061) + 2); v383 = v382 + v376 + v381 - 1881910858; v384 = v378 - 947925440 + 2; v385 = v384 + v383 + v382 + 842529404 + 1; v376 -= 168380786; ++v384; v386 = v376 + v383 + 1; v387 = v376 - v384 + 1919090703; v388 = 2 * (v384 + 1 - v386 + 1) - 376075359 - v387; v389 = v388 + v386 - 1078951762; v388 += 4; v390 = v388 + v387 + 1; v388 += 1398399335; v391 = v390 + v385 - 1475310267 - v389 + 1 + 9540715; v392 = v389 - 1641637778 + 2 - v388 + 2; v393 = v388 - 1972219276 + 1; v392 -= 866747255; v394 = v392 + v390 - 1373171668 + 1586106979; v395 = v393 + v391 - 931348898; v396 = v394 + v392 - 1900058436 - v395; v397 = 2 * v394 + 1334476417; v398 = v396 - 467332541 + 1817029648; v399 = 2 * (v393 + 808026034) - 1047285892 + 609483421 - v397; v395 += 1953163588; v400 = v397 - 292607806 - v399 - 42192282 - v395 + 1; v401 = v399 + 1 - v398 + 1; v402 = v395 + 1 - v401; v403 = v400 - v402; v404 = v401 + 1 - v403 + 1872425146; v405 = v398 - 195196821 + 377105645 - v404 - v404; v406 = v402 - v404; v407 = v404 - 1842186611 + 547686199; v408 = v407 + v403 + 1374530959 - v405 + 1 - 184314042; v409 = v405 - 1871472347 + 1; v410 = v409 + v406; v409 += 2027045620; v411 = v409 + v410 - 855357098 + 1 - 2037318886; v412 = v411 + v408 + 1324830997 - v409 + 1863672173; v413 = 2 * (v407 - 1311778367) + 1; v414 = v413 + v411; v409 += 638982232; v415 = v412 - 1420319999 - v409 + 1706741566; v413 += 2; v416 = v409 + 600153250 - v415 - 1749613292 + 1; v417 = v415 - v413; v418 = v414 - 103143630 + 151909657 - v416 + 1; v419 = 2 * v413 - v416++; v420 = v417 + 1 - ++v419; v421 = v418 - v416 - v416 + v419; v420 -= 1385665685; return v420 + v421 + 1 + 640484926 + 2 * (v421 + v416 + 1815285368) + v420 + v421 + 1 + 640484926 + v420 + v421 + 1 + v420 + v421 + 1 + v420 - 1389466703 + 495424244 + 2 * (v420 + v421 + 1 + 640484926); }
As you can see, Hex-Rays was not helpful in that case. Since IDA and Hex-Rays are highly programmable, one can actually improve the output of the Hex-Rays decompiler and teach it to simplify those expressions (a topic for another time).
So as you can see, unless we approach this function as a blackbox algorithm, we have no real understanding of its operation yet. We are going to use Z3 and see if it can simplify all of those instructions into something approachable.
Quick Z3 Primer
Dennis and Eric did a good job introducing Z3, therefore I will keep my primer very short.
Imagine the following assembly listing:
.text:0040EF04 mov eax, [ebp+c1] .text:0040EF07 mov edx, [ebp+c2] .text:0040EF0A mov ecx, [ebp+c3] .text:0040EF0D mov ebx, [ebp+c4] .text:0040EF10 inc eax .text:0040EF11 inc ebx .text:0040EF12 inc edx .text:0040EF13 inc ecx .text:0040EF14 add ebx, edx .text:0040EF16 add ecx, 123h .text:0040EF1C add eax, ebx .text:0040EF1E add ebx, 456h .text:0040EF24 add edx, eax .text:0040EF26 add ecx, eax .text:0040EF28 add edx, ebx .text:0040EF2A sub eax, 12312312h .text:0040EF2F add ecx, eax .text:0040EF31 add eax, ebx .text:0040EF33 add eax, ecx .text:0040EF35 add eax, edx .text:0040EF37 mov [ebp+result], eax
From 0x040EF04 to 0x040EF0D, we see that eax==c1, edx==c2, ecx==c3, ebx==c4 (4 input arguments). From 0x040EF10 to 0x040EF35, we see some operations taking place and the result is copied to eax at 0x040EF37.
Mathematically speaking, we can translate the above listing into a series of expressions:
.text:0040EF10 eax = eax + 1 .text:0040EF11 ebx = ebx + 1 .text:0040EF12 edx = edx + 1 .text:0040EF13 ecx = ecx + 1 .text:0040EF14 ebx = ebx + edx .text:0040EF16 ecx = ecx + 0x123 .text:0040EF1C eax = eax + ebx .text:0040EF1E ebx = ebx + 0x456 .text:0040EF24 edx = edx + eax .text:0040EF26 ecx = ecx + eax .text:0040EF28 edx = edx + ebx .text:0040EF2A eax = eax - 0x12312312 .text:0040EF2F ecx = ecx + eax .text:0040EF31 eax = eax + ebx .text:0040EF33 eax = eax + ecx .text:0040EF35 eax = eax + edx
Let’s now give those expressions to Z3 (note that Z3 overloads the arithmetic operators):
import z3 c1, c2, c3, c4 = z3.BitVecs('c1 c2 c3 c4', 32) eax, edx, ecx, ebx = c1, c2, c3, c4 eax = eax + 1 ebx = ebx + 1 edx = edx + 1 ecx = ecx + 1 ebx = ebx + edx ecx = ecx + 0x123 eax = eax + ebx ebx = ebx + 0x456 edx = edx + eax ecx = ecx + eax edx = edx + ebx eax = eax - 0x12312312 ecx = ecx + eax eax = eax + ebx eax = eax + ecx eax = eax + edx print(eax)
The final expression is:
c1 + 1 + c4 + 1 + c2 + 1 - 305210130 + c4 + 1 + c2 + 1 + 1110 + c3 + 1 + 291 + c1 + 1 + c4 + 1 + c2 + 1 + c1 + 1 + c4 + 1 + c2 + 1 - 305210130 + c2 + 1 + c1 + 1 + c4 + 1 + c2 + 1 + c4 + 1 + c2 + 1 + 1110
However, we can still ask Z3 to simplify the expression by calling z3.simplify(eax)
and get the following simpler output:
3684549565 + 4*c1 + 6*c4 + 7*c2 + c3
Now that we have the final expression, we can evaluate its value like this:
solver = z3.Solver() result = z3.BitVec('result', 32) solver.add(c1 == 1, c2 == 2, c3 == 3, c4 == 4, eax == result) if solver.check() == z3.sat: m = solver.model() print("result=%08X" % m[result].as_long())
Essentially, we are asking the solver to find the result of the expression (eax == result)
given that c1 == 1
, c2 == 2
, c3 == 3
and c4 == 4
. The output is result=DB9DC3F1
.
Converting the assembly listing to a Z3 expression
Now that we know how to manually build an expression and ask Z3 to simplify and evaluate it, can we automatically generate the expression from the disassembly listing?
The answer is Yes and we are going to use a similar technique to the emulation article from last week. Instead of computing the values, we will simply be doing Z3 arithmetics:
def simplify_func(emu_start, emu_end): # Reset registers regs_initial = { REG_EAX: z3.BitVec('c1', 32), REG_EDX: z3.BitVec('c2', 32), REG_ECX: z3.BitVec('c3', 32), REG_EBX: z3.BitVec('c4', 32), } regs = {} for k, v in regs_initial.items(): regs[k] = v def get_opr_val(inst, regs): if inst.Op2.type == o_imm: return (True, z3.BitVecVal(inst.Op2.value, 32)) elif inst.Op2.type == idaapi.o_reg: return (True, regs[inst.Op2.reg]) else: return (False, 0) ea = emu_start while ea <= emu_end: ok = True inst = idautils.DecodeInstruction(ea) ea += inst.size if inst.itype == idaapi.NN_dec and inst.Op1.type == idaapi.o_reg: regs[inst.Op1.reg] = (regs.get(inst.Op1.reg, 0) - 1) elif inst.itype == idaapi.NN_inc and inst.Op1.type == idaapi.o_reg: regs[inst.Op1.reg] = (regs.get(inst.Op1.reg, 0) + 1) elif inst.itype == idaapi.NN_sub: ok, val = get_opr_val(inst, regs) regs[inst.Op1.reg] = (regs.get(inst.Op1.reg, 0) - val) elif inst.itype == idaapi.NN_add: ok, val = get_opr_val(inst, regs) regs[inst.Op1.reg] = (regs.get(inst.Op1.reg, 0) + val) else: ok = False if not ok: return (False, "Emulation failed at %08X" % ea) # Simplify the final expression which is in EAX result_expr = z3.simplify(regs[REG_EAX]) def evaluate(c1, c2, c3, c4): """Capture the context and return a function that can be used to evaluate the simplified expression given the input arguments""" s = z3.Solver() r = z3.BitVec('r', 32) # Add contraints for input variables s.add(regs_initial[REG_EAX] == c1, regs_initial[REG_EDX] == c2, regs_initial[REG_ECX] == c3, regs_initial[REG_EBX] == c4) # Add the result constraint s.add(result_expr == r) if s.check() == z3.sat: m = s.model() return m[r].as_long() else: return None return (True, evaluate)
The code above is very similar to what we have seen before, so I will only explain Z3 related code:
- Lines 3-8: Create 32-bits Z3 variables. These variables correspond to the initial variables values (and the input values)
- Lines 10-12: Aliases the variables. Those aliases will be updated down the line and will contain more complicated expressions (not just the initial values)
- Line 44: Get a simplified version of the final expression
- Lines 46-63: Create a nested function that captures the current context. The
evaluate
function takes 4 input arguments and returns the evaluation result of the simplified expression. I return a function so that I can cache it and call it to evaluate functions in question.
To test the code, we can do something like:
Python>ok, challenge_1 = simplify_func(0x401020, 0x40266C) Python>print('result=%08X' % challenge_1(1, 2, 3, 4))
We get 5E6571B0
. If the code works correctly, we should also have the same result as running the program:
C:\ida-z3-tests>test 0 1 2 3 4 challenge_1(1, 2, 3, 4) -> 5E6571B0
You can download the full script + binary from here:
Thanks to all those who are contributing knowledge and code to the infosec community.
You might also like: