├── LICENSE ├── README.md ├── decrypter.py └── fupy.py /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | python-decrypter 2 | ================ 3 | 4 | python command line tool to decrypt and decompile bytecode, compatible with python 2.7 5 | 6 | 7 | ### Usage 8 | Download *decrypter.py* and *fupy.py* in the same directory then run: 9 | ``` 10 | > python decrypter.py path/yourfile.pyc 11 | ``` 12 | In *path* directory will be generated the decrypted file *_.pyc* and the decompiled file *_.py* 13 | 14 | ### Features 15 | * work also with file with old python version(magic number) 16 | * bypass stupid algorithms that obfuscate marshal dumps, doesn't matter how it's done..the code is retrived from memory 17 | 18 | ### Thanks 19 | Fupy small and dirty decompiler: https://github.com/gdelugre/fupy 20 | -------------------------------------------------------------------------------- /decrypter.py: -------------------------------------------------------------------------------- 1 | import marshal, struct, sys, time, py_compile, traceback, fupy, os 2 | 3 | def unpack_pyc(filename): 4 | f = open(filename, "rb") 5 | magic = f.read(4) 6 | unixtime = struct.unpack("L", f.read(4))[0] 7 | timestamp = time.asctime(time.localtime(unixtime)) 8 | code = marshal.load(f) 9 | f.close() 10 | return filename, magic, unixtime, timestamp, code 11 | 12 | def mydecomp(decrypted_name,decompyled_name): 13 | indent_pattern = ' ' * 4 14 | pydec = fupy.PythonDecompiler(decrypted_name) 15 | dump = pydec.decompile(indent = indent_pattern) 16 | output = open(decompyled_name, "w") 17 | output.write(dump) 18 | output.close() 19 | 20 | def mydecrypt(code,decrypted_name,decompyled_name): 21 | with open(decrypted_name, 'wb') as fc: 22 | fc.write(py_compile.MAGIC) 23 | py_compile.wr_long(fc, long(time.time())) 24 | marshal.dump(code, fc) 25 | fc.flush() 26 | fc.seek(0, 0) 27 | mydecomp(decrypted_name,decompyled_name) 28 | 29 | 30 | def do(name): 31 | pname = os.path.dirname(os.path.abspath(__file__))+"\\"+name 32 | crypted_name = pname + ".pyc" 33 | decrypted_name = pname + "___decrypt.pyc" 34 | decompyled_name = pname + "___decrypt.py" 35 | sys.path.append(os.path.dirname(pname)) 36 | try: 37 | cur_module = __import__(os.path.basename(pname)) 38 | filename, magic, unixtime, timestamp, code = unpack_pyc(crypted_name) 39 | mydecrypt(code,decrypted_name,decompyled_name) 40 | except: 41 | traceback.print_exc(file=sys.stdout) 42 | exc_traceback = sys.exc_info()[2] 43 | trkobj = exc_traceback 44 | while(trkobj.tb_next is not None): 45 | trkobj = trkobj.tb_next 46 | code = trkobj.tb_frame.f_code 47 | mydecrypt(code,decrypted_name,decompyled_name) 48 | 49 | first = True 50 | for file in sys.argv: 51 | if first: 52 | first = False 53 | continue 54 | pythonFolder = os.path.dirname(os.path.abspath(__file__)) 55 | modulePath = file.replace(pythonFolder+'\\',"") 56 | do(os.path.splitext(modulePath)[0]) -------------------------------------------------------------------------------- /fupy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Decompiles a .pyc or .pyo file into a human-readable .py source file. 5 | 6 | fupy is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | fupy is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with fupy. If not, see . 18 | 19 | Contact: Guillaume Delugre 20 | """ 21 | 22 | import sys 23 | import imp, dis, opcode, marshal 24 | import types 25 | import __builtin__ 26 | 27 | class Statement: 28 | """ Python generic statement. """ 29 | def __init__(self, indent_level = 0): 30 | self.indent_level = indent_level 31 | 32 | def set_indent_level(self, level): 33 | self.indent_level = level 34 | 35 | def make_indent(self, indent): 36 | return indent * self.indent_level 37 | 38 | def __eq__(self, s): 39 | if isinstance(s, Statement): 40 | saved_indent_levels = self.indent_level, s.indent_level 41 | self.set_indent_level(0) 42 | s.set_indent_level(0) 43 | if hasattr(self, 'parenthesize'): 44 | saved_self_parenthesize = self.parenthesize 45 | self.parenthesize = True 46 | if hasattr(s, 'parenthesize'): 47 | saved_other_parenthesize = s.parenthesize 48 | s.parenthesize = True 49 | 50 | equals = self.write(' ') == s.write(' ') 51 | 52 | self.set_indent_level(saved_indent_levels[0]) 53 | s.set_indent_level(saved_indent_levels[1]) 54 | if hasattr(self, 'parenthesize'): 55 | self.parenthesize = saved_self_parenthesize 56 | if hasattr(s, 'parenthesize'): 57 | self.parenthesize = saved_other_parenthesize 58 | return equals 59 | else: 60 | return False 61 | 62 | def __ne__(self, s): 63 | return not (self == s) 64 | 65 | @staticmethod 66 | def auto_indent(writer): 67 | def do_indent(self, indent = ''): 68 | return indent * self.indent_level + writer(self, indent) 69 | return do_indent 70 | 71 | class Block(Statement): 72 | """ Python compound statement. Contains one or more indented block of statements. 73 | Can be 74 | - if/elif/else 75 | - for in..else 76 | - while..else 77 | - try..except..else..finally 78 | - with 79 | - def 80 | - class 81 | """ 82 | def __init__(self, statements = [], indent_level = 0): 83 | Statement.__init__(self, indent_level) 84 | self.statements = statements 85 | for statement in statements: 86 | statement.set_indent_level(self.indent_level + 1) 87 | 88 | def append_statement(self, statement): 89 | self.statements.append(statement) 90 | statement.set_indent_level(self.indent_level + 1) 91 | 92 | def set_indent_level(self, level): 93 | Statement.set_indent_level(self, level) 94 | for statement in self.statements: 95 | statement.set_indent_level(level + 1) 96 | 97 | def replace_statements(self, match, new, rec = False): 98 | for i, statement in enumerate(self.statements): 99 | if rec and isinstance(statement, Block): 100 | statement.replace_statements(match, new, rec) 101 | if match(statement): 102 | self.statements[i] = new(statement) 103 | self.statements[i].set_indent_level(self.indent_level + 1) 104 | 105 | def delete_statements(self, match, rec = False): 106 | pos = [] 107 | for i, statement in enumerate(self.statements): 108 | if rec and isinstance(statement, Block): 109 | statement.delete_statements(cond) 110 | if match(statement): 111 | pos.insert(0, i) 112 | for i in pos: 113 | self.statements.pop(i) 114 | if len(self.statements) == 0: 115 | self.append_statement(Pass()) 116 | 117 | def write(self, indent = ''): 118 | # Reduce conditional blocks to assert statements when possible 119 | i = 0 120 | while i < len(self.statements): 121 | s = self.statements[i] 122 | next = i < len(self.statements) - 1 and self.statements[i + 1] 123 | if isinstance(s, If) and not isinstance(next, Elif): 124 | if len(s.statements) == 1 and isinstance(s.statements[0], Raise) and s.statements[0].exception == Variable('AssertionError'): 125 | self.statements[i] = Assert(s.expr.expr if isinstance(s.expr, UnaryOp) and s.expr.op == 'not ' else UnaryOp('not ', s.expr), s.statements[0].param) 126 | self.statements[i].set_indent_level(self.indent_level + 1) 127 | if isinstance(next, Else): 128 | self.statements.pop(i + 1) 129 | self.statements[i+1:i+2] = next.statements 130 | for statement in next.statements: 131 | statement.set_indent_level(self.indent_level + 1) 132 | elif isinstance(next, Else) and len(next.statements) == 1 and isinstance(next.statements[0], Raise) and next.statements[0].exception == Variable('AssertionError'): 133 | self.statements[i] = Assert(s.expr, next.statements[0].param) 134 | self.statements[i].set_indent_level(self.indent_level + 1) 135 | self.statements.pop(i + 1) 136 | self.statements[i+1:i+2] = s.statements 137 | for statement in s.statements: 138 | statement.set_indent_level(self.indent_level + 1) 139 | i = i + 1 140 | 141 | if len(self.statements) == 0: 142 | self.append_statement(Pass()) 143 | return "\n".join([statement.write(indent) for statement in self.statements]) 144 | 145 | class Expression(Statement): 146 | """ Python generic expression. """ 147 | def __hash__(self): 148 | return hash(id(self)) 149 | 150 | class ExpressionList(Expression): 151 | """ List of expressions : expr1, expr2, ... """ 152 | def __init__(self, exprs = None): 153 | Expression.__init__(self) 154 | if exprs: 155 | self.exprs = exprs 156 | else: 157 | self.exprs = [] 158 | 159 | def append(self, expr): 160 | self.exprs.append(expr) 161 | 162 | @Statement.auto_indent 163 | def write(self, indent = ''): 164 | return ", ".join([expr.write() for expr in self.exprs]) 165 | 166 | class ConditionalExpression(Expression): 167 | """ Conditional expression : expr1 if cond else expr2 """ 168 | def __init__(self, if_expr, cond_expr, else_expr): 169 | Expression.__init__(self) 170 | self.if_expr = if_expr 171 | self.cond_expr = cond_expr 172 | self.else_expr = else_expr 173 | 174 | @Statement.auto_indent 175 | def write(self, indent = ''): 176 | return self.if_expr.write() + " if " + self.cond_expr.write() + " else " + self.else_expr.write() 177 | 178 | class Assignment(Expression): 179 | """ Assignement expr1 = expr2 """ 180 | def __init__(self, left, right): 181 | Expression.__init__(self) 182 | self.left = left 183 | self.right = right 184 | 185 | @Statement.auto_indent 186 | def write(self, indent = ''): 187 | return self.left.write() + " = " + self.right.write() 188 | 189 | class GetAttr(Expression): 190 | """ Expression attribute expr.attr """ 191 | def __init__(self, expr, attr): 192 | Expression.__init__(self) 193 | self.base = expr 194 | if isinstance(self.base, BinaryOp): 195 | self.base.parenthesize = True 196 | self.attr = attr 197 | 198 | @Statement.auto_indent 199 | def write(self, indent = ''): 200 | return self.base.write() + "." + self.attr 201 | 202 | class Variable(Expression): 203 | """ Expression as variable name """ 204 | def __init__(self, name): 205 | Expression.__init__(self) 206 | self.name = name 207 | 208 | def __hash__(self): 209 | return hash(self.name) 210 | 211 | def __eq__(self, other): 212 | return isinstance(other, Variable) and (self.name == other.name) 213 | 214 | def __ne__(self, other): 215 | return not (self == other) 216 | 217 | @Statement.auto_indent 218 | def write(self, indent = ''): 219 | return self.name 220 | 221 | class Constant(Expression): 222 | """ Expression as constant (e.g "foo", (1,2,x)) """ 223 | def __init__(self, value): 224 | Expression.__init__(self) 225 | self.value = value 226 | 227 | def __hash__(self): 228 | return hash(self.value) 229 | 230 | def __eq__(self, other): 231 | return isinstance(other, Constant) and (self.value == other.value) 232 | 233 | def __ne__(self, other): 234 | return not (self == other) 235 | 236 | @Statement.auto_indent 237 | def write(self, indent = ''): 238 | if isinstance(self.value, types.ListType): 239 | values = [ value.write() if isinstance(value, Expression) else repr(value) for value in self.value ] 240 | out = "[" + ", ".join(values) + "]" 241 | elif isinstance(self.value, types.TupleType): 242 | values = [ value.write() if isinstance(value, Expression) else repr(value) for value in self.value ] 243 | out = "(" + ", ".join(values) 244 | if len(values) == 1: 245 | out += "," 246 | out += ")" 247 | elif isinstance(self.value, types.DictType): 248 | couples = [] 249 | for key, value in self.value.iteritems(): 250 | key = key.write() if isinstance(key, Expression) else repr(key) 251 | value = value.write() if isinstance(value, Expression) else repr(value) 252 | couples.append(key + ": " + value) 253 | out = "{" + ", ".join(couples) + "}" 254 | elif isinstance(self.value, set): 255 | values = [ value.write() if isinstance(value, Expression) else repr(value) for value in self.value ] 256 | out = "{" + ", ".join(values) + "}" 257 | else: 258 | out = repr(self.value) 259 | return out 260 | 261 | class Comprehension(Expression): 262 | """ Comprehension expression : expr1 for vars in expr2 """ 263 | def __init__(self, expr, variables, iterable, if_expr = None): 264 | Expression.__init__(self) 265 | self.expr = expr 266 | self.variables = variables 267 | self.iterable = iterable 268 | self.if_expr = if_expr 269 | 270 | @Statement.auto_indent 271 | def write(self, indent = ''): 272 | var_list = ", ".join([var.write() for var in self.variables]) 273 | out = self.expr.write() + " for " + var_list + " in " + self.iterable.write() 274 | if self.if_expr: 275 | out += " if " + self.if_expr.write() 276 | return out 277 | 278 | class TypedComprehension(Expression): 279 | """ List/set/dict comprehensions """ 280 | def __init__(self, comp): 281 | Expression.__init__(self) 282 | self.comp = comp 283 | 284 | class ListComprehension(TypedComprehension): 285 | """ List comprehension : [expr1 for vars in expr2] """ 286 | @Statement.auto_indent 287 | def write(self, indent = ''): 288 | return '[' + self.comp.write() + ']' 289 | 290 | class SetComprehension(TypedComprehension): 291 | """ Set comprehension : {expr1 for vars in expr2} """ 292 | @Statement.auto_indent 293 | def write(self, indent = ''): 294 | return '{' + self.comp.write() + '}' 295 | 296 | class DictComprehensionEntry: 297 | """ Entry in dict comprehension : key_expr : value_expr """ 298 | def __init__(self, key_expr, value_expr): 299 | self.key_expr = key_expr 300 | self.value_expr = value_expr 301 | 302 | def write(self, indent = ''): 303 | return self.key_expr.write() + ' : ' + self.value_expr.write() 304 | 305 | class DictComprehension(TypedComprehension): 306 | """ Dict comprehension : { key_expr : value_expr for vars in expr } """ 307 | @Statement.auto_indent 308 | def write(self, indent = ''): 309 | return '{' + self.comp.write() + '}' 310 | 311 | class Generator(Expression): 312 | """ Generator expression : (expr1 for vars in expr2) """ 313 | def __init__(self, comp): 314 | Expression.__init__(self) 315 | self.comp = comp 316 | self.parenthesize = True 317 | 318 | @Statement.auto_indent 319 | def write(self, indent = ''): 320 | out = '' 321 | if self.parenthesize: 322 | out += '(' 323 | out += self.comp.write() 324 | if self.parenthesize: 325 | out += ')' 326 | return out 327 | 328 | class UnaryOp(Expression): 329 | """ Expression with unary operator (e.g ~expr) """ 330 | def __init__(self, op, expr): 331 | Expression.__init__(self) 332 | self.op = op 333 | self.expr = expr 334 | self.parenthesize = False 335 | if isinstance(self.expr, BinaryOp) or isinstance(self.expr, UnaryOp): 336 | if self.expr.precedence() < self.precedence(): 337 | self.expr.parenthesize = True 338 | 339 | def precedence(self): 340 | return PythonDecompiler.unary_operator_precedences[self.op] 341 | 342 | @Statement.auto_indent 343 | def write(self, indent = ''): 344 | out = '' 345 | if self.parenthesize: 346 | out += '(' 347 | out += self.op + self.expr.write() 348 | if self.parenthesize: 349 | out += ')' 350 | return out 351 | 352 | class UnaryConvert(Expression): 353 | """ Expression with convert operator (`expr`) """ 354 | def __init__(self, expr): 355 | Expression.__init__(self) 356 | self.expr = expr 357 | 358 | @Statement.auto_indent 359 | def write(self, indent = ''): 360 | return '`' + self.expr.write() + '`' 361 | 362 | class BinaryOp(Expression): 363 | """ Expression with binary operator (e.g expr1 == expr2) """ 364 | def __init__(self, left, op, right): 365 | Expression.__init__(self) 366 | self.op = op 367 | self.left = left 368 | self.right = right 369 | self.parenthesize = False 370 | logical_ops = ( 'and', 'or' ) 371 | if self.op in logical_ops: 372 | neg_op = logical_ops[1-logical_ops.index(self.op)] 373 | if isinstance(self.left, BinaryOp) and self.left.op == neg_op: 374 | self.left.parenthesize = True 375 | if isinstance(self.right, BinaryOp) and self.right.op == neg_op: 376 | self.right.parenthesize = True 377 | else: 378 | if isinstance(self.left, BinaryOp) or isinstance(self.left, UnaryOp): 379 | if self.left.precedence() < self.precedence(): 380 | self.left.parenthesize = True 381 | if isinstance(self.right, BinaryOp) or isinstance(self.right, UnaryOp): 382 | if self.precedence() == self.right.precedence() and not self.commutative(): 383 | self.right.parenthesize = True 384 | elif self.right.precedence() < self.precedence(): 385 | self.right.parenthesize = True 386 | 387 | def commutative(self): 388 | return self.op in PythonDecompiler.commutative_operators 389 | 390 | def precedence(self): 391 | return PythonDecompiler.binary_operator_precedences[self.op] 392 | 393 | def first(self): 394 | if isinstance(self.left, BinaryOp): 395 | return self.left.first 396 | else: 397 | return self.left 398 | 399 | def last(self): 400 | if isinstance(self.right, BinaryOp): 401 | return self.right.last 402 | else: 403 | return self.right 404 | 405 | @Statement.auto_indent 406 | def write(self, indent = ''): 407 | out = '' 408 | if self.parenthesize: 409 | out += '(' 410 | out += self.left.write() + " " + self.op + " " + self.right.write() 411 | if self.parenthesize: 412 | out += ')' 413 | return out 414 | 415 | class BinarySubscr(Expression): 416 | """ Expression expr1[expr2]. """ 417 | def __init__(self, base_expr, index_expr): 418 | Expression.__init__(self) 419 | self.base_expr = base_expr 420 | if isinstance(self.base_expr, BinaryOp): 421 | self.base_expr.parenthesize = True 422 | self.index_expr = index_expr 423 | 424 | @Statement.auto_indent 425 | def write(self, indent = ''): 426 | return self.base_expr.write() + "[" + self.index_expr.write() + "]" 427 | 428 | class FunctionCall(Expression): 429 | """ Function call expression : expr(expr1, expr2, param = expr3) """ 430 | def __init__(self, expr, pos_args = [], key_args = {}, var_args = None, kwvar_args = None): 431 | Expression.__init__(self) 432 | self.function = expr 433 | self.pos_args = pos_args 434 | self.key_args = key_args 435 | self.var_args = var_args 436 | self.kwvar_args = kwvar_args 437 | # rare case where generator does not need parenthesises 438 | if len(self.key_args) == 0 and len(self.pos_args) == 1 and isinstance(self.pos_args[0], Generator): 439 | self.pos_args[0].parenthesize = False 440 | 441 | @Statement.auto_indent 442 | def write(self, indent = ''): 443 | args = ", ".join([arg.write() for arg in self.pos_args ]) 444 | if len(self.key_args) > 0: 445 | if len(args) > 0: 446 | args += ", " 447 | keys = self.key_args.keys() 448 | keys.sort() 449 | args += ", ".join([key + " = " + self.key_args[key].write() for key in keys]) 450 | if self.var_args: 451 | if len(args) > 0: 452 | args += ", " 453 | args += '*' + self.var_args.write() 454 | if self.kwvar_args: 455 | if len(args) > 0: 456 | args += ", " 457 | args += '**' + self.kwvar_args.write() 458 | return self.function.write() + "(" + args + ")" 459 | 460 | class Slice(Expression): 461 | """ Slice expression : start:stop:step """ 462 | def __init__(self, start = None, stop = None, step = None): 463 | Expression.__init__(self) 464 | self.start = start 465 | self.stop = stop 466 | self.step = step 467 | 468 | @Statement.auto_indent 469 | def write(self, indent = ''): 470 | out = '' 471 | if self.start != None and self.start != Constant(None): 472 | out += self.start.write() 473 | out += ':' 474 | if self.stop != None and self.stop != Constant(None): 475 | out += self.stop.write() 476 | if self.step != None and self.step != Constant(None): 477 | out += ':' + self.step.write() 478 | return out 479 | 480 | class Yield(Expression): 481 | """ Yield expression. """ 482 | def __init__(self, expr): 483 | Statement.__init__(self) 484 | self.expr = expr 485 | 486 | @Statement.auto_indent 487 | def write(self, indent = ''): 488 | return "yield " + self.expr.write() 489 | 490 | class Lambda(Expression): 491 | """ Lambda expression: lambda args: expr """ 492 | def __init__(self, args, default_args, statements): 493 | Expression.__init__(self) 494 | self.args = args 495 | self.default_values = default_args 496 | self.statements = statements 497 | self.__remove_returns() 498 | 499 | def __remove_returns(self): 500 | for i, statement in enumerate(self.statements): 501 | if isinstance(statement, Return): 502 | self.statements[i] = statement.expr 503 | elif isinstance(statement, Block): 504 | statement.replace_statements(match = (lambda s: isinstance(s, Return)), new = (lambda s: s.expr), rec = True) 505 | 506 | @Statement.auto_indent 507 | def write(self, indent = ''): 508 | args = [] 509 | first_default = len(self.args) - len(self.default_values) 510 | for i in range(0, first_default): 511 | args.append(self.args[i]) 512 | for i in range(first_default, len(self.args)): 513 | args.append(self.args[i] + " = " + self.default_values[i - first_default].write()) 514 | out = "(lambda " + ", ".join([str(arg) for arg in args]) + ": " 515 | out += "; ".join([ statement.write() for statement in self.statements ]) + ")" 516 | return out 517 | 518 | class With(Block): 519 | """ With block : with expr as var: statements """ 520 | def __init__(self, expr, var, statements): 521 | Block.__init__(self, statements) 522 | self.expr = expr 523 | self.var = var 524 | 525 | @Statement.auto_indent 526 | def write(self, indent): 527 | out = "with " + self.expr.write() 528 | if self.var: 529 | out += " as " + self.var.write() 530 | out += ":\n" 531 | out += Block.write(self, indent) 532 | return out 533 | 534 | class For(Block): 535 | """ For block : for variables in expr: statements """ 536 | def __init__(self, variables, expr, statements): 537 | Block.__init__(self, statements) 538 | self.variables = variables 539 | self.expr = expr 540 | 541 | @Statement.auto_indent 542 | def write(self, indent): 543 | var_list = ", ".join([ var.write() for var in self.variables ]) 544 | return "for " + var_list + " in " + self.expr.write() + ":\n" + Block.write(self, indent) 545 | 546 | class While(Block): 547 | """ While block: while expr: statements """ 548 | def __init__(self, expr, statements): 549 | Block.__init__(self, statements) 550 | self.expr = expr 551 | 552 | @Statement.auto_indent 553 | def write(self, indent): 554 | return "while " + self.expr.write() + ":\n" + Block.write(self, indent) 555 | 556 | class If(Block): 557 | """ If block: if expr: statements """ 558 | def __init__(self, expr, statements): 559 | Block.__init__(self, statements) 560 | self.expr = expr 561 | 562 | @Statement.auto_indent 563 | def write(self, indent): 564 | return "if " + self.expr.write() + ":\n" + Block.write(self, indent) 565 | 566 | class Elif(Block): 567 | """ Elif block: elif expr: statements 568 | Can appear after an If statement or another Elif statement. 569 | """ 570 | def __init__(self, expr, statements): 571 | Block.__init__(self, statements) 572 | self.expr = expr 573 | 574 | @Statement.auto_indent 575 | def write(self, indent): 576 | return "elif " + self.expr.write() + ":\n" + Block.write(self, indent) 577 | 578 | class Else(Block): 579 | """ Else block: else: statements 580 | Can appear after If, Elif, For, While, Try statements. 581 | """ 582 | @Statement.auto_indent 583 | def write(self, indent): 584 | return "else:\n" + Block.write(self, indent) 585 | 586 | class Try(Block): 587 | """ Try block """ 588 | 589 | def __init__(self, statements): 590 | Block.__init__(self, statements) 591 | 592 | @Statement.auto_indent 593 | def write(self, indent): 594 | return "try:\n" + Block.write(self, indent) 595 | 596 | class Except(Block): 597 | """ Except block: except exc as name: statements """ 598 | 599 | def __init__(self, statements, exc = None, as_name = None): 600 | Block.__init__(self, statements) 601 | self.exc = exc 602 | self.as_name = as_name 603 | 604 | @Statement.auto_indent 605 | def write(self, indent): 606 | out = "except" 607 | if self.exc: 608 | out += ' ' + self.exc.write() 609 | if self.as_name: 610 | out += " as " + self.as_name.write() 611 | out += ":\n" 612 | out += Block.write(self, indent) 613 | return out 614 | 615 | class Finally(Block): 616 | """ Finally block """ 617 | 618 | @Statement.auto_indent 619 | def write(self, indent): 620 | return "finally:\n" + Block.write(self, indent) 621 | 622 | class DocString(Statement): 623 | """ Docstring comment. """ 624 | def __init__(self, comment): 625 | Statement.__init__(self) 626 | self.comment = comment 627 | 628 | @Statement.auto_indent 629 | def write(self, indent): 630 | lines = self.comment.split("\n") 631 | indented_lines = [ self.make_indent(indent) + line.lstrip() for line in lines[1:] ] 632 | return '"""' + "\n".join(lines[0:1] + indented_lines) + '"""' 633 | 634 | class ClassDefinition(Block): 635 | """ Class definition block: class name(superclasses): statements """ 636 | def __init__(self, name, supers, statements): 637 | Block.__init__(self, statements) 638 | self.name = name 639 | self.supers = supers 640 | self.docstring = None 641 | docstrings = filter( 642 | lambda s: isinstance(s, Assignment) and s.left == Variable('__doc__'), 643 | self.statements 644 | ) 645 | if len(docstrings) > 0: 646 | self.docstring = DocString(docstrings[0].right.value) 647 | self.docstring.set_indent_level(self.indent_level + 1) 648 | self.delete_statements( 649 | match = lambda s: isinstance(s, Return) or isinstance(s, Assignment) and (s.left == Variable('__module__') or s.left == Variable('__doc__')), 650 | rec = False 651 | ) 652 | methods = filter( 653 | lambda s: isinstance(s, FunctionDefinition), 654 | self.statements 655 | ) 656 | for method in methods: 657 | self.unmangle(method) 658 | 659 | def unmangle(self, method): 660 | if method.name[0:1] == '_' and method.name[1:len(self.name)+1] == self.name: 661 | method.name = method.name[len(self.name)+1:] 662 | 663 | def set_indent_level(self, level): 664 | Block.set_indent_level(self, level) 665 | if self.docstring: 666 | self.docstring.set_indent_level(level + 1) 667 | 668 | @Statement.auto_indent 669 | def write(self, indent): 670 | out = "class " + self.name 671 | if len(self.supers) > 0: 672 | supers = ", ".join([superclass.write() for superclass in self.supers]) 673 | out += "(" + supers + ")" 674 | out += ":\n" 675 | if self.docstring: 676 | out += self.docstring.write(indent) + "\n" 677 | out += Block.write(self, indent) 678 | if len(self.statements) > 0 and not isinstance(self.statements[-1], FunctionDefinition) and not isinstance(self.statements[-1], ClassDefinition): 679 | out += "\n" 680 | return out 681 | 682 | class FunctionDefinition(Block): 683 | """ Function definition block: 684 | def func(args, arg_n = default_arg_1, arg_n+1 = default_arg_2): statements 685 | """ 686 | def __init__(self, name, args, default_args, varargs, kwvarargs, statements): 687 | Block.__init__(self, statements) 688 | self.name = name 689 | self.args = args 690 | self.default_values = default_args 691 | self.varargs = varargs 692 | self.kwvarargs = kwvarargs 693 | self.docstring = None 694 | if len(statements) > 0 and statements[-1].write('') == 'return': 695 | statements.pop() 696 | 697 | def set_docstring(self, string): 698 | self.docstring = DocString(string) 699 | self.docstring.set_indent_level(self.indent_level + 1) 700 | 701 | def set_indent_level(self, level): 702 | Block.set_indent_level(self, level) 703 | if self.docstring: 704 | self.docstring.set_indent_level(level + 1) 705 | 706 | @Statement.auto_indent 707 | def write(self, indent): 708 | args = [] 709 | first_default = len(self.args) - len(self.default_values) 710 | for i in range(0, first_default): 711 | args.append(self.args[i]) 712 | for i in range(first_default, len(self.args)): 713 | args.append(self.args[i] + " = " + self.default_values[i - first_default].write()) 714 | out = "def " + self.name + "(" + ", ".join(args) 715 | if self.varargs: 716 | if len(args) > 0: 717 | out += ", " 718 | out += "*" + self.varargs 719 | if self.kwvarargs: 720 | if len(args) > 0 or self.varargs: 721 | out += ", " 722 | out += "**" + self.kwvarargs 723 | out += "):\n" 724 | if self.docstring: 725 | out += self.docstring.write(indent) + "\n" 726 | out += Block.write(self, indent) + "\n" 727 | return out 728 | 729 | class Import(Statement): 730 | """ Import statement. 731 | Equivalent to 732 | module = __import__('module', fromlist = None, level = -1). 733 | """ 734 | def __init__(self, module, as_name = None): 735 | Statement.__init__(self) 736 | self.module = module 737 | self.as_name = as_name 738 | 739 | @Statement.auto_indent 740 | def write(self, indent = ''): 741 | out = "import " + '.'.join(self.module.path) 742 | if self.as_name: 743 | out += " as " + self.as_name.write() 744 | return out 745 | 746 | class ImportFrom(Statement): 747 | """ Import from statement. 748 | Equivalent to 749 | name = __import__('module', fromlist = None, level = -1).name. 750 | """ 751 | def __init__(self, module, name, as_name = None): 752 | Statement.__init__(self) 753 | self.module = module 754 | self.name = name 755 | self.as_name = as_name 756 | 757 | @Statement.auto_indent 758 | def write(self, indent = ''): 759 | out = "from " + '.'.join(self.module.path) + " import " + self.name.write() 760 | if self.as_name: 761 | out += " as " + self.as_name.write() 762 | return out 763 | 764 | class Assert(Statement): 765 | """ Assert statement. 766 | Equivalent to 767 | if not expr: raise AssertionError(message) 768 | """ 769 | def __init__(self, expr, message = None): 770 | Statement.__init__(self) 771 | self.expr = expr 772 | self.message = message 773 | 774 | @Statement.auto_indent 775 | def write(self, indent = ''): 776 | out = "assert " + self.expr.write() 777 | if self.message: 778 | out += ", " + self.message.write() 779 | return out 780 | 781 | class Print(Statement): 782 | """ Print statement. """ 783 | def __init__(self, expr_list = None): 784 | Statement.__init__(self) 785 | if expr_list: 786 | self.expr_list = expr_list 787 | else: 788 | self.expr_list = ExpressionList() 789 | self.new_line = False 790 | 791 | @Statement.auto_indent 792 | def write(self, indent = ''): 793 | out = "print " + self.expr_list.write() 794 | if not self.new_line: 795 | out += "," 796 | return out 797 | 798 | class ExtendedPrint(Statement): 799 | """ Extended print statement. """ 800 | def __init__(self, filedesc, expr_list = None): 801 | Statement.__init__(self) 802 | self.filedesc = filedesc 803 | if expr_list: 804 | self.expr_list = expr_list 805 | else: 806 | self.expr_list = ExpressionList() 807 | self.new_line = False 808 | 809 | @Statement.auto_indent 810 | def write(self, indent = ''): 811 | out = "print >> " + self.filedesc.write() + ", " + self.expr_list.write() 812 | if not self.new_line: 813 | out += "," 814 | return out 815 | 816 | class Break(Statement): 817 | """ break statement. """ 818 | @Statement.auto_indent 819 | def write(self, indent = ''): 820 | return "break" 821 | 822 | class Continue(Statement): 823 | """ continue statement. """ 824 | @Statement.auto_indent 825 | def write(self, indent = ''): 826 | return "continue" 827 | 828 | class Return(Statement): 829 | """ return statement. """ 830 | def __init__(self, expr = None): 831 | Statement.__init__(self) 832 | self.expr = expr 833 | 834 | @Statement.auto_indent 835 | def write(self, indent = ''): 836 | out = "return" 837 | if self.expr != None and self.expr != Constant(None): 838 | out += " " + self.expr.write() 839 | return out 840 | 841 | class Global(Statement): 842 | """ global statement """ 843 | def __init__(self, var): 844 | Statement.__init__(self) 845 | self.var = var 846 | 847 | @Statement.auto_indent 848 | def write(self, indent = ''): 849 | return "global " + self.var.write() 850 | 851 | class Del(Statement): 852 | """ del statement. """ 853 | def __init__(self, expr): 854 | Statement.__init__(self) 855 | self.expr = expr 856 | 857 | @Statement.auto_indent 858 | def write(self, indent = ''): 859 | return "del " + self.expr.write() 860 | 861 | class Raise(Statement): 862 | """ raise statement. """ 863 | def __init__(self, exception = None, param = None, trace = None): 864 | Statement.__init__(self) 865 | self.exception = exception 866 | self.param = param 867 | self.trace = trace 868 | 869 | @Statement.auto_indent 870 | def write(self, indent = ''): 871 | out = "raise" 872 | if self.exception != None: 873 | out += " " + self.exception.write() 874 | if self.param != None: 875 | out += ", " + self.param.write() 876 | if self.trace != None: 877 | out += ", " + self.trace.write() 878 | return out 879 | 880 | class Decorator(Statement): 881 | """ Function decorator. """ 882 | def __init__(self, expr): 883 | Statement.__init__(self) 884 | self.expr = expr 885 | 886 | @Statement.auto_indent 887 | def write(self, indent = ''): 888 | return "@" + self.expr.write() 889 | 890 | class Exec(Statement): 891 | """ exec statement. """ 892 | def __init__(self, expr, global_vars, local_vars): 893 | Statement.__init__(self) 894 | self.expr = expr 895 | self.global_vars = global_vars 896 | self.local_vars = local_vars 897 | 898 | @Statement.auto_indent 899 | def write(self, indent = ''): 900 | out = "exec " + self.expr.write() 901 | if self.global_vars != Constant(None): 902 | out += " in " + self.global_vars.write() 903 | if self.local_vars != Constant(None): 904 | out += ", " + self.local_vars.write() 905 | return out 906 | 907 | class Pass(Statement): 908 | """ pass statement. """ 909 | @Statement.auto_indent 910 | def write(self, indent = ''): 911 | return "pass" 912 | 913 | class PythonProgram(Block): 914 | """ Python program. This is what we wish to reconstruct. 915 | The python source is considered as a block of statements. 916 | """ 917 | def __init__(self, statements): 918 | Block.__init__(self, statements, -1) 919 | self.delete_statements(match = lambda s: isinstance(s, Return), rec = False) 920 | docstrings = filter( 921 | lambda s: isinstance(s, Assignment) and s.left == Variable('__doc__'), 922 | self.statements 923 | ) 924 | if len(docstrings) > 0: 925 | statements.insert(0, DocString(docstrings[0].right.value)) 926 | self.delete_statements( 927 | match = lambda s: isinstance(s, Assignment) and s.left == Variable('__doc__'), 928 | rec = False 929 | ) 930 | 931 | class PythonExceptionClass: 932 | """ 933 | Internally used by the decompiler to represent the current exception class. 934 | """ 935 | def write(self, indent = ''): 936 | return '.CurrentExceptionClass' 937 | 938 | class PythonExceptionInstance: 939 | """ 940 | Internally used by the decompiler to represent the current exception instance. 941 | """ 942 | def write(self, indent = ''): 943 | return '.CurrentException' 944 | 945 | class PythonImportedModule: 946 | """ 947 | Internally used by the decompiler to represent an imported module. 948 | """ 949 | def __init__(self, name): 950 | self.path = name.split('.') 951 | 952 | class PythonImportedModuleAttr: 953 | """ 954 | Internally used by the decompiler to represent an imported module name. 955 | """ 956 | def __init__(self, module, name): 957 | self.module = module 958 | self.name = name 959 | 960 | class PythonCompiledFunction: 961 | """ 962 | Internally used by the decompiler to represent a function made out of a precompiled code object. 963 | """ 964 | def __init__(self, code, default_args): 965 | self.code = code 966 | self.default_args = default_args 967 | 968 | class PythonCompiledGenerator: 969 | """ 970 | Internally used by the decompiler to represent a precompiled generator object. 971 | """ 972 | def __init__(self, code): 973 | self.code = code 974 | 975 | class PythonCompiledComprehension: 976 | """ 977 | Internally used by the decompiler to represent set and dict comprehensions. 978 | """ 979 | def __init__(self, code): 980 | self.code = code 981 | 982 | class PythonCompiledFunctionCall: 983 | """ 984 | Internally used by the decompiler to represent a call to a precompiled function. 985 | """ 986 | def __init__(self, compiled): 987 | self.function = compiled 988 | 989 | class PythonCompiledClass: 990 | """ 991 | Internally used by the decompiler to represent a compiled class object. 992 | """ 993 | def __init__(self, supers, code): 994 | self.supers = supers 995 | self.code = code 996 | 997 | class PythonIterator: 998 | """ 999 | Internally used by the decompiler to represent a volatile iterator. 1000 | """ 1001 | def __init__(self, expr): 1002 | self.expr = expr 1003 | self.walked = False 1004 | self.alive = True 1005 | self.exhausted_addr = None 1006 | 1007 | class PythonIterate: 1008 | """ 1009 | Internally used by the decompiler to mark a next() call on a volatile iterator. 1010 | """ 1011 | def __init__(self, iterator, addr): 1012 | self.iterator = iterator 1013 | self.addr = addr 1014 | 1015 | class PythonOpenedComprehension(Statement): 1016 | """ 1017 | Fake statement. Internally used by the decompiler for reconstructing comprehensions. 1018 | """ 1019 | def __init__(self, comp): 1020 | self.init = comp 1021 | self.current = comp.comp # Comprehension component in type comprehension 1022 | 1023 | class PythonBlockFinalizer(Block): 1024 | """ 1025 | Internally used by the decompiler to represent Else statements of loops. 1026 | """ 1027 | 1028 | class PythonBasicBlock: 1029 | """ 1030 | Internally used by the decompiler to analyze control flows. 1031 | """ 1032 | def __init__(self, addr): 1033 | self.addr = addr 1034 | self.children = set() # direct children 1035 | self.parents = set() # direct parents 1036 | self.ancestors = None 1037 | self.end_addr = 0 1038 | 1039 | def add_child(self, block): 1040 | self.children.add(block) 1041 | block.parents.add(self) 1042 | 1043 | def remove_child(self, block): 1044 | if block in self.children: 1045 | self.children.remove(block) 1046 | block.parents.remove(self) 1047 | 1048 | def get_ancestors(self, walked = None): 1049 | if walked is None: 1050 | walked = set() 1051 | if self.ancestors: 1052 | return self.ancestors 1053 | else: 1054 | self.ancestors = set() 1055 | for parent in self.parents: 1056 | if parent not in walked: 1057 | walked.add(parent) 1058 | self.ancestors |= parent.get_ancestors(walked) 1059 | self.ancestors |= self.parents 1060 | return self.ancestors 1061 | 1062 | def __hash__(self): 1063 | return hash(self.addr) 1064 | 1065 | # Block are compared by their starting and ending addresses only 1066 | def __eq__(self, block): 1067 | assert isinstance(block, PythonBasicBlock) 1068 | return self.addr == block.addr 1069 | 1070 | def __ne__(self, block): 1071 | return not (self == block) 1072 | 1073 | def __lt__(self, block): 1074 | assert isinstance(block, PythonBasicBlock) 1075 | return self.addr < block.addr 1076 | 1077 | class PythonUnpackedSequence: 1078 | """ 1079 | Internally used by the decompiler to represent an unpacked sequence. 1080 | """ 1081 | def __init__(self, expr, size): 1082 | self.expr = expr 1083 | self.size = size 1084 | self.variables = [ None ] * size 1085 | self.nested = False 1086 | self.parent = None 1087 | self.parent_index = None 1088 | 1089 | def bind(self, index, var): 1090 | self.variables[index] = var 1091 | 1092 | def is_complete(self): 1093 | return all(self.variables) 1094 | 1095 | class PythonUnpackedValue: 1096 | """ 1097 | Internally used by the decompiler to represent an unpacked value from a sequence. 1098 | """ 1099 | def __init__(self, sequence, index): 1100 | self.sequence = sequence 1101 | self.index = index 1102 | 1103 | class PythonWithEnter: 1104 | """ 1105 | Internally used by the decompiler to represent a return value from __enter__. 1106 | Used in with statements. 1107 | """ 1108 | def __init__(self, expr): 1109 | self.expr = expr 1110 | 1111 | class PythonWithExit: 1112 | """ 1113 | Internally used by the decompiler to represent a pointer to __exit__. 1114 | Used in with statements. 1115 | """ 1116 | 1117 | class PythonCompiledObjectFile: 1118 | """ 1119 | Helper class for opening Python compiled object files (.pyc or .pyo) 1120 | """ 1121 | 1122 | version_by_magic = { 1123 | '\xb3\xf2\r\n' : 2.5, 1124 | '\xd1\xf2\r\n' : 2.6, 1125 | '\x03\xf3\r\n' : 2.7 1126 | } 1127 | 1128 | def __init__(self, path): 1129 | fp = open(path, 'rb') 1130 | 1131 | try: 1132 | self.magic = fp.read(4) 1133 | if self.magic != imp.get_magic(): 1134 | raise Exception("Magic constant does not match with current Python version.") 1135 | 1136 | self.timestamp = fp.read(4) 1137 | self.code = marshal.load(fp) 1138 | if not isinstance(self.code, types.CodeType): 1139 | raise TypeError("Cannot find main code object.") 1140 | 1141 | finally: 1142 | fp.close() 1143 | 1144 | def python_version(self): 1145 | return self.version_by_magic[self.magic] 1146 | 1147 | class PythonDecompilerError(Exception): 1148 | def __init__(self, message, addr = None, opname = None, arg = None): 1149 | Exception.__init__(self, message) 1150 | self.addr = addr 1151 | self.opname = opname 1152 | self.arg = arg 1153 | 1154 | def __str__(self): 1155 | info = "Decompilation error: %s" % self.message 1156 | if self.addr and self.opname: 1157 | info += " [address: %d, instruction: %s" % (self.addr, self.opname) 1158 | if self.arg: 1159 | info += " (%s)" % repr(self.arg) 1160 | info += "]" 1161 | return info 1162 | 1163 | class PythonDecompiler: 1164 | binary_operators = { 1165 | 'ADD' : '+', 1166 | 'SUBTRACT' : '-', 1167 | 'MULTIPLY' : '*', 1168 | 'DIVIDE' : '/', 1169 | 'TRUE_DIVIDE' : '/', 1170 | 'FLOOR_DIVIDE' : '//', 1171 | 'MODULO' : '%', 1172 | 'POWER' : '**', 1173 | 'LSHIFT' : '<<', 1174 | 'RSHIFT' : '>>', 1175 | 'AND' : '&', 1176 | 'OR' : '|', 1177 | 'XOR' : '^' 1178 | } 1179 | 1180 | unary_operators = { 1181 | 'POSITIVE' : '+', 1182 | 'NEGATIVE' : '-', 1183 | 'NOT' : 'not ', 1184 | 'INVERT' : '~' 1185 | } 1186 | 1187 | binary_operator_precedences = { 1188 | 'or' : 1, 1189 | 'and' : 2, 1190 | 'in' : 4, 'not in' : 4, 'is' : 4, 'is not' : 4, 1191 | '<' : 4, '<=' : 4, '>' : 4, '>=' : 4, '<>' : 4, '!=' : 4, '==' : 4, 1192 | '|' : 5, 1193 | '^' : 6, 1194 | '&' : 7, 1195 | '<<' : 8, '>>' : 8, 1196 | '+' : 9, '-' : 9, 1197 | '*' : 10, '/' : 10, '//' : 10, '%' : 10, 1198 | '**' : 12, 1199 | 'exception match' : 13 1200 | } 1201 | 1202 | commutative_operators = ( '+', '*', '&', '^', '|', '!=', '==', '<>' ) 1203 | comparison_operators = ( '<', '<=', '==', '!=', '<>', '>=', '>' ) 1204 | 1205 | unary_operator_precedences = { 1206 | 'not ' : 3, 1207 | '+' : 11, '-' : 11, '~' : 11 1208 | } 1209 | 1210 | conditional_jump_insns = ( 1211 | 'POP_JUMP_IF_TRUE', 1212 | 'POP_JUMP_IF_FALSE', 1213 | 'JUMP_IF_TRUE_OR_POP', 1214 | 'JUMP_IF_FALSE_OR_POP', 1215 | 'JUMP_IF_TRUE', # < 2.7 1216 | 'JUMP_IF_FALSE' # < 2.7 1217 | ) 1218 | 1219 | conditional_insns = ( 1220 | 'POP_JUMP_IF_TRUE', 1221 | 'POP_JUMP_IF_FALSE', 1222 | 'JUMP_IF_TRUE_OR_POP', 1223 | 'JUMP_IF_FALSE_OR_POP', 1224 | 'JUMP_IF_TRUE', # < 2.7 1225 | 'JUMP_IF_FALSE', # < 2.7 1226 | 1227 | 'SETUP_FINALLY', # finally clause 1228 | 'SETUP_EXCEPT', # except clause 1229 | ) 1230 | 1231 | jump_insns = ( 1232 | 'POP_JUMP_IF_TRUE', 1233 | 'POP_JUMP_IF_FALSE', 1234 | 'JUMP_IF_TRUE_OR_POP', 1235 | 'JUMP_IF_FALSE_OR_POP', 1236 | 'JUMP_IF_TRUE', # < 2.7 1237 | 'JUMP_IF_FALSE', # < 2.7 1238 | 'JUMP_FORWARD', 1239 | 'JUMP_ABSOLUTE', 1240 | 'CONTINUE_LOOP', 1241 | # XXX: BREAK_LOOP ??? Need to keep track of loop blocks 1242 | 'FOR_ITER', # finally clause 1243 | 'SETUP_FINALLY', # finally clause 1244 | 'SETUP_WITH', # finally clause 1245 | 'SETUP_EXCEPT', # except clause 1246 | 'SETUP_LOOP' 1247 | ) 1248 | 1249 | CODE_FLAG_OPTIMIZED = 1 1250 | CODE_FLAG_NEWLOCALS = 2 1251 | CODE_FLAG_VARARGS = 4 1252 | CODE_FLAG_KWVARARGS = 8 1253 | CODE_FLAG_NESTED = 16 1254 | CODE_FLAG_GENERATOR = 32 1255 | CODE_FLAG_NOFREE = 64 1256 | 1257 | WHY_NOT = 1 1258 | WHY_EXCEPT = 2 1259 | WHY_RERAISE = 4 1260 | WHY_RETURN = 8 1261 | WHY_BREAK = 16 1262 | WHY_CONTINUE = 32 1263 | WHY_YIELD = 64 1264 | 1265 | def __init__(self, path): 1266 | self.pyc = PythonCompiledObjectFile(path) 1267 | self.target_version = self.pyc.python_version() 1268 | self.global_vars = set() 1269 | 1270 | def __disassemble_insn_at(self, bytecode, i): 1271 | """ Return tuple ( opcode, operand, addr_of_next ) from a bytecode string. """ 1272 | if ord(bytecode[i:i+1]) == opcode.EXTENDED_ARG: 1273 | ext = (ord(bytecode[i+1:i+2]) << 16) | (ord(bytecode[i+2:i+3]) << 24) 1274 | i = i + 3 1275 | else: 1276 | ext = 0 1277 | 1278 | op = ord(bytecode[i:i+1]) 1279 | arg = None 1280 | if op >= opcode.HAVE_ARGUMENT: 1281 | arg = ord(bytecode[i+1:i+2]) | (ord(bytecode[i+2:i+3]) << 8) | ext 1282 | i = i + 2 1283 | 1284 | i = i + 1 1285 | return (op, arg, i) 1286 | 1287 | def __disassemble(self, code): 1288 | """ Return list of tuples ( address, opname, operand, size of instruction ) from a code object. """ 1289 | insns = [] 1290 | bytecode = code.co_code 1291 | cells = code.co_cellvars + code.co_freevars 1292 | i = 0 1293 | while i < len(bytecode): 1294 | op_addr = i 1295 | op, arg, i = self.__disassemble_insn_at(bytecode, i) 1296 | if arg != None: 1297 | if op in opcode.hasconst: 1298 | arg = code.co_consts[arg] 1299 | elif op in opcode.hasname: 1300 | arg = code.co_names[arg] 1301 | elif op in opcode.haslocal: 1302 | arg = code.co_varnames[arg] 1303 | elif op in opcode.hascompare: 1304 | arg = opcode.cmp_op[arg] 1305 | elif op in opcode.hasjrel: 1306 | arg = arg + i 1307 | elif op in opcode.hasfree: 1308 | arg = cells[arg] 1309 | insns.append((op_addr, opcode.opname[op], arg, i - op_addr)) 1310 | return insns 1311 | 1312 | def disassemble(self): 1313 | disass = "" 1314 | for addr, opname, arg, _ in self.__disassemble(self.pyc.code): 1315 | disass += " @%.4d: %s (%s)\n" % (addr, opname.ljust(20), arg) 1316 | return disass 1317 | 1318 | def __jmp_to_insn_at(self, insns, addr): 1319 | # Jump to end ( addr of last insn + size of last insn ) 1320 | if addr >= self.__last_addr(insns): 1321 | return len(insns) 1322 | i = 0 1323 | while i < len(insns): 1324 | if insns[i][0] == addr: 1325 | break 1326 | i = i + 1 1327 | else: 1328 | raise Exception("Jump to %d out of bounds [%d..%d]" % (addr, self.__first_addr(insns), self.__last_addr(insns))) 1329 | return i 1330 | 1331 | def __first_addr(self, insns): 1332 | if len(insns) > 0: 1333 | return insns[0][0] 1334 | else: 1335 | return None 1336 | 1337 | def __last_addr(self, insns): 1338 | if len(insns) > 0: 1339 | return insns[-1][0] + insns[-1][3] 1340 | else: 1341 | return None 1342 | 1343 | def __next_addr(self, insns, addr): 1344 | n = self.__jmp_to_insn_at(insns, addr) 1345 | return insns[n][0] + insns[n][3] 1346 | 1347 | def __decompile(self, code): 1348 | """ Decompile a code object. 1349 | Return a list of Statements. 1350 | """ 1351 | #if __debug__: 1352 | #print 'Disassembling %s:' % code.co_name 1353 | #dis.dis(code) 1354 | #print '_' * 80 1355 | insns = self.__disassemble(code) 1356 | return self.__decompile_block(insns, [])[1] 1357 | 1358 | def __decompile_loop(self, insns, stack): 1359 | """ Decompile a loop statement. 1360 | Return ( next_addr, statements ) 1361 | """ 1362 | next_addr, statements = self.__decompile_block(insns, stack) 1363 | else_block = None 1364 | assert(len(statements) > 0) 1365 | 1366 | # 1367 | # var = iterate over iterator => for loop 1368 | # 1369 | if isinstance(statements[0], Assignment) and isinstance(statements[0].right, PythonIterate): 1370 | iterate = statements[0].right 1371 | iterator = iterate.iterator 1372 | if isinstance(statements[0].left, ExpressionList): 1373 | var = statements[0].left.exprs 1374 | else: 1375 | var = [ statements[0].left ] 1376 | expr = iterator.expr 1377 | if isinstance(statements[-1], PythonBlockFinalizer): 1378 | else_block = Else(statements.pop().statements) 1379 | loop = For(var, expr, statements[1:]) 1380 | # 1381 | # while loop 1382 | # 1383 | else: 1384 | if len(statements) == 1 and isinstance(statements[0], If): 1385 | if_block = statements.pop(0) 1386 | if isinstance(if_block.statements[-1], PythonBlockFinalizer): 1387 | else_block = Else(if_block.statements.pop().statements) 1388 | loop = While(if_block.expr, if_block.statements) 1389 | else: 1390 | if isinstance(statements[-1], PythonBlockFinalizer): 1391 | else_block = Else(statements.pop().statements) 1392 | loop = While(Constant(True), statements) 1393 | 1394 | # Remove extraneous continue statement 1395 | if len(loop.statements) > 1 and isinstance(loop.statements[-1], Continue): 1396 | loop.statements.pop() 1397 | 1398 | # Reached end of for loop before cleaning the block (e.g. because of a return statement) 1399 | if isinstance(loop, For) and iterator.alive: 1400 | # Finalize the block, cleanup the stack 1401 | next_addr, finalize_stmts = self.__decompile_block(insns[self.__jmp_to_insn_at(insns, iterate.addr):], stack) 1402 | if len(finalize_stmts) > 0 and isinstance(finalize_stmts[-1], PythonBlockFinalizer): 1403 | else_block = Else(finalize_stmts.pop().statements) 1404 | # Escape while loop 1405 | elif isinstance(loop, While) and next_addr is None or next_addr == self.__first_addr(insns): 1406 | next_addr = self.__last_addr(insns) # escape loop 1407 | 1408 | loop_stmts = [ loop ] 1409 | if else_block: 1410 | loop_stmts.append(else_block) 1411 | 1412 | return (next_addr, loop_stmts) 1413 | 1414 | def __decompile_with(self, expr, finally_addr, insns, stack): 1415 | """ Decompile a with block. 1416 | Return (next_addr, with_block). 1417 | """ 1418 | end_with = self.__jmp_to_insn_at(insns, finally_addr) 1419 | _, statements = self.__decompile_block(insns[:end_with], stack) 1420 | var = None 1421 | if len(statements) > 0 and isinstance(statements[0], Assignment) and isinstance(statements[0].right, PythonWithEnter): 1422 | var = statements.pop(0).left 1423 | if len(statements) > 0 and isinstance(statements[-1], PythonBlockFinalizer): 1424 | statements.pop() 1425 | with_block = With(expr, var, statements) 1426 | 1427 | # cleanup with 1428 | #stack.pop() 1429 | #stack.pop() 1430 | 1431 | # finally cleanup 1432 | next_addr, _ = self.__decompile_block(insns[end_with:], stack) 1433 | 1434 | return ( next_addr, with_block ) 1435 | 1436 | def __create_basic_blocks(self, start_addr, insns, blocks): 1437 | """ Recursively create basic blocks """ 1438 | addr = start_addr 1439 | block = PythonBasicBlock(addr) 1440 | blocks[addr] = block 1441 | if len(insns) == 0: 1442 | block.end_addr = addr 1443 | return block 1444 | i = 0 1445 | while i < len(insns): 1446 | addr, opname, arg, opsize = insns[i] 1447 | if i > 0 and addr in blocks: # break if we enter existing block 1448 | block.add_child(blocks[addr]) 1449 | break 1450 | elif opname == 'RETURN_VALUE' or opname == 'RAISE_VARARGS': 1451 | break 1452 | elif opname in self.jump_insns or i == len(insns) - 1: 1453 | if opname in self.jump_insns: 1454 | if opname in self.conditional_insns: 1455 | if arg < start_addr: # outer loop iteration, probably a continue statement 1456 | if not arg in blocks: 1457 | blocks[arg] = PythonBasicBlock(arg) 1458 | blocks[arg].end_addr = arg 1459 | block.add_child(blocks[arg]) 1460 | addr_list = ( arg, addr + opsize ) 1461 | else: # unconditional jump 1462 | if arg < start_addr: # outer loop iteration, probably a continue statement 1463 | if not arg in blocks: 1464 | blocks[arg] = PythonBasicBlock(arg) 1465 | blocks[arg].end_addr = arg 1466 | block.add_child(blocks[arg]) 1467 | #break 1468 | addr_list = ( arg, ) 1469 | else: 1470 | addr_list = ( addr + opsize, ) 1471 | targets = [ 1472 | blocks[dest] if dest in blocks 1473 | else self.__create_basic_blocks(dest, insns[self.__jmp_to_insn_at(insns, dest):], blocks) 1474 | for dest in addr_list 1475 | ] 1476 | for target in targets: 1477 | prev_blocks = sorted(filter(lambda b: b < target, blocks.values()), key=lambda b: b.addr) 1478 | if len(prev_blocks) > 0 and prev_blocks[-1].end_addr >= target.addr: # block split 1479 | prev_block = prev_blocks[-1] 1480 | prev_block.end_addr = insns[self.__jmp_to_insn_at(insns, target.addr) - 1][0] 1481 | for child in set(prev_block.children): 1482 | prev_block.remove_child(child) # clear edges 1483 | prev_block.add_child(target) 1484 | block.add_child(target) 1485 | break 1486 | i = i + 1 1487 | 1488 | block.end_addr = addr 1489 | return block 1490 | 1491 | def __find_convergent_block(self, src_blocks, blocks, walked): 1492 | """ Recursively find first basic block reachable by all paths from src_blocks 1493 | and for which a parent has exactly one children. 1494 | This basic block is characteristic of the end of a conditional statement. 1495 | 1496 | Return None if no such basic block exists. 1497 | """ 1498 | if len(blocks) == 0: 1499 | return None 1500 | 1501 | for block in blocks: 1502 | #print 'current block : ', 1503 | #print block.addr 1504 | #print 'parents : ', 1505 | #print [ p.addr for p in block.parents ] 1506 | if any(len(p.children) == 1 for p in block.parents): 1507 | for src_block in src_blocks: 1508 | if not walked.issubset(block.get_ancestors()): 1509 | break 1510 | else: 1511 | return block 1512 | 1513 | walked |= blocks 1514 | # set of all children of current blocks, minus already walked blocks 1515 | children = reduce(lambda c1, c2: c1 | c2, [ b.children for b in blocks ]) - walked 1516 | return self.__find_convergent_block(src_blocks, children, walked) 1517 | 1518 | def __reduce_conditional_blocks(self, stmts): 1519 | """ Reduce conditional blocks """ 1520 | repass = True 1521 | while repass: 1522 | repass = False 1523 | i = 0 1524 | while i < len(stmts): 1525 | if isinstance(stmts[i], If) or isinstance(stmts[i], Elif): 1526 | j = i + 1 1527 | while j < len(stmts): 1528 | if isinstance(stmts[j], If) or isinstance(stmts[j], Elif): 1529 | # (el)if x: z elif y: z => (el)if x or y: z 1530 | if stmts[i].statements == stmts[j].statements: 1531 | stmts[i].expr = BinaryOp(stmts[i].expr, 'or', stmts[j].expr) 1532 | stmts.pop(j) 1533 | repass = True 1534 | continue 1535 | j = j + 1 1536 | i = i + 1 1537 | 1538 | i = 0 1539 | while i < len(stmts): 1540 | # if x: if x and y: | if x: if not x or y: 1541 | # if y: => foo | if y: => z 1542 | # foo z | z 1543 | # [z] | else: 1544 | # z | z 1545 | if isinstance(stmts[i], If) or isinstance(stmts[i], Elif): 1546 | if len(stmts[i].statements) > 0 and isinstance(stmts[i].statements[0], If): 1547 | if i < len(stmts) - 1 and isinstance(stmts[i+1], Else): 1548 | if stmts[i].statements[0].statements == stmts[i+1].statements: 1549 | stmts[i].expr = BinaryOp(UnaryOp('not ', stmts[i].expr), 'or', stmts[i].statements[0].expr) 1550 | stmts[i].statements = stmts.pop(i+1).statements 1551 | repass = True 1552 | continue 1553 | if stmts[i].statements[1:] == stmts[1-len(stmts[i].statements):] or stmts[i].statements[1:] == stmts[i+1:] or len(stmts[i].statements) == 1: 1554 | stmts[i].expr = BinaryOp(stmts[i].expr, 'and', stmts[i].statements[0].expr) 1555 | stmts[i].statements = stmts[i].statements[0].statements 1556 | repass = True 1557 | continue 1558 | i = i + 1 1559 | 1560 | return stmts 1561 | 1562 | def __factorize_condition(self, expr): 1563 | """ Factorize a logical expression """ 1564 | ops = ( 'and', 'or' ) 1565 | repass = True 1566 | while repass: 1567 | repass = False 1568 | if isinstance(expr, BinaryOp) and expr.op in ops: 1569 | neg_op = ops[1-ops.index(expr.op)] 1570 | expr.left = self.__factorize_condition(expr.left) 1571 | expr.right = self.__factorize_condition(expr.right) 1572 | # 1573 | # (x < y) and (y < z) = x < y < z 1574 | # 1575 | #if expr.op == 'and' and isinstance(expr.left, BinaryOp) and isinstance(expr.right, BinaryOp) and expr.right.op in self.comparison_operators and expr.left.op in self.comparison_operators and expr.left.last == expr.right.first: 1576 | # expr = BinaryOp(expr.left, expr.right.op, expr.right.right) 1577 | # expr.left.parenthesize = expr.parenthesize = False 1578 | 1579 | # 1580 | # (x and y) or (z and y) = (x or z) and y 1581 | # 1582 | if isinstance(expr.left, BinaryOp) and isinstance(expr.right, BinaryOp) and expr.right.op == expr.left.op == neg_op and expr.left.right == expr.right.right: 1583 | expr = BinaryOp(BinaryOp(expr.left.left, expr.op, expr.right.left), neg_op, expr.right.right) 1584 | repass = True 1585 | continue 1586 | # 1587 | # (x and (y or z)) or z = (x and y) or z 1588 | # 1589 | elif isinstance(expr.left, BinaryOp) and expr.left.op == neg_op and isinstance(expr.left.right, BinaryOp) and expr.left.right.op == expr.op and expr.left.right.right == expr.right: 1590 | expr = BinaryOp(BinaryOp(expr.left.left, neg_op, expr.left.right.left), expr.op, expr.right) 1591 | repass = True 1592 | continue 1593 | 1594 | elif isinstance(expr, UnaryOp) and expr.op == 'not ': 1595 | expr.expr = self.__factorize_condition(expr.expr) 1596 | # 1597 | # not (not x) = x 1598 | # 1599 | if isinstance(expr.expr, UnaryOp) and expr.expr.op == 'not ': 1600 | expr = expr.expr.expr 1601 | repass = True 1602 | continue 1603 | # 1604 | # not ((not x) and (not y)) = x or y 1605 | # not ((not x) or (not y)) = x and y 1606 | # 1607 | elif isinstance(expr.expr, BinaryOp) and expr.expr.op in ops: 1608 | if isinstance(expr.expr.left, UnaryOp) and isinstance(expr.expr.right, UnaryOp): 1609 | if expr.expr.left.op == expr.expr.right.op == 'not ': 1610 | expr = BinaryOp(expr.expr.left, ops[1-ops.index(expr.expr.op)], expr.expr.right) 1611 | repass = True 1612 | continue 1613 | return expr 1614 | 1615 | def __detect_end_of_statement(self, insns): 1616 | """ Find the next address after a conditional statement """ 1617 | blocks = {} 1618 | #print 'creating basic blocks at ' + str(insns[0][0]) 1619 | first_block = self.__create_basic_blocks(self.__first_addr(insns), insns, blocks) 1620 | #print 'src_blocks : ', 1621 | #print [ b.addr for b in first_block.children] 1622 | 1623 | if len(first_block.children) == 1: 1624 | end_block = None 1625 | else: 1626 | end_block = self.__find_convergent_block(first_block.children, first_block.children, set()) 1627 | #print 'result : ', 1628 | #if end_block: 1629 | # print end_block.addr 1630 | #else: 1631 | # print None 1632 | 1633 | # Couldn't find a final block 1634 | if not end_block: 1635 | # Check if one destination address resumes to start of loop (continue) 1636 | for b in first_block.children: 1637 | if b.addr <= self.__first_addr(insns): 1638 | end_addr = b.addr 1639 | break 1640 | else: 1641 | end_addr = self.__last_addr(insns) 1642 | else: 1643 | end_addr = end_block.addr 1644 | 1645 | if end_addr <= self.__first_addr(insns): 1646 | last_addr = self.__last_addr(insns) 1647 | else: 1648 | last_addr = end_addr 1649 | 1650 | #print 'bounds : [' + str(insns[0][0]) + ', ' + str(insns[-1][0] + insns[-1][3]) + ']' 1651 | #print 'end_addr = ' + str(end_addr) 1652 | #print 'last_addr = ' + str(last_addr) 1653 | return (end_addr, last_addr) 1654 | 1655 | # The horror show is about to begin 1656 | def __decompile_condition(self, insns, stack): 1657 | """ Decompile a if/elif/else block. 1658 | Return (next_addr, statements) 1659 | """ 1660 | end_addr, last_addr = self.__detect_end_of_statement(insns) 1661 | 1662 | addr, opname, target, opsize = insns[0] 1663 | if opname[0:8] == 'POP_JUMP': 1664 | cond_expr = stack.pop() 1665 | if_stack = stack[:] 1666 | else_stack = stack[:] 1667 | if opname[-8:] == 'IF_FALSE': 1668 | if_addr = addr + opsize 1669 | else_addr = target 1670 | else: 1671 | if_addr = target 1672 | else_addr = addr + opsize 1673 | else: 1674 | cond_expr = stack[-1] 1675 | if_stack = stack[:] 1676 | else_stack = stack[:] 1677 | if opname[5:13] == 'IF_FALSE': 1678 | if_addr = addr + opsize 1679 | else_addr = target 1680 | if opname[-6:] == 'OR_POP': 1681 | if_stack.pop() # OR_POP 1682 | else: 1683 | if_addr = target 1684 | else_addr = addr + opsize 1685 | if opname[-6:] == 'OR_POP': 1686 | else_stack.pop() # OR_POP 1687 | 1688 | try: 1689 | if_insns = insns[self.__jmp_to_insn_at(insns, if_addr):self.__jmp_to_insn_at(insns, last_addr)] 1690 | except: 1691 | if_insns = [] 1692 | try: 1693 | else_insns = insns[self.__jmp_to_insn_at(insns, else_addr):self.__jmp_to_insn_at(insns, last_addr)] 1694 | except: 1695 | else_insns = [] 1696 | 1697 | # Decompile each branch 1698 | _, if_block = self.__decompile_block(if_insns, if_stack) 1699 | if_stmt = If(cond_expr, if_block) 1700 | _, else_block = self.__decompile_block(else_insns, else_stack) 1701 | else_stmt = Else(else_block) 1702 | 1703 | # Conditional execution paths must set the stack in a consistent state at return. 1704 | # Something wrong happened if this property is not satisfied. 1705 | assert(len(if_stack) == len(else_stack)) 1706 | 1707 | # Empty statements ? Check the stack for remaining values. 1708 | # XXX: multiple expressions on the stack ??? 1709 | logical_expr = False 1710 | if len(if_stmt.statements) == 0 and len(else_stmt.statements) == 0: 1711 | #i = len(if_stack) - 1; 1712 | #while i >= 0 and (i > len(stack) - 1 or if_stack[i] != stack[i] or else_stack[i] != stack[i]): 1713 | if len(if_stack) > len(stack) or (len(stack) > 0 and len(if_stack) > 0 and (if_stack[-1] != stack[len(if_stack)-1] or else_stack[-1] != stack[len(else_stack)-1])): 1714 | logical_expr = True 1715 | # if x: x else y => x or y 1716 | if if_stmt.expr == if_stack[-1]: 1717 | if_stack[-1] = self.__factorize_condition(BinaryOp(if_stack[-1], "or", else_stack[-1])) 1718 | # if x: y else x => x and y 1719 | elif if_stmt.expr == else_stack[-1]: 1720 | if_stack[-1] = self.__factorize_condition(BinaryOp(else_stack[-1], "and", if_stack[-1])) 1721 | # if x: z else (y and z) => (x or y) and z 1722 | elif isinstance(else_stack[-1], BinaryOp) and else_stack[-1].op == 'and' and if_stack[-1] == else_stack[-1].right: 1723 | if_stack[-1] = self.__factorize_condition(BinaryOp(BinaryOp(if_stmt.expr, 'or', else_stack[-1].left), 'and', if_stack[-1])) 1724 | # if x: (y or z) else: z => (x and y) or z 1725 | elif isinstance(if_stack[-1], BinaryOp) and if_stack[-1].op == 'or' and else_stack[-1] == if_stack[-1].right: 1726 | if_stack[-1] = self.__factorize_condition(BinaryOp(BinaryOp(if_stmt.expr, 'and', if_stack[-1].left), 'or', else_stack[-1])) 1727 | else: # generic conditional expression 1728 | if_stack[-1] = ConditionalExpression(if_stack[-1], self.__factorize_condition(if_stmt.expr), else_stack[-1]) 1729 | #i = i - 1 1730 | stack[:] = if_stack 1731 | if logical_expr: 1732 | return (end_addr, []) 1733 | 1734 | # Python < 2.7 does not have POP_* instructions. 1735 | # Stack cleaning inside condition blocks can create dummy statements if the expression is a function call. 1736 | if self.target_version < 2.7: 1737 | while len(if_stmt.statements) > 0 and if_stmt.statements[0] == if_stmt.expr: 1738 | if_stmt.statements.pop(0) 1739 | while len(else_stmt.statements) > 0 and else_stmt.statements[0] == if_stmt.expr: 1740 | else_stmt.statements.pop(0) 1741 | 1742 | """ 1743 | if expr1: pass 1744 | else: xxx 1745 | 1746 | => if not expr1: xxx 1747 | """ 1748 | if len(if_stmt.statements) == 0 and len(else_stmt.statements) > 0: 1749 | if_stmt.expr = UnaryOp('not ', if_stmt.expr) 1750 | if_stmt.statements = else_stmt.statements 1751 | else_stmt.statements = [] 1752 | 1753 | stmts = [ if_stmt ] 1754 | if len(else_stmt.statements) > 0: 1755 | else_stmts = [] 1756 | """ if else: if => if elif: """ 1757 | if all(isinstance(s, If) or isinstance(s, Elif) or isinstance(s, Else) for s in else_stmt.statements): 1758 | nested_if = else_stmt.statements.pop(0) 1759 | else_stmts.append(Elif(nested_if.expr, nested_if.statements)) 1760 | else_stmts.extend(else_stmt.statements) 1761 | else: 1762 | else_stmts.append(else_stmt) 1763 | stmts.extend(else_stmts) 1764 | # reduction of statements 1765 | stmts = self.__reduce_conditional_blocks(stmts) 1766 | # reduction of expression 1767 | for s in stmts: 1768 | if isinstance(s, If) or isinstance(s, Elif): 1769 | s.expr = self.__factorize_condition(s.expr) 1770 | 1771 | return (end_addr, stmts) 1772 | 1773 | def __decompile_try_catch(self, insns, stack): 1774 | """ Decompile a try/except/else/finally block 1775 | Return (next_addr, statements) 1776 | """ 1777 | end_addr, last_addr = self.__detect_end_of_statement(insns) 1778 | 1779 | i = 0 1780 | except_addr = finally_addr = None 1781 | while i < len(insns) and insns[i][1] in ('SETUP_EXCEPT', 'SETUP_FINALLY'): 1782 | arg = insns[i][2] 1783 | if insns[i][1] == 'SETUP_EXCEPT': 1784 | except_addr = arg 1785 | else: 1786 | finally_addr = arg 1787 | i = i + 1 1788 | 1789 | else_stmt = None 1790 | finally_stmt = None 1791 | stmt_end = self.__jmp_to_insn_at(insns, last_addr) 1792 | next_addr, try_block = self.__decompile_block(insns[i:stmt_end], stack[:]) 1793 | if len(try_block) > 0 and isinstance(try_block[-1], PythonBlockFinalizer): 1794 | else_block = try_block.pop().statements 1795 | if len(else_block) > 0 and isinstance(else_block[-1], PythonBlockFinalizer): 1796 | else_block.pop() 1797 | if len(else_block) > 0: 1798 | else_stmt = Else(else_block) 1799 | try_stmt = Try(try_block) 1800 | 1801 | if finally_addr: 1802 | finally_stack = stack[:] 1803 | finally_stack.append(Constant(None)) 1804 | next_addr, finally_stmts = self.__decompile_block(insns[self.__jmp_to_insn_at(insns, finally_addr):], finally_stack) 1805 | finally_stmt = Finally(finally_stmts) 1806 | 1807 | excepts = [] 1808 | if except_addr: 1809 | except_stack = stack[:] 1810 | except_stack.append(Constant(None)) # Dummy traceback 1811 | except_stack.append(PythonExceptionInstance()) 1812 | except_stack.append(PythonExceptionClass()) 1813 | start_excepts = self.__jmp_to_insn_at(insns, except_addr) 1814 | end_excepts = self.__jmp_to_insn_at(insns, last_addr) 1815 | _, except_stmts = self.__decompile_block( 1816 | insns[start_excepts:end_excepts], 1817 | except_stack 1818 | ) 1819 | if len(except_stmts) > 0 and isinstance(except_stmts[-1], PythonBlockFinalizer): 1820 | except_stmts.pop() 1821 | 1822 | if len(except_stmts) == 0: 1823 | excepts.append(Except([])) 1824 | 1825 | multiple_excepts = False 1826 | for statement in except_stmts: 1827 | if (isinstance(statement, If) or isinstance(statement, Elif)) and isinstance(statement.expr, BinaryOp) and statement.expr.op == 'exception match': 1828 | multiple_excepts = True 1829 | exc_var = None 1830 | if len(statement.statements) > 0 and isinstance(statement.statements[0], Assignment) and isinstance(statement.statements[0].right, PythonExceptionInstance): 1831 | exc_var = statement.statements.pop(0).left 1832 | except_class = statement.expr.left if isinstance(statement.expr.right, PythonExceptionClass) else statement.expr.right 1833 | excepts.append(Except(statement.statements, except_class, exc_var)) 1834 | else: 1835 | if not multiple_excepts: 1836 | excepts.append(Except(except_stmts)) 1837 | break 1838 | 1839 | stmts = [ try_stmt ] + excepts 1840 | if else_stmt and len(excepts) > 0: 1841 | stmts.append(else_stmt) 1842 | if finally_stmt: 1843 | stmts.append(finally_stmt) 1844 | if else_stmt and len(excepts) == 0: 1845 | stmts.extend(else_stmt.statements) 1846 | return (next_addr, stmts) 1847 | 1848 | def __decompile_comprehension(self, code, argument): 1849 | """ Decompile a comprehension implemented as a code object """ 1850 | comp_stmts = self.__decompile(code) 1851 | if len(comp_stmts) != 1 or not isinstance(comp_stmts[0], Return): 1852 | if not isinstance(comp_stmts[0].expr, TypedComprehension): 1853 | raise PythonDecompilerError("Cannot decompile comprehension in code object.") 1854 | comp = comp_stmts[0].expr 1855 | iterable = argument.expr 1856 | nested = comp.comp 1857 | while isinstance(nested.expr, Comprehension): 1858 | nested = nested.expr 1859 | nested.iterable = iterable 1860 | return comp 1861 | 1862 | def __decompile_generator(self, code, argument): 1863 | """ Decompile an anonymous generator """ 1864 | gen_stmts = self.__decompile(code) 1865 | 1866 | # convert statements into a one-line generator expression 1867 | def create_generator_from_statements(first, stmts, current): 1868 | for statement in stmts: 1869 | if isinstance(statement, Yield): 1870 | first.expr = statement.expr 1871 | return current 1872 | elif isinstance(statement, For): # < 2.7 1873 | iterable = statement.expr 1874 | var = statement.variables 1875 | if not current.variables: 1876 | current.variables = var 1877 | if not isinstance(iterable, Variable): 1878 | current.iterable = iterable 1879 | else: 1880 | current = Comprehension(current, var, iterable) 1881 | return create_generator_from_statements(first, statement.statements, current) 1882 | elif isinstance(statement, Assignment) and isinstance(statement.right, PythonIterate): 1883 | iterable = statement.right.iterator.expr 1884 | if isinstance(statement.left, ExpressionList): 1885 | var = statement.left.exprs 1886 | else: 1887 | var = [ statement.left ] 1888 | if not current.variables: 1889 | current.variables = var 1890 | if not isinstance(iterable, Variable): 1891 | current.iterable = iterable 1892 | else: 1893 | current = Comprehension(current, var, iterable) 1894 | elif isinstance(statement, If): 1895 | current.if_expr = statement.expr 1896 | return create_generator_from_statements(first, statement.statements, current) 1897 | else: 1898 | raise PythonDecompilerError("Cannot decompile generator, invalid statements") 1899 | raise PythonDecompilerError("Cannot decompile generator, no yield statement") 1900 | 1901 | iterable = argument.expr 1902 | init = Comprehension(None, None, iterable) 1903 | return Generator(create_generator_from_statements(init, gen_stmts, init)) 1904 | 1905 | def __decompile_block(self, insns, stack): 1906 | """ Core method for decompiling a block of instructions. 1907 | Returns ( next_addr, statements) 1908 | """ 1909 | statements = [] 1910 | 1911 | n = 0 1912 | while n < len(insns): 1913 | addr, opname, arg, opsize = insns[n] 1914 | n = n + 1 1915 | if opname in self.conditional_jump_insns: 1916 | next_addr, cond_stmts = self.__decompile_condition(insns[n-1:], stack) 1917 | statements.extend(cond_stmts) 1918 | if next_addr is None or next_addr <= self.__first_addr(insns): 1919 | return (next_addr, statements) 1920 | n = self.__jmp_to_insn_at(insns, next_addr) 1921 | elif opname[:7] == 'BINARY_': 1922 | right = stack.pop() 1923 | left = stack.pop() 1924 | if opname == 'BINARY_SUBSCR': 1925 | stack.append(BinarySubscr(left, right)) 1926 | elif opname[7:] in self.binary_operators: 1927 | stack.append(BinaryOp(left, self.binary_operators[opname[7:]], right)) 1928 | else: 1929 | raise PythonDecompilerError("Unhandled opcode", addr, opname, arg) 1930 | elif opname == 'BREAK_LOOP': 1931 | statements.append(Break()) 1932 | elif opname == 'BUILD_CLASS': 1933 | codecall = stack.pop() 1934 | if not isinstance(codecall, PythonCompiledFunctionCall): 1935 | raise PythonDecompilerError( 1936 | "Bad type for first argument of BUILD_CLASS.", 1937 | addr, opname, arg 1938 | ) 1939 | supers = stack.pop() 1940 | if not isinstance(supers, Constant): 1941 | raise PythonDecompilerError( 1942 | "Expected constant as second argument of BUILD_CLASS.", 1943 | addr, opname, arg 1944 | ) 1945 | name = stack.pop() 1946 | stack.append(PythonCompiledClass(supers.value, codecall.function.code)) 1947 | elif opname == 'BUILD_LIST': 1948 | values = [] 1949 | for i in range(0, arg): 1950 | values.insert(0, stack.pop()) 1951 | stack.append(Constant(values)) 1952 | elif opname == 'BUILD_MAP': 1953 | stack.append(Constant({})) 1954 | elif opname == 'BUILD_TUPLE': 1955 | values = [] 1956 | for i in range(0, arg): 1957 | values.insert(0, stack.pop()) 1958 | stack.append(Constant(tuple(values))) 1959 | elif opname == 'BUILD_SET': 1960 | values = [] 1961 | for i in range(0, arg): 1962 | values.append(stack.pop()) 1963 | stack.append(Constant(set(values))) 1964 | elif opname == 'BUILD_SLICE': 1965 | step = None 1966 | if arg == 2: 1967 | stop = stack.pop() 1968 | start = stack.pop() 1969 | elif arg == 3: 1970 | step = stack.pop() 1971 | stop = stack.pop() 1972 | start = stack.pop() 1973 | else: 1974 | raise PythonDecompilerError("Invalid argument for BUILD_SLICE.", addr, opname, arg) 1975 | stack.append(Slice(start, stop, step)) 1976 | elif opname[:13] == 'CALL_FUNCTION': 1977 | nkey_params = (arg >> 8) & 0xff; 1978 | npos_params = arg & 0xff; 1979 | keywords = {} 1980 | positional_params = [] 1981 | var_args = kwvar_args = None 1982 | if opname[13:] == '_VAR': 1983 | var_args = stack.pop() 1984 | elif opname[13:] == '_KW': 1985 | kwvar_args = stack.pop() 1986 | elif opname[13:] == '_VAR_KW': 1987 | kwvar_args = stack.pop() 1988 | var_args = stack.pop() 1989 | for i in range(0, nkey_params): 1990 | key_value = stack.pop() 1991 | key_name = stack.pop() 1992 | keywords[key_name.value] = key_value 1993 | for i in range(0, npos_params): 1994 | positional_params.insert(0, stack.pop()) 1995 | func = stack.pop() 1996 | if isinstance(func, PythonCompiledFunction): 1997 | stack.append(PythonCompiledFunctionCall(func)) 1998 | elif isinstance(func, PythonCompiledGenerator): 1999 | if npos_params != 1 or not isinstance(positional_params[0], PythonIterator): 2000 | raise PythonDecompilerError("Expected iterator argument for generator.", addr, opname, arg) 2001 | generator = self.__decompile_generator(func.code, positional_params[0]) 2002 | stack.append(generator) 2003 | elif isinstance(func, PythonCompiledComprehension): 2004 | if npos_params != 1 or not isinstance(positional_params[0], PythonIterator): 2005 | raise PythonDecompilerError("Expected iterator argument for comprehension.", addr, opname, arg) 2006 | comp = self.__decompile_comprehension(func.code, positional_params[0]) 2007 | stack.append(comp) 2008 | else: 2009 | if arg == 1 and isinstance(positional_params[0], PythonCompiledFunction): 2010 | stack.append(positional_params[0]) 2011 | statements.append(Decorator(func)) 2012 | else: 2013 | stack.append(FunctionCall(func, positional_params, keywords, var_args, kwvar_args)) 2014 | elif opname == 'COMPARE_OP': 2015 | right = stack.pop() 2016 | left = stack.pop() 2017 | stack.append(BinaryOp(left, arg, right)) 2018 | elif opname == 'CONTINUE_LOOP': 2019 | statements.append(Continue()) 2020 | return ( arg, statements ) 2021 | elif opname == 'DELETE_ATTR': 2022 | base = stack.pop() 2023 | statements.append(Del(GetAttr(base, arg))) 2024 | elif opname in ( 'DELETE_FAST', 'DELETE_NAME' ): 2025 | statements.append(Del(Variable(arg))) 2026 | elif opname == 'DELETE_GLOBAL': 2027 | if arg not in __builtin__.__dict__: 2028 | self.global_vars.add(Variable(arg)) 2029 | statements.append(Del(Variable(arg))) 2030 | elif opname == 'DELETE_SLICE+0': 2031 | statements.append(Del(BinarySubscr(stack.pop(), Slice()))) 2032 | elif opname == 'DELETE_SLICE+1': 2033 | expr = stack.pop() 2034 | statements.append(Del(BinarySubscr(stack.pop(), Slice(start = expr)))) 2035 | elif opname == 'DELETE_SLICE+2': 2036 | expr = stack.pop() 2037 | statements.append(Del(BinarySubscr(stack.pop(), Slice(stop = expr)))) 2038 | elif opname == 'DELETE_SLICE+3': 2039 | stop_expr = stack.pop() 2040 | start_expr = stack.pop() 2041 | statements.append(Del(BinarySubscr(stack.pop(), Slice(start = start_expr, stop = stop_expr)))) 2042 | elif opname == 'DELETE_SUBSCR': 2043 | index = stack.pop() 2044 | base = stack.pop() 2045 | statements.append(Del(BinarySubscr(base, index))) 2046 | elif opname == 'DUP_TOP': 2047 | stack.append(stack[-1]) 2048 | elif opname == 'DUP_TOPX': 2049 | stack.extend(stack[-arg:]) 2050 | elif opname == 'EXEC_STMT': 2051 | global_vars = stack.pop() 2052 | local_vars = stack.pop() 2053 | statements.append(Exec(stack.pop(), global_vars, local_vars)) 2054 | elif opname == 'END_FINALLY': 2055 | why = stack.pop() 2056 | if isinstance(why, PythonExceptionClass): 2057 | stack.pop() # exception instance 2058 | stack.pop() # traceback 2059 | return (addr + opsize, statements) 2060 | elif opname == 'FOR_ITER': 2061 | if len(stack) == 0 or (not isinstance(stack[-1], PythonIterator) and not isinstance(stack[-1], Variable)): 2062 | raise PythonDecompilerError("Expected iterator on the stack.", addr, opname, arg) 2063 | iterator = stack[-1] 2064 | if isinstance(iterator, Variable): 2065 | stack[-1] = iterator = PythonIterator(iterator) # cast variable to iterator 2066 | if not iterator.walked: 2067 | iterator.walked = True 2068 | iterator.alive = True 2069 | iterator.exhausted_addr = arg 2070 | stack.append(PythonIterate(iterator, addr)) 2071 | else: 2072 | iterator = stack.pop() # remove the iterator from the stack 2073 | iterator.alive = False 2074 | if_expr = None 2075 | if len(statements) > 0 and isinstance(statements[-1], If): 2076 | if len(statements[-1].statements) > 0 and isinstance(statements[-1].statements[0], PythonOpenedComprehension): 2077 | comp = statements[-1].statements[0].current # get comprehension 2078 | if_expr = statements[-1].expr 2079 | statements[-1] = statements[-1].statements[0] # destroy If 2080 | if len(statements) > 1 and isinstance(statements[-1], PythonOpenedComprehension): 2081 | comp = statements[-1].current 2082 | if isinstance(statements[-2], Assignment): 2083 | assign = statements[-2] 2084 | if isinstance(assign.right, PythonIterate) and assign.right.iterator is iterator: 2085 | iterable = iterator.expr 2086 | if isinstance(assign.left, ExpressionList): 2087 | var = assign.left.exprs 2088 | else: 2089 | var = [ assign.left ] 2090 | if comp.variables: 2091 | comp.expr = Comprehension(comp.expr, var, iterable) 2092 | statements[-1].current = comp.expr 2093 | else: 2094 | comp.variables = var 2095 | comp.iterable = iterable 2096 | if if_expr: 2097 | statements[-1].current.if_expr = if_expr 2098 | statements.pop(-2) # remove assign 2099 | if len(stack) > 0 and stack[-1] is statements[-1].init: 2100 | statements.pop() # no more iteration, remove opened comprehension 2101 | 2102 | n = self.__jmp_to_insn_at(insns, iterator.exhausted_addr) 2103 | elif opname == 'GET_ITER': 2104 | stack.append(PythonIterator(stack.pop())) 2105 | elif opname == 'IMPORT_FROM': 2106 | if len(stack) == 0 or not isinstance(stack[-1], PythonImportedModule): 2107 | raise PythonDecompilerError("Expected module on the stack.", addr, opname, arg) 2108 | stack.append(PythonImportedModuleAttr(stack[-1], Variable(arg))) 2109 | elif opname == 'IMPORT_NAME': 2110 | fromlist = stack.pop() 2111 | level = stack.pop() 2112 | if fromlist.value is None or isinstance(fromlist.value, list) and len(fromlist.value) == 0: 2113 | stack.append(PythonImportedModule(arg.split('.')[0])) 2114 | else: 2115 | stack.append(PythonImportedModule(arg)) 2116 | elif opname == 'IMPORT_STAR': 2117 | if len(stack) == 0 or not isinstance(stack[-1], PythonImportedModule): 2118 | raise PythonDecompilerError("Expected module on the stack.", addr, opname, arg) 2119 | statements.append(ImportFrom(stack.pop(), Variable('*'))) 2120 | elif opname[:8] == 'INPLACE_': 2121 | right = stack.pop() 2122 | left = stack.pop() 2123 | stack.append(BinaryOp(left, self.binary_operators[opname[8:]], right)) 2124 | elif opname == 'JUMP_FORWARD': 2125 | if arg <= addr: 2126 | raise PythonDecompilerError("Unexpected jump address in bytecode.", addr, opname, arg) 2127 | n = self.__jmp_to_insn_at(insns, arg) 2128 | elif opname == 'JUMP_ABSOLUTE': 2129 | if arg <= self.__first_addr(insns): # outer loop iteration 2130 | statements.append(Continue()) 2131 | # can't go any further in this block, let the caller decide where to resume 2132 | return (arg, statements) 2133 | else: 2134 | jmp_n = self.__jmp_to_insn_at(insns, arg) 2135 | n = jmp_n 2136 | elif opname == 'LIST_APPEND': 2137 | expr = stack.pop() 2138 | list_pos = arg or -1 # 2.7 can specify pos in argument 2139 | stack[-list_pos] = ListComprehension(Comprehension(expr, None, None)) 2140 | statements.append(PythonOpenedComprehension(stack[-list_pos])) 2141 | elif opname == 'LOAD_ATTR': 2142 | base = stack.pop() 2143 | if isinstance(base, PythonImportedModule): 2144 | stack.append(PythonImportedModuleAttr(base, Variable(arg))) 2145 | elif isinstance(base, PythonImportedModuleAttr): 2146 | base.module.path.append(base.name.name) 2147 | base.name = Variable(arg) 2148 | stack.append(base) 2149 | else: 2150 | stack.append(GetAttr(base, arg)) 2151 | elif opname == 'LOAD_CONST': 2152 | stack.append(Constant(arg)) 2153 | elif opname == 'LOAD_GLOBAL': 2154 | stack.append(Variable(arg)) 2155 | elif opname in ('LOAD_FAST', 'LOAD_NAME', 'LOAD_DEREF', 'LOAD_CLOSURE'): 2156 | stack.append(Variable(arg)) 2157 | elif opname == 'LOAD_LOCALS': 2158 | stack.append(FunctionCall(Variable('locals'))) 2159 | elif opname in ('MAKE_FUNCTION', 'MAKE_CLOSURE'): 2160 | fcode = stack.pop() 2161 | if opname == 'MAKE_CLOSURE': 2162 | stack.pop() # discard closure cells 2163 | if not isinstance(fcode, Constant) and not isinstance(fcode.value, types.CodeType): 2164 | raise PythonDecompilerError("Expected code object.") 2165 | default_args = [] 2166 | code = fcode.value 2167 | for i in range(0, arg): 2168 | default_args.insert(0, stack.pop()) 2169 | if code.co_name == '': 2170 | args = code.co_varnames[:code.co_argcount] 2171 | stack.append(Lambda(args, default_args, self.__decompile(code))) 2172 | elif code.co_name == '': 2173 | stack.append(PythonCompiledGenerator(code)) 2174 | elif code.co_name in ('', ''): 2175 | stack.append(PythonCompiledComprehension(code)) 2176 | else: 2177 | stack.append(PythonCompiledFunction(code, default_args)) 2178 | elif opname == 'MAP_ADD': 2179 | key_expr = stack.pop() 2180 | value_expr = stack.pop() 2181 | stack[-arg] = DictComprehension(Comprehension(DictComprehensionEntry(key_expr, value_expr), None, None)) 2182 | statements.append(PythonOpenedComprehension(stack[-arg])) 2183 | elif opname == 'NOP': 2184 | pass 2185 | elif opname == 'POP_BLOCK': 2186 | if n != len(insns): 2187 | next_addr, finalize = self.__decompile_block(insns[n:], stack) 2188 | statements.append(PythonBlockFinalizer(finalize)) 2189 | return (next_addr, statements) 2190 | elif opname == 'POP_TOP': 2191 | value = stack.pop() 2192 | if isinstance(value, FunctionCall) or isinstance(value, Yield): 2193 | #if isinstance(value, Expression) and not isinstance(value, Variable) and not isinstance(value, Constant): 2194 | statements.append(value) 2195 | elif opname == 'PRINT_ITEM': 2196 | if len(statements) > 0 and isinstance(statements[-1], Print) and not statements[-1].new_line: 2197 | statements[-1].expr_list.append(stack.pop()) 2198 | else: 2199 | statements.append(Print(ExpressionList([stack.pop()]))) 2200 | elif opname == 'PRINT_ITEM_TO': 2201 | filedesc = stack.pop() 2202 | if len(statements) > 0 and isinstance(statements[-1], ExtendedPrint) and statements[-1].filedesc == filedesc and not statements[-1].new_line: 2203 | statements[-1].expr_list.append(stack.pop()) 2204 | else: 2205 | statements.append(ExtendedPrint(filedesc, ExpressionList([stack.pop()]))) 2206 | elif opname == 'PRINT_NEWLINE': 2207 | if len(statements) > 0 and isinstance(statements[-1], Print) and not statements[-1].new_line: 2208 | statements[-1].new_line = True 2209 | else: 2210 | statements.append(Print()) 2211 | statements[-1].new_line = True 2212 | elif opname == 'PRINT_NEWLINE_TO': 2213 | filedesc = stack.pop() 2214 | if len(statements) > 0 and isinstance(statements[-1], ExtendedPrint) and statements[-1].filedesc == filedesc and not statements[-1].new_line: 2215 | statements[-1].new_line = True 2216 | else: 2217 | statements.append(ExtendedPrint(filedesc)) 2218 | statements[-1].new_line = True 2219 | elif opname == 'RAISE_VARARGS': 2220 | exception = param = trace = None 2221 | if arg == 0: 2222 | pass 2223 | elif arg == 1: 2224 | exception = stack.pop() 2225 | elif arg == 2: 2226 | param = stack.pop() 2227 | exception = stack.pop() 2228 | elif arg == 3: 2229 | trace = stack.pop() 2230 | param = stack.pop() 2231 | exception = stack.pop() 2232 | else: 2233 | raise PythonDecompilerError("Bad number of arguments", addr, opname, arg) 2234 | statements.append(Raise(exception, param, trace)) 2235 | return (None, statements) 2236 | elif opname == 'RETURN_VALUE': 2237 | statements.append(Return(stack.pop())) 2238 | return (None, statements) # next instruction unreachable 2239 | elif opname == 'ROT_FOUR': 2240 | tos = stack.pop(); tos1 = stack.pop(); tos2 = stack.pop(); tos3 = stack.pop() 2241 | stack.append(tos); stack.append(tos3); stack.append(tos2); stack.append(tos1) 2242 | elif opname == 'ROT_THREE': 2243 | tos = stack.pop(); tos1 = stack.pop(); tos2 = stack.pop() 2244 | stack.append(tos); stack.append(tos2); stack.append(tos1) 2245 | elif opname == 'ROT_TWO': 2246 | tos = stack.pop(); tos1 = stack.pop() 2247 | stack.append(tos); stack.append(tos1) 2248 | elif opname == 'SET_ADD': 2249 | expr = stack.pop() 2250 | stack[-arg] = SetComprehension(Comprehension(expr, None, None)) 2251 | statements.append(PythonOpenedComprehension(stack[-arg])) 2252 | elif opname in ('SETUP_EXCEPT', 'SETUP_FINALLY'): 2253 | next_addr, try_stmts = self.__decompile_try_catch(insns[n-1:], stack) 2254 | statements.extend(try_stmts) 2255 | if next_addr is None or next_addr <= self.__first_addr(insns): 2256 | return (next_addr, statements) 2257 | n = self.__jmp_to_insn_at(insns, next_addr) 2258 | elif opname == 'SETUP_LOOP': 2259 | endloop = self.__jmp_to_insn_at(insns, arg) 2260 | next_addr, loop_stmts = self.__decompile_loop(insns[n:endloop], stack) 2261 | statements.extend(loop_stmts) 2262 | if next_addr is None or next_addr <= self.__first_addr(insns): 2263 | return (next_addr, statements) 2264 | n = self.__jmp_to_insn_at(insns, next_addr) 2265 | elif opname == 'SETUP_WITH': 2266 | with_expr = stack.pop() 2267 | stack.append(Constant(None)) # Dummy why for the finally block 2268 | stack.append(PythonWithExit()) 2269 | stack.append(PythonWithEnter(with_expr)) 2270 | next_addr, with_block = self.__decompile_with(with_expr, arg, insns[n:], stack) 2271 | statements.append(with_block) 2272 | if next_addr is None or next_addr <= self.__first_addr(insns): 2273 | return (next_addr, statements) 2274 | n = self.__jmp_to_insn_at(insns, next_addr) 2275 | elif opname == 'SLICE+0': 2276 | stack.append(BinarySubscr(stack.pop(), Slice())) 2277 | elif opname == 'SLICE+1': 2278 | expr = stack.pop() 2279 | stack.append(BinarySubscr(stack.pop(), Slice(start = expr))) 2280 | elif opname == 'SLICE+2': 2281 | expr = stack.pop() 2282 | stack.append(BinarySubscr(stack.pop(), Slice(stop = expr))) 2283 | elif opname == 'SLICE+3': 2284 | stop_expr = stack.pop() 2285 | start_expr = stack.pop() 2286 | stack.append(BinarySubscr(stack.pop(), Slice(start = start_expr, stop = stop_expr))) 2287 | elif opname[:6] == 'STORE_': 2288 | store_type = opname[6:] 2289 | if store_type == 'ATTR': 2290 | base = stack.pop() 2291 | value = stack.pop() 2292 | target = GetAttr(base, arg) 2293 | elif store_type == 'GLOBAL': 2294 | value = stack.pop() 2295 | target = Variable(arg) 2296 | if arg not in __builtin__.__dict__: 2297 | self.global_vars.add(target) 2298 | elif store_type == 'MAP': 2299 | map_const = stack[-3] 2300 | if not isinstance(map_const, Constant) or not isinstance(map_const.value, types.DictType): 2301 | raise PythonDecompilerError("Expected dict type.", addr, opname, arg) 2302 | key = stack.pop() 2303 | value = stack.pop() 2304 | map_const.value[key] = value 2305 | continue 2306 | elif store_type == 'SLICE+0': 2307 | target = BinarySubscr(stack.pop(), Slice()) 2308 | value = stack.pop() 2309 | elif store_type == 'SLICE+1': 2310 | start_expr = stack.pop() 2311 | base = stack.pop() 2312 | target = BinarySubscr(base, Slice(start = start_expr)) 2313 | value = stack.pop() 2314 | elif store_type == 'SLICE+2': 2315 | stop_expr = stack.pop() 2316 | base = stack.pop() 2317 | target = BinarySubscr(base, Slice(stop = stop_expr)) 2318 | value = stack.pop() 2319 | elif store_type == 'SLICE+3': 2320 | stop_expr = stack.pop() 2321 | start_expr = stack.pop() 2322 | base = stack.pop() 2323 | value = stack.pop() 2324 | target = BinarySubscr(base, Slice(start = start_expr, stop = stop_expr)) 2325 | elif store_type == 'SUBSCR': 2326 | index = stack.pop() 2327 | base = stack.pop() 2328 | value = stack.pop() 2329 | target = BinarySubscr(base, index) 2330 | elif store_type in ('NAME', 'FAST', 'DEREF'): 2331 | value = stack.pop() 2332 | target = Variable(arg) 2333 | else: 2334 | raise PythonDecompilerError("Unhandled opcode", addr, opname, arg) 2335 | if isinstance(value, PythonCompiledFunction): 2336 | func = value 2337 | arg_count = func.code.co_argcount 2338 | args = func.code.co_varnames[:arg_count] 2339 | name = arg 2340 | varargs = None 2341 | if (func.code.co_flags & self.CODE_FLAG_VARARGS) != 0: 2342 | varargs = func.code.co_varnames[arg_count] 2343 | arg_count = arg_count + 1 2344 | kwvarargs = None 2345 | if (func.code.co_flags & self.CODE_FLAG_KWVARARGS) != 0: 2346 | kwvarargs = func.code.co_varnames[arg_count] 2347 | funcdef = FunctionDefinition(name, args, func.default_args, varargs, kwvarargs, self.__decompile(func.code)) 2348 | if len(func.code.co_consts) > 0 and func.code.co_consts[0]: 2349 | funcdef.set_docstring(func.code.co_consts[0]) 2350 | statements.append(funcdef) 2351 | elif isinstance(value, PythonCompiledClass): 2352 | classo = value 2353 | name = arg 2354 | statements.append(ClassDefinition(name, classo.supers, self.__decompile(classo.code))) 2355 | elif isinstance(value, PythonImportedModule): 2356 | if target.name != value.path[-1]: 2357 | statements.append(Import(value, as_name = target)) 2358 | else: 2359 | statements.append(Import(value)) 2360 | elif isinstance(value, PythonImportedModuleAttr): 2361 | if target != value.name: 2362 | statements.append(ImportFrom(value.module, value.name, as_name = target)) 2363 | else: 2364 | statements.append(ImportFrom(value.module, value.name)) 2365 | elif isinstance(value, PythonUnpackedValue): 2366 | seq = value.sequence 2367 | seq.bind(value.index, target) 2368 | while seq.nested: 2369 | if seq.is_complete(): 2370 | seq.parent.bind(seq.parent_index, Constant(tuple(seq.variables))) 2371 | seq = seq.parent 2372 | if seq.is_complete(): 2373 | statements.append(Assignment(ExpressionList(seq.variables), seq.expr)) 2374 | else: 2375 | statements.append(Assignment(target, value)) 2376 | elif opname[:6] == 'UNARY_': 2377 | if opname == 'UNARY_CONVERT': 2378 | stack.append(UnaryConvert(stack.pop())) 2379 | elif opname[6:] in self.unary_operators: 2380 | stack.append(UnaryOp(self.unary_operators[opname[6:]], stack.pop())) 2381 | else: 2382 | raise PythonDecompilerError("Unhandled opcode", addr, opname, arg) 2383 | elif opname == 'UNPACK_SEQUENCE': 2384 | packed_expr = stack.pop() 2385 | sequence = PythonUnpackedSequence(packed_expr, arg) 2386 | if isinstance(packed_expr, PythonUnpackedValue): 2387 | sequence.nested = True 2388 | sequence.parent = packed_expr.sequence 2389 | sequence.parent_index = packed_expr.index 2390 | for i in range(0, arg): 2391 | stack.append(PythonUnpackedValue(sequence, arg - i - 1)) 2392 | elif opname == 'WITH_CLEANUP': 2393 | while not isinstance(stack[-1], PythonWithExit): 2394 | stack.pop() # Reason 2395 | stack.pop() # PythonWithExit 2396 | elif opname == 'YIELD_VALUE': 2397 | stack.append(Yield(stack.pop())) 2398 | else: 2399 | raise PythonDecompilerError("Unhandled opcode", addr, opname, arg) 2400 | 2401 | if len(insns) > 0: 2402 | return ( self.__last_addr(insns), statements ) 2403 | else: 2404 | return ( None, statements ) 2405 | 2406 | def decompile(self, indent = ' '): 2407 | program = PythonProgram(self.__decompile(self.pyc.code)) 2408 | if len(self.global_vars) > 0: 2409 | start = isinstance(program.statements[0], DocString) and 1 or 0 2410 | program.statements.insert(start, Global(ExpressionList(list(self.global_vars)))) 2411 | return program.write(indent) + "\n" 2412 | 2413 | def usage(): 2414 | print("Usage: %s [options] " % sys.argv[0]) 2415 | print("Reverse a Python pyc file back into a human readable form.") 2416 | print(" -h, --help Display this message.") 2417 | print(" --disass Disassemble bytecode only.") 2418 | print(" -i style, --indent=style Set indentation style (default to 4s).") 2419 | print(" style is [num]char where char is 's' for spaces or 't' for tabs.") 2420 | print(" -o file, --output=file Redirect output to file (default to stdout).") 2421 | 2422 | if __name__ == "__main__": 2423 | import getopt 2424 | import re 2425 | 2426 | try: 2427 | opts, args = getopt.getopt(sys.argv[1:], "hi:o:", ["help", "disass", "indent=", "output="]) 2428 | except getopt.GetoptError: 2429 | usage() 2430 | exit(1) 2431 | 2432 | indent_pattern = ' ' * 4 2433 | disassemble = False 2434 | output = sys.stdout 2435 | for opt, arg in opts: 2436 | if opt in ("-h", "--help"): 2437 | usage() 2438 | exit() 2439 | elif opt in ("--disass"): 2440 | disassemble = True 2441 | elif opt in ("-i", "--indent"): 2442 | m = re.match("^(\d?)(s|t)$", arg) 2443 | if not m: 2444 | usage() 2445 | exit(1) 2446 | indent_num = int(len(m.group(1)) > 0 and m.group(1) or "1") 2447 | indent_char = {"s":" ", "t": "\t"}[m.group(2)] 2448 | indent_pattern = indent_num * indent_char 2449 | elif opt in ("-o", "--output"): 2450 | try: 2451 | output = open(arg, "w") 2452 | except IOError: 2453 | print("Error: cannot open '%s' for writing" % arg) 2454 | exit(1) 2455 | 2456 | if len(args) != 1: 2457 | usage() 2458 | exit(1) 2459 | 2460 | target = args[0] 2461 | try: 2462 | pydec = PythonDecompiler(target) 2463 | if disassemble: 2464 | dump = pydec.disassemble() 2465 | else: 2466 | dump = pydec.decompile(indent = indent_pattern) 2467 | output.write(dump) 2468 | except PythonDecompilerError as e: 2469 | print("Error: " + str(e)) 2470 | finally: 2471 | output.close() 2472 | 2473 | --------------------------------------------------------------------------------