├── .flake8 ├── .gitignore ├── LICENSE ├── README.md ├── detectors ├── code_injection.py └── template_script.py ├── dev-requirements.txt ├── setup_scripts ├── install_deps.sh └── install_framework.sh ├── src ├── php_bytecode_api │ ├── php_bytecode_api │ │ ├── __init__.py │ │ ├── api.py │ │ ├── helper_functions.py │ │ ├── model_utils.py │ │ └── models.py │ ├── requirements.txt │ └── setup.py └── php_to_bytecode_converter │ ├── setup.py │ └── src │ ├── includes │ ├── php_cfg.h │ ├── php_dfg.h │ ├── php_embed_ex.h │ ├── php_ssa.h │ └── zend_worklist_ex.h │ ├── libs │ ├── php_cfg.c │ ├── php_dfg.c │ ├── php_embed_ex.c │ └── php_ssa.c │ └── main.c └── vulnerabilities └── vulnerable_web_apps.md /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 88 3 | extend-ignore = E203 4 | exclude = venv,docs, src/php_to_bytecode_converter/build, src/php_bytecode_api/build, tmp -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | 162 | targets/DVWA 163 | targets/* 164 | reports/* 165 | tmp/* 166 | src/php_to_bytecode_converter/build 167 | 168 | .DS_Store 169 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 *finixbit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-bytecode-security-framework 2 | 3 | A high-level Python API for converting PHP files into a PHP Bytecode Pydantic model. It incorporates additional functions to assist in the analysis of PHP bytecode. The implementation involves a Python module written in `C` to convert PHP source files to Zend bytecode (op_arrays) Python objects. 4 | 5 | ## Why `php-bytecode-security-framework` 6 | 7 | Please visit 8 | 9 | ## Requirements 10 | 11 | [Docker](https://www.docker.com/) - Build, test, and deploy applications quickly. 12 | 13 | ## Setting up 14 | ```sh 15 | git clone https://github.com/finixbit/php-bytecode-security-framework 16 | cd php-bytecode-security-framework 17 | 18 | docker run --rm -it --entrypoint /bin/bash -v ${PWD}/:/app debian:buster-20220801 19 | cd /app 20 | 21 | ./setup_scripts/install_deps.sh 22 | ./setup_scripts/install_framework.sh 23 | ``` 24 | 25 | ## Example Test cases 26 | Before running any of the test cases below, you need to set the docker container by following the [setup above](#setting-up) 27 | 28 | ### Testing with Vulnerable Web Applications 29 | All tests were done with `detection_script/simple_code_injection.py` which only models generic PHP global variables (SOURCES) like `$_GET, $_POST, $_REQUEST` and traces data down to potentially vulnerable calls (SINKS) like `echo, concat, include, require, cast, etc`. 30 | 31 | | Name | Vulns | Report | Github 32 | | ----------- |:-------------:| -----:| -----:| 33 | | Damn Vulnerable Web Application (DVWA) | `21` | [Report Link](https://github.com/finixbit/php-bytecode-security-framework/blob/master/vulnerabilities/vulnerable_web_apps.md#testing-with-dvwa) | [Github Repo](https://github.com/digininja/DVWA) | 34 | | OWASP Vulnerable Web Application Project | `10` | [Report Link](https://github.com/finixbit/php-bytecode-security-framework/blob/master/vulnerabilities/vulnerable_web_apps.md#testing-with-owasp-vuln-web-app) | [Github Repo](https://github.com/OWASP/Vulnerable-Web-Application) | 35 | | Simple SQL Injection Training App | `15` | [Report Link](https://github.com/finixbit/php-bytecode-security-framework/blob/master/vulnerabilities/vulnerable_web_apps.md#testing-with-sqlinjection-training-app) | [Github Repo](https://github.com/appsecco/sqlinjection-training-app) | 36 | | Vulnerable Web application made with PHP/SQL | `6` | [Report Link](https://github.com/finixbit/php-bytecode-security-framework/blob/master/vulnerabilities/vulnerable_web_apps.md#testing-with-oste-vulnerable-web-application) | [Github Repo](https://github.com/OSTEsayed/OSTE-Vulnerable-Web-Application) | 37 | | InsecureTrust_Bank - Educational repo demonstrating web app vulnerabilities | `5` | [Report Link](https://github.com/finixbit/php-bytecode-security-framework/blob/master/vulnerabilities/vulnerable_web_apps.md#testing-with-insecuretrust_bank) | [Github Repo](https://github.com/Hritikpatel/InsecureTrust_Bank) | 38 | 39 | 40 | ## Projects 41 | 42 | ### php_bytecode_api (`src/php_bytecode_api`) 43 | A high-level Python API for `src/php_to_bytecode_converter`, intended for the conversion of PHP files into a defined PHP Bytecode Pydantic model. This includes additional functions to facilitate the analysis of PHP bytecode. 44 | 45 | ```python 46 | import php_bytecode_api 47 | functions: list[FunctionModel] = php_bytecode_api.convert_and_lift(path_to_php_file) 48 | ``` 49 | 50 | See [Pydantic Models](https://github.com/finixbit/php-bytecode-security-framework/blob/master/src/php_bytecode_api/models.py) below for more details on the data structure `FunctionModel` extracted from the PHP Bytecode. 51 | 52 | ### php_to_bytecode_converter (`src/php_to_bytecode_converter`) 53 | A Python module is written in `C` to convert PHP source files to Zend bytecode (op_arrays), CFG, DFG, and SSA Python objects. 54 | 55 | ```python 56 | import php_to_bytecode_converter 57 | functions: list[dict] = php_to_bytecode_converter.convert(path_to_php_file) 58 | ``` 59 | 60 | Below is the data structure extracted from each function found in a PHP file: 61 | ```yaml 62 | - filename: str 63 | - class_name: str 64 | - function_name: str 65 | - num_args: int 66 | - required_num_args: int 67 | - number_of_instructions: int 68 | - extra = { 69 | type: int 70 | number_of_cv_variables: int 71 | number_of_tmp_variables: int 72 | last_live_range: int 73 | last_try_catch: int 74 | arg_flags: list [int, int, int] 75 | last_literal: int 76 | literals: [ 77 | { 78 | type: str 79 | value: str 80 | } 81 | ] 82 | } 83 | - instructions = [ 84 | { 85 | op1: { 86 | type: str 87 | value: str 88 | variable_number: int 89 | } 90 | op2: { 91 | type: str 92 | value: str 93 | variable_number: int 94 | } 95 | result: { 96 | type: str 97 | value: str 98 | variable_number: int 99 | } 100 | num: int 101 | extended_value: int 102 | lineno: int 103 | opcode: int 104 | opcode_name: str 105 | opcode_flags: int 106 | op1_type: int 107 | op2_type: int 108 | result_type: int 109 | } 110 | ] 111 | - cfg = { 112 | blocks_count: int 113 | edges_count: int 114 | blocks: [ 115 | { 116 | start: int 117 | len: int 118 | successors_count: int 119 | predecessors_count: int 120 | predecessor_offset: int 121 | idom: int 122 | loop_header: int 123 | level: int 124 | children: int 125 | next_child: int 126 | successors_storage: [int, int] 127 | successors: [int, int] 128 | } 129 | ] 130 | predecessors: [int, int] 131 | } 132 | - dfg = [ 133 | { 134 | block_index: int 135 | var_def: [ 136 | { 137 | var_num: int 138 | var_name: str 139 | } 140 | ] 141 | var_use: [ 142 | { 143 | var_num: int 144 | var_name: str 145 | } 146 | ] 147 | var_in: [ 148 | { 149 | var_num: int 150 | var_name: str 151 | } 152 | ] 153 | var_out: [ 154 | { 155 | var_num: int 156 | var_name: str 157 | } 158 | ] 159 | var_tmp: [ 160 | { 161 | var_num: int 162 | var_name: str 163 | } 164 | ] 165 | } 166 | ] 167 | - ssa = { 168 | number_of_sccs: int 169 | number_of_ssa_variables: int 170 | ssa_variables: [ 171 | { 172 | ssa_var_num: int 173 | var_num: int 174 | var_name: str 175 | definition: int 176 | definition_phi: { 177 | pi: int 178 | variable_index: int 179 | variable: { 180 | var_num: int 181 | var_name: str 182 | } 183 | ssa_variable_index: int 184 | current_block_index: int 185 | visited: int 186 | has_range_constraint: int 187 | constraint: { 188 | range_range_min: int 189 | range_range_max: int 190 | range_range_underflow: int 191 | range_range_overflow: int 192 | range_min_var: int 193 | range_max_var: int 194 | range_min_ssa_var: int 195 | range_max_ssa_var: int 196 | } 197 | sources: [...int] 198 | } 199 | no_val: int 200 | use_chain: int 201 | escape_state: int 202 | strongly_connected_component: int 203 | strongly_connected_component_entry: int 204 | 205 | } 206 | ] 207 | ssa_instructions: [ 208 | { 209 | op1_use: int 210 | op2_use: int 211 | result_use: int 212 | op1_def: int 213 | op2_def: int 214 | result_def: int 215 | op1_use_chain: int 216 | op2_use_chain: int 217 | res_use_chain: int 218 | } 219 | ] 220 | ssa_blocks: [ 221 | { 222 | block_index; int 223 | phis; [ 224 | { 225 | pi: int 226 | variable_index: int 227 | variable: { 228 | var_num: int 229 | var_name: str 230 | } 231 | ssa_variable_index: int 232 | current_block_index: int 233 | visited: int 234 | has_range_constraint: int 235 | constraint: { 236 | range_range_min: int 237 | range_range_max: int 238 | range_range_underflow: int 239 | range_range_overflow: int 240 | range_min_var: int 241 | range_max_var: int 242 | range_min_ssa_var: int 243 | range_max_ssa_var: int 244 | } 245 | sources: [...int] 246 | } 247 | ] 248 | } 249 | ] 250 | 251 | } 252 | ``` 253 | 254 | ### detectors 255 | Directory to hold all scripts 256 | - `detectors/template_script.py` - template script to get started. 257 | - `detectors/code_injection.py` - Models generic PHP global variable (SOURCES) like `$_GET, $_POST, $_REQUEST` and traces data down to potentially vulnerable calls (SINKS) like `echo, concat, include, etc`. 258 | 259 | ### targets 260 | Directory to store target source files. Follow the [Example Test Cases](#example-test-cases) above to set up a target. 261 | 262 | -------------------------------------------------------------------------------- /detectors/code_injection.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | import datetime 4 | import re 5 | from pydantic import BaseModel 6 | from collections import defaultdict 7 | from typing import List 8 | from php_bytecode_api import convert_and_lift, helper_functions, models 9 | 10 | rootdir = "/app/targets" 11 | 12 | TOTAL_LOW_VULNS_FOUND = 0 13 | TOTAL_HIGH_VULNS_FOUND = 0 14 | 15 | NOW_TS = int(datetime.datetime.now().timestamp()) 16 | LOG_FILENAME = f"{'__'.join(rootdir.split('/'))}_{NOW_TS}.log" 17 | 18 | file_handler = logging.FileHandler(filename=f"reports/{LOG_FILENAME}") 19 | stdout_handler = logging.StreamHandler(stream=sys.stdout) 20 | handlers = [file_handler, stdout_handler] 21 | 22 | logging.basicConfig( 23 | level=logging.INFO, 24 | # format='[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s', 25 | format="%(message)s", 26 | handlers=handlers, 27 | ) 28 | 29 | VULNS_IDS_TRACKER = defaultdict(lambda: []) 30 | VULNS = [] 31 | 32 | 33 | class StateModel(BaseModel): 34 | visited_instructions_index: List[int] = [] 35 | has_sql_string: bool = False 36 | visited_function_calls: List[str] = [] 37 | 38 | 39 | def _is_sql_string(data): 40 | SIMPLE_SQL_RE = re.compile( 41 | r"(select\s.*from\s|" 42 | r"delete\s+from\s|" 43 | r"insert\s+into\s.*values\s|" 44 | r"update\s.*set\s)", 45 | re.IGNORECASE | re.DOTALL, 46 | ) 47 | return SIMPLE_SQL_RE.search(data) is not None 48 | 49 | 50 | def save_vuln( 51 | impact: str, 52 | function: models.FunctionModel, 53 | has_sql_string: bool, 54 | visited_instructions_index: List[int], 55 | visited_function_calls: List[str], 56 | ): 57 | vuln_file_fn = f"{function.filename}:{function.class_name}:{function.function_name}" 58 | vuln_key = "::".join([str(i) for i in visited_instructions_index]) 59 | 60 | if vuln_key in VULNS_IDS_TRACKER[vuln_file_fn]: 61 | return 62 | 63 | VULNS_IDS_TRACKER[vuln_file_fn].append(vuln_key) 64 | 65 | VULN_INFO = { 66 | "impact": impact, 67 | "vuln_name": "sql_injection" if has_sql_string else "code_injection", 68 | "visited_function_calls": ",".join(visited_function_calls), 69 | "vuln_file": vuln_file_fn, 70 | "vuln_key": vuln_key, 71 | "lines": [], 72 | } 73 | 74 | visited_lines = [] 75 | for instruction_index in visited_instructions_index[::-1]: 76 | lineno = function.instructions[instruction_index].lineno 77 | if lineno not in visited_lines: 78 | line = ( 79 | f"[*] lineno={lineno}" 80 | f" ------ {function.instructions[instruction_index].source_line.strip()}" # noqa: E501 81 | ) 82 | visited_lines.append(lineno) 83 | VULN_INFO["lines"].append(line) 84 | 85 | vuln_key = ":_:".join([str(i) for i in visited_lines]) 86 | if vuln_key in VULNS_IDS_TRACKER[vuln_file_fn]: 87 | return 88 | 89 | global TOTAL_HIGH_VULNS_FOUND 90 | global TOTAL_LOW_VULNS_FOUND 91 | 92 | if impact == "HIGH": 93 | TOTAL_HIGH_VULNS_FOUND = TOTAL_HIGH_VULNS_FOUND + 1 94 | 95 | if impact == "LOW": 96 | TOTAL_LOW_VULNS_FOUND = TOTAL_LOW_VULNS_FOUND + 1 97 | 98 | VULNS_IDS_TRACKER[vuln_file_fn].append(vuln_key) 99 | VULNS.append(VULN_INFO) 100 | 101 | 102 | def check_vuln( 103 | function: models.FunctionModel, state: StateModel, instruction_index: int 104 | ): 105 | instruction = function.instructions[instruction_index] 106 | 107 | if instruction.is_global_fetch_instruction: 108 | prev_instruction_index = state.visited_instructions_index[ 109 | len(state.visited_instructions_index) - 1 110 | ] 111 | prev_instruction = function.instructions[prev_instruction_index] 112 | 113 | if prev_instruction.opcode_name.startswith("ZEND_FETCH"): 114 | if ( 115 | prev_instruction.op1.type == "IS_CONST" 116 | and prev_instruction.op1.value 117 | in ["_POST", "_GET", "_COOKIE", "_REQUEST", "_FILES"] 118 | ): # _SESSION 119 | save_vuln( 120 | "HIGH", 121 | function, 122 | state.has_sql_string, 123 | state.visited_instructions_index, 124 | state.visited_function_calls, 125 | ) 126 | return True 127 | 128 | if ( 129 | instruction.opcode_name in ["ZEND_RECV", "ZEND_RECV_INIT", "ZEND_RECV_VARIADIC"] 130 | and state.has_sql_string 131 | ): 132 | save_vuln( 133 | "LOW", 134 | function, 135 | state.has_sql_string, 136 | state.visited_instructions_index, 137 | state.visited_function_calls, 138 | ) 139 | return True 140 | 141 | return False 142 | 143 | 144 | def trace_instruction_variable_to_global_variable( 145 | function: models.FunctionModel, state: StateModel, instruction_index: int 146 | ): 147 | new_state = state.copy() 148 | 149 | if instruction_index in new_state.visited_instructions_index: 150 | return 151 | 152 | if instruction_index not in new_state.visited_instructions_index: 153 | new_state.visited_instructions_index.append(instruction_index) 154 | 155 | insn = function.instructions[instruction_index] 156 | 157 | if check_vuln(function, new_state, instruction_index): 158 | return 159 | 160 | # for function calls, add results 161 | if insn.is_function_call_instruction: 162 | visited_fn = helper_functions.get_recent_function_call_from_instruction_index( 163 | function, instruction_index 164 | ) 165 | if visited_fn not in new_state.visited_function_calls: 166 | new_state.visited_function_calls.append(visited_fn) 167 | 168 | for arg in helper_functions.args_without_result(insn): 169 | new_state = state.copy() 170 | if arg.type == "IS_CONST" and _is_sql_string(arg.value.lower()): 171 | new_state.has_sql_string = True 172 | continue 173 | 174 | if arg.variable_number is None: 175 | continue 176 | 177 | # : 178 | # if its not a function call, variable must be used in only current instruction 179 | if ( 180 | not insn.is_function_call_instruction 181 | and arg.variable_number not in insn.variables_used_here 182 | ): 183 | continue 184 | 185 | var_definition_at_instruction_index = ( 186 | helper_functions.get_instruction_index_where_variable_number_is_defined( 187 | function=function, variable_number=arg.variable_number 188 | ) 189 | ) 190 | if var_definition_at_instruction_index > -1: 191 | trace_instruction_variable_to_global_variable( 192 | function=function, 193 | state=new_state, 194 | instruction_index=var_definition_at_instruction_index, 195 | ) 196 | 197 | var_definition_phi_at_instructions_index = helper_functions.get_instructions_index_where_variable_number_is_defined_phi( # noqa: E501 198 | function=function, variable_number=arg.variable_number 199 | ) 200 | if var_definition_phi_at_instructions_index: 201 | for ( 202 | var_definition_phi_at_instruction_index 203 | ) in var_definition_phi_at_instructions_index: 204 | trace_instruction_variable_to_global_variable( 205 | function=function, 206 | state=new_state, 207 | instruction_index=var_definition_phi_at_instruction_index, 208 | ) 209 | 210 | 211 | def analyze_function(function: models.FunctionModel): 212 | potential_vuln_opcode_names = [ 213 | "ZEND_ECHO", 214 | "ZEND_ROPE_INIT", 215 | "ZEND_ROPE_ADD", 216 | "ZEND_ROPE_END", 217 | "ZEND_CONCAT", 218 | "ZEND_FAST_CONCAT", 219 | "ZEND_INCLUDE_OR_EVAL", 220 | "ZEND_EVAL", 221 | "ZEND_INCLUDE", 222 | "ZEND_INCLUDE_ONCE", 223 | "ZEND_REQUIRE", 224 | "ZEND_REQUIRE_ONCE", 225 | "ZEND_CAST", 226 | ] 227 | 228 | for instruction_index, instruction in enumerate(function.instructions): 229 | has_sql_string = False 230 | 231 | if instruction.opcode_name not in potential_vuln_opcode_names: 232 | continue 233 | 234 | if instruction.opcode_name in ["ZEND_ROPE_ADD", "ZEND_ROPE_END"]: 235 | """ 236 | check if it's predecessor instructions (ZEND_ROPE_INIT, ZEND_ROPE_ADD) 237 | is an SQL query string 238 | """ 239 | current_instruction_index = instruction_index - 1 240 | while True: 241 | if current_instruction_index < 1: 242 | break 243 | 244 | prev_insn = function.instructions[current_instruction_index] 245 | if prev_insn not in ["ZEND_ROPE_ADD", "ZEND_ROPE_INIT"]: 246 | break 247 | 248 | for arg in helper_functions.args_without_result(prev_insn): 249 | if arg.type == "IS_CONST" and _is_sql_string(arg.value.lower()): 250 | has_sql_string = True 251 | 252 | current_instruction_index -= 1 253 | 254 | for arg in helper_functions.args_without_result(instruction): 255 | state = StateModel(has_sql_string=has_sql_string) 256 | 257 | if arg.type == "IS_CONST" and _is_sql_string(arg.value.lower()): 258 | state.has_sql_string = True 259 | continue 260 | 261 | if arg.variable_number is None: 262 | continue 263 | 264 | if ( 265 | arg.variable_number 266 | not in helper_functions.variables_used_at_instruction( 267 | function, instruction_index 268 | ) 269 | ): 270 | continue 271 | 272 | var_definition_at_instruction_index = ( 273 | helper_functions.get_instruction_index_where_variable_number_is_defined( 274 | function=function, variable_number=arg.variable_number 275 | ) 276 | ) 277 | 278 | if var_definition_at_instruction_index > -1: 279 | if instruction_index not in state.visited_instructions_index: 280 | state.visited_instructions_index.append(instruction_index) 281 | 282 | trace_instruction_variable_to_global_variable( 283 | function=function, 284 | state=state, 285 | instruction_index=var_definition_at_instruction_index, 286 | ) 287 | 288 | 289 | def main(): 290 | for file_path in helper_functions.recursively_get_directory_php_files(rootdir): 291 | functions = convert_and_lift(file_path) 292 | if not functions: 293 | logging.error(f"Loading ... {file_path} -- No Functions Found") 294 | else: 295 | for function in functions: 296 | print( 297 | f"Analyzing function ... {file_path}:{function.class_name}:{function.function_name}" # noqa: E501 298 | ) 299 | analyze_function(function) 300 | 301 | def log_total_vulns(): 302 | logging.info(f"[*] {'-'*100}") 303 | logging.info(f"[*] {'-'*100}") 304 | logging.info(f"[*] TOTAL_VULNS = {TOTAL_HIGH_VULNS_FOUND}") 305 | logging.info("[*]") 306 | 307 | log_total_vulns() 308 | 309 | for index, vuln in enumerate(VULNS): 310 | logging.info(f"[*] {'-'*100}") 311 | logging.info(f"[*] {'-'*100}") 312 | logging.info(f"[*] INDEX = {index+1}") 313 | logging.info(f"[*] IMPACT = {vuln['impact']}") 314 | logging.info(f"[*] VULNERABILTY TYPE = {vuln['vuln_name']}") 315 | logging.info(f"[*] VISITED FUNCTION CALLS = {vuln['visited_function_calls']}") 316 | logging.info(f"[*] FILE = {vuln['vuln_file']}") 317 | logging.info("[*] ") 318 | logging.info("[*] --- SOURCE PATH TO VULNERABILTY ---") 319 | 320 | for line in vuln["lines"]: 321 | logging.info(line) 322 | 323 | logging.info("[*]") 324 | logging.info("[*]") 325 | 326 | log_total_vulns() 327 | 328 | 329 | if __name__ == "__main__": 330 | main() 331 | -------------------------------------------------------------------------------- /detectors/template_script.py: -------------------------------------------------------------------------------- 1 | from php_bytecode_api import convert_and_lift 2 | from php_bytecode_api import helper_functions 3 | from php_bytecode_api import models 4 | import logging 5 | 6 | logging.getLogger().setLevel(logging.INFO) 7 | 8 | rootdir = "/app/targets" 9 | 10 | 11 | def do_something(function: models.FunctionModel): 12 | pass 13 | 14 | 15 | def main(): 16 | for file_path in helper_functions.recursively_get_directory_php_files(rootdir): 17 | functions = convert_and_lift(file_path) 18 | if not functions: 19 | logging.error(f"Loading ... {file_path} -- No Functions Found") 20 | else: 21 | for function in functions: 22 | logging.info( 23 | f"Analyzing function ... {file_path}:{function.function_name}" 24 | ) 25 | do_something(function) 26 | 27 | 28 | if __name__ == "__main__": 29 | main() 30 | -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | black==24.8.0 2 | flake8==5.0.4 -------------------------------------------------------------------------------- /setup_scripts/install_deps.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | apt update 4 | apt install -y gcc g++ make git 5 | apt install -y libphp-embed php-dev php-phpdbg procps 6 | apt install -y python3 python3-dev python3-pip 7 | 8 | echo "done ..." 9 | -------------------------------------------------------------------------------- /setup_scripts/install_framework.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # install php_to_bytecode_converter 5 | # 6 | cd /app/src/php_to_bytecode_converter 7 | python3 setup.py install 8 | 9 | # 10 | # install php_bytecode_api 11 | # 12 | cd /app/src/php_bytecode_api 13 | python3 setup.py install 14 | 15 | cd /app 16 | 17 | echo "done ..." -------------------------------------------------------------------------------- /src/php_bytecode_api/php_bytecode_api/__init__.py: -------------------------------------------------------------------------------- 1 | from .api import convert_and_lift 2 | 3 | 4 | __all__ = ["convert_and_lift"] 5 | -------------------------------------------------------------------------------- /src/php_bytecode_api/php_bytecode_api/api.py: -------------------------------------------------------------------------------- 1 | from php_bytecode_api.models import FunctionModel 2 | import php_to_bytecode_converter 3 | 4 | 5 | def convert_and_lift(file_path: str) -> [FunctionModel]: 6 | functions = php_to_bytecode_converter.convert(file_path) 7 | if not functions: 8 | print(f"Unable to convert file path {file_path} to PHP Bytecodes") 9 | return 10 | try: 11 | source_lines = open(file_path, "rb").readlines() 12 | for fn in functions: 13 | fn.update({"source_lines": source_lines}) 14 | fn_obj = FunctionModel(**fn) 15 | fn_obj.update_all() 16 | yield fn_obj 17 | except Exception as e: 18 | print(str(e)) 19 | -------------------------------------------------------------------------------- /src/php_bytecode_api/php_bytecode_api/helper_functions.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | FUNCTION_CALL_OPCODE_NAMES = [ 4 | "ZEND_DO_FCALL_BY_NAME", 5 | "ZEND_DO_ICALL", 6 | "ZEND_DO_FCALL", 7 | "ZEND_DO_UCALL", 8 | "ZEND_CALLABLE_CONVERT", 9 | ] 10 | 11 | 12 | def recursively_get_directory_php_files(rootdir): 13 | for subdir, dirs, files in os.walk(rootdir): 14 | for file in files: 15 | file_path = os.path.join(subdir, file) 16 | if not file_path.endswith(".php"): 17 | continue 18 | yield file_path 19 | 20 | 21 | def args(instruction, filter_operand_type=None): 22 | results = [] 23 | for operand in [instruction.op1, instruction.op2, instruction.result]: 24 | if filter_operand_type and operand.type not in filter_operand_type: 25 | continue 26 | results.append(operand) 27 | return results 28 | 29 | 30 | def args_without_result(instruction, filter_operand_type=None): 31 | results = [] 32 | for operand in [instruction.op1, instruction.op2]: 33 | if filter_operand_type and operand.type not in filter_operand_type: 34 | continue 35 | results.append(operand) 36 | return results 37 | 38 | 39 | def variables_used_at_function_call_args(function, instruction_index): 40 | instruction = function.instructions[instruction_index] 41 | variables = [] 42 | instructions_to_extract = [] 43 | 44 | if instruction.opcode_name in FUNCTION_CALL_OPCODE_NAMES: 45 | current_instruction_index = instruction_index 46 | while current_instruction_index >= 0: 47 | insn = function.instructions[current_instruction_index] 48 | # print("[***]", insn.opcode_name) 49 | 50 | # break if function start opcode is reached 51 | if insn.opcode_name in ["ZEND_INIT_FCALL_BY_NAME", "ZEND_INIT_FCALL"]: 52 | break 53 | 54 | # retrieve instruction if function argument is sent to call 55 | if insn.opcode_name in [ 56 | "ZEND_SEND_VAL", 57 | "ZEND_SEND_FUNC_ARG", 58 | "ZEND_SEND_VAR_EX", 59 | "ZEND_SEND_VAR", 60 | ]: 61 | # print("[yes]", insn.opcode_name) 62 | instructions_to_extract.append(insn) 63 | 64 | current_instruction_index = current_instruction_index - 1 65 | 66 | for insn in instructions_to_extract: 67 | for arg in args(insn): 68 | if arg.variable_number is not None and arg.variable_number >= 0: 69 | if arg.type != "IS_UNUSED" and arg.variable_number not in variables: 70 | variables.append(arg.variable_number) 71 | return variables 72 | 73 | 74 | def variables_used_at_instruction( 75 | function, instruction_index, is_function_call_instruction=False 76 | ): 77 | variables = [] 78 | if is_function_call_instruction: 79 | variables = variables_used_at_function_call_args(function, instruction_index) 80 | else: 81 | for var in function.ssa.ssa_variables: 82 | # use_chain is an instruction' index 83 | if var.use_chain >= 0: 84 | if var.use_chain == instruction_index: 85 | variables.append(var.var_num) 86 | return variables 87 | 88 | 89 | def get_instruction_index_where_variable_number_is_defined(function, variable_number): 90 | instruction_index = -1 91 | for var in function.ssa.ssa_variables: 92 | # definition is an instruction' index 93 | if var.definition >= 0: 94 | if var.var_num == variable_number: 95 | instruction_index = var.definition 96 | return instruction_index 97 | 98 | 99 | def get_recent_function_call_from_instruction_index(function, instruction_index): 100 | # https://php-legacy-docs.zend.com/manual/php5/en/internals2.opcodes.init-fcall-by-name 101 | 102 | fcall_name = "UNDEFINED - UNABLE TO FETCH FCALL NAME" 103 | current_instruction_index = instruction_index 104 | while current_instruction_index > -1: 105 | insn = function.instructions[current_instruction_index] 106 | if insn.opcode_name in [ 107 | "ZEND_INIT_FCALL_BY_NAME", 108 | "ZEND_INIT_FCALL", 109 | "ZEND_INIT_STATIC_METHOD_CALL", 110 | ]: 111 | fcall_name = insn.op2.value 112 | break 113 | 114 | current_instruction_index = current_instruction_index - 1 115 | return fcall_name 116 | 117 | 118 | def get_instructions_index_where_variable_number_is_defined_phi( 119 | function, variable_number 120 | ): 121 | definition_phi_sources = [] 122 | for var in function.ssa.ssa_variables: 123 | if var.var_num == variable_number: 124 | definition_phi_sources = var.definition_phi.sources 125 | 126 | instructions_index = [] 127 | if definition_phi_sources: 128 | for source in definition_phi_sources: 129 | for var in function.ssa.ssa_variables: 130 | if var.ssa_var_num == source: 131 | instructions_index.append(var.definition) 132 | return instructions_index 133 | -------------------------------------------------------------------------------- /src/php_bytecode_api/php_bytecode_api/model_utils.py: -------------------------------------------------------------------------------- 1 | from php_bytecode_api.helper_functions import args, FUNCTION_CALL_OPCODE_NAMES 2 | 3 | 4 | BRANCH_OPCODE_NAMES = [ 5 | "ZEND_RECV", 6 | "ZEND_RECV_INIT" "ZEND_RETURN", 7 | "ZEND_RETURN_BY_REF", 8 | "ZEND_GENERATOR_RETURN", 9 | "ZEND_EXIT", 10 | "ZEND_THROW" "ZEND_INCLUDE_OR_EVAL", 11 | "ZEND_GENERATOR_CREATE", 12 | "ZEND_YIELD", 13 | "ZEND_YIELD_FROM", 14 | "ZEND_DO_FCALL", 15 | "ZEND_DO_UCALL", 16 | "ZEND_DO_FCALL_BY_NAME", 17 | "ZEND_FAST_CALL", 18 | "ZEND_FAST_RET", 19 | "ZEND_JMP", 20 | "ZEND_JMPZNZ", 21 | "ZEND_JMPZ", 22 | "ZEND_JMPNZ", 23 | "ZEND_JMPZ_EX", 24 | "ZEND_JMPNZ_EX", 25 | "ZEND_JMP_SET", 26 | "ZEND_COALESCE", 27 | "ZEND_ASSERT_CHECK", 28 | "ZEND_CATCH", 29 | "ZEND_DECLARE_ANON_CLASS", 30 | "ZEND_DECLARE_ANON_INHERITED_CLASS", 31 | "ZEND_FE_FETCH_R", 32 | "ZEND_FE_FETCH_RW", 33 | "ZEND_FE_RESET_R", 34 | "ZEND_FE_RESET_RW", 35 | "ZEND_SWITCH_LONG", 36 | "ZEND_SWITCH_STRING", 37 | ] 38 | 39 | 40 | def _is_branch_instruction(opcode_name: str): 41 | if opcode_name in BRANCH_OPCODE_NAMES: 42 | return True 43 | return False 44 | 45 | 46 | def _instruction_is_global_fetch(opcode_flags, extended_value): 47 | ZEND_VM_EXT_VAR_FETCH = 0x00010000 48 | 49 | # global/local fetches 50 | ZEND_FETCH_GLOBAL = 1 << 1 51 | ZEND_FETCH_LOCAL = 1 << 2 52 | ZEND_FETCH_GLOBAL_LOCK = 1 << 3 53 | 54 | if ZEND_VM_EXT_VAR_FETCH & opcode_flags: 55 | # (global) 56 | if extended_value & ZEND_FETCH_GLOBAL: 57 | return True 58 | 59 | # (local) 60 | if extended_value & ZEND_FETCH_LOCAL: 61 | return True 62 | 63 | # (global+lock) 64 | if extended_value & ZEND_FETCH_GLOBAL_LOCK: 65 | return True 66 | return False 67 | 68 | 69 | def _instruction_is_function_call(opcode_name): 70 | if opcode_name in FUNCTION_CALL_OPCODE_NAMES: 71 | return True 72 | return False 73 | 74 | 75 | def _variables_used_at_function_call_args(function, instruction_index): 76 | instruction = function.instructions[instruction_index] 77 | variables = [] 78 | instructions_to_extract = [] 79 | 80 | if instruction.opcode_name in FUNCTION_CALL_OPCODE_NAMES: 81 | current_instruction_index = instruction_index 82 | while current_instruction_index >= 0: 83 | insn = function.instructions[current_instruction_index] 84 | # print("[***]", insn.opcode_name) 85 | 86 | # break if function start opcode is reached 87 | if insn.opcode_name in ["ZEND_INIT_FCALL_BY_NAME", "ZEND_INIT_FCALL"]: 88 | break 89 | 90 | # retrieve instruction if function argument is sent to call 91 | if insn.opcode_name in [ 92 | "ZEND_SEND_VAL", 93 | "ZEND_SEND_FUNC_ARG", 94 | "ZEND_SEND_VAR_EX", 95 | "ZEND_SEND_VAR", 96 | ]: 97 | # print("[yes]", insn.opcode_name) 98 | instructions_to_extract.append(insn) 99 | 100 | current_instruction_index = current_instruction_index - 1 101 | 102 | for insn in instructions_to_extract: 103 | for arg in args(insn): 104 | if arg.variable_number is not None and arg.variable_number >= 0: 105 | if arg.type != "IS_UNUSED" and arg.variable_number not in variables: 106 | variables.append(arg.variable_number) 107 | return variables 108 | 109 | 110 | def _function_call_args(function, instruction_index): 111 | instruction = function.instructions[instruction_index] 112 | instructions_to_extract = [] 113 | 114 | if instruction.opcode_name in FUNCTION_CALL_OPCODE_NAMES: 115 | current_instruction_index = instruction_index 116 | while current_instruction_index >= 0: 117 | insn = function.instructions[current_instruction_index] 118 | # print("[***]", insn.opcode_name) 119 | 120 | # break if function start opcode is reached 121 | if insn.opcode_name in ["ZEND_INIT_FCALL_BY_NAME", "ZEND_INIT_FCALL"]: 122 | break 123 | 124 | # retrieve instruction if function argument is sent to call 125 | if insn.opcode_name in [ 126 | "ZEND_SEND_VAL", 127 | "ZEND_SEND_FUNC_ARG", 128 | "ZEND_SEND_VAR_EX", 129 | "ZEND_SEND_VAR", 130 | ]: 131 | # print("[yes]", insn.opcode_name) 132 | instructions_to_extract.append(insn) 133 | 134 | current_instruction_index = current_instruction_index - 1 135 | 136 | function_args = [] 137 | for insn in instructions_to_extract: 138 | for arg in args(insn): 139 | if ( 140 | arg.variable_number is not None 141 | and arg.variable_number >= 0 142 | and arg.type != "IS_UNUSED" 143 | ): 144 | function_args.append(arg) 145 | 146 | return function_args 147 | 148 | 149 | def _variables_used_at_instruction( 150 | function, instruction_index, is_function_call_instruction=False 151 | ): 152 | variables = [] 153 | if is_function_call_instruction: 154 | variables = _variables_used_at_function_call_args(function, instruction_index) 155 | else: 156 | for var in function.ssa.ssa_variables: 157 | # use_chain is an instruction' index 158 | if var.use_chain >= 0: 159 | if var.use_chain == instruction_index: 160 | variables.append(var.var_num) 161 | return variables 162 | 163 | 164 | def _ssa_variables_used_at_instruction(function, instruction_index): 165 | ssa_variables = [] 166 | for var in function.ssa.ssa_variables: 167 | # use_chain is an instruction' index 168 | if var.use_chain >= 0: 169 | if var.use_chain == instruction_index: 170 | ssa_variables.append(var.ssa_var_num) 171 | return ssa_variables 172 | 173 | 174 | def _get_block_instructions(block): 175 | instructions_start_index = block.start 176 | instructions_end_index = block.start + block.len 177 | block_instructions = list(range(instructions_start_index, instructions_end_index)) 178 | return block_instructions 179 | -------------------------------------------------------------------------------- /src/php_bytecode_api/php_bytecode_api/models.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel 2 | from typing import List, Optional, Union 3 | from php_bytecode_api import model_utils 4 | 5 | 6 | class LiteralModel(BaseModel): 7 | type: str 8 | value: str 9 | 10 | 11 | class ExtraModel(BaseModel): 12 | arg_flags: Optional[List[int]] 13 | cache_size: int 14 | last_literal: int 15 | last_live_range: int 16 | last_try_catch: int 17 | number_of_cv_variables: int 18 | number_of_tmp_variables: int 19 | type: Optional[int] 20 | literals: List[LiteralModel] 21 | 22 | 23 | class OperandModel(BaseModel): 24 | type: Optional[str] = "IS_UNUSED" 25 | value: Union[str, None] 26 | variable_number: Optional[int] = 0 27 | 28 | 29 | class VariableModel(BaseModel): 30 | var_name: Union[str, None] 31 | var_num: int 32 | 33 | 34 | class DFGBasicBlockModel(BaseModel): 35 | block_index: int 36 | var_def: List[VariableModel] 37 | var_in: List[VariableModel] 38 | var_out: List[VariableModel] 39 | var_tmp: List[VariableModel] 40 | var_use: List[VariableModel] 41 | 42 | 43 | class SSAPhiConstraintModel(BaseModel): 44 | range_range_min: Optional[int] = 0 45 | range_range_max: Optional[int] = 0 46 | range_range_underflow: Optional[int] = 0 47 | range_range_overflow: Optional[int] = 0 48 | range_min_var: Optional[int] = 0 49 | range_max_var: Optional[int] = 0 50 | range_min_ssa_var: Optional[int] = 0 51 | range_max_ssa_var: Optional[int] = 0 52 | 53 | 54 | class SSAPhiModel(BaseModel): 55 | constraint: Optional[SSAPhiConstraintModel] 56 | current_block_index: Optional[int] 57 | has_range_constraint: Optional[int] 58 | pi: Optional[int] 59 | sources: Optional[List[int]] = [] 60 | ssa_variable_index: Optional[int] 61 | variable: Optional[VariableModel] 62 | variable_index: Optional[int] 63 | visited: Optional[int] 64 | 65 | 66 | class SSABlockModel(BaseModel): 67 | block_index: int 68 | phis: List[SSAPhiModel] 69 | 70 | 71 | class SSAInstructionModel(BaseModel): 72 | op1_def: int 73 | op1_use: int 74 | op1_use_chain: int 75 | op2_def: int 76 | op2_use: int 77 | op2_use_chain: int 78 | result_def: int 79 | result_use: int 80 | res_use_chain: int 81 | 82 | 83 | class SSAVariableModel(BaseModel): 84 | definition: int 85 | definition_phi: SSAPhiModel 86 | escape_state: int 87 | no_val: int 88 | ssa_var_num: int 89 | strongly_connected_component: int 90 | strongly_connected_component_entry: int 91 | use_chain: int 92 | var_name: str 93 | var_num: int 94 | 95 | 96 | class SSAModel(BaseModel): 97 | number_of_sccs: int 98 | number_of_ssa_variables: int 99 | ssa_variables: List[SSAVariableModel] 100 | ssa_instructions: List[SSAInstructionModel] 101 | ssa_blocks: List[SSABlockModel] 102 | 103 | 104 | class CFGBasicBlockModel(BaseModel): 105 | """ 106 | default props 107 | """ 108 | 109 | children: int 110 | idom: int 111 | len: int 112 | level: int 113 | loop_header: int 114 | next_child: int 115 | predecessor_offset: int 116 | predecessors_count: int 117 | start: int 118 | successors: List[int] 119 | successors_count: int 120 | successors_storage: List[int] 121 | 122 | """ 123 | additional generated/updated props 124 | """ 125 | block_instructions: List[int] = [] 126 | block_edges: List[int] = [] 127 | 128 | def source_lines(self, fn): 129 | source_lines = [] 130 | for instruction_index in self.block_instructions: 131 | instruction = fn.updated_instructions[instruction_index] 132 | if instruction.source_line not in source_lines: 133 | source_lines.append(instruction.source_line) 134 | return source_lines 135 | 136 | def instruction_names(self, fn): 137 | _instructions_opcode_names = [] 138 | for instruction_index in self.block_instructions: 139 | instruction = fn.updated_instructions[instruction_index] 140 | _instructions_opcode_names.append(instruction.opcode_name) 141 | return _instructions_opcode_names 142 | 143 | 144 | class CFGModel(BaseModel): 145 | blocks: List[CFGBasicBlockModel] 146 | blocks_count: int 147 | edges_count: int 148 | predecessors: List[int] 149 | 150 | 151 | class InstructionModel(BaseModel): 152 | """ 153 | Default props 154 | """ 155 | 156 | lineno: int 157 | num: int 158 | op1: OperandModel 159 | op1_type: int 160 | op2: OperandModel 161 | op2_type: int 162 | opcode: int 163 | opcode_flags: int 164 | opcode_name: str 165 | result: OperandModel 166 | result_type: int 167 | extended_value: int 168 | 169 | """ 170 | Additional generated/updated props 171 | """ 172 | source_line: Optional[str] 173 | basicblock_index: Optional[int] 174 | is_branch_instruction: bool = False 175 | is_global_fetch_instruction: bool = False 176 | is_function_call_instruction: bool = False 177 | variables_used_at_function_call_args: List[int] = [] 178 | function_call_args: List[OperandModel] = [] 179 | variables_used_here: List[int] = [] 180 | ssa_variables_used_here: List[int] = [] 181 | 182 | 183 | class FunctionModel(BaseModel): 184 | class_name: str 185 | function_name: str 186 | num_args: int 187 | required_num_args: int 188 | number_of_instructions: int 189 | filename: str 190 | extra: ExtraModel 191 | instructions: List[InstructionModel] 192 | cfg: CFGModel 193 | dfg: List[DFGBasicBlockModel] 194 | ssa: SSAModel 195 | source_lines: List[str] = [] 196 | 197 | def update_all(self) -> "FunctionModel": 198 | for index, instruction in enumerate(self.instructions): 199 | if model_utils._is_branch_instruction(instruction.opcode_name): 200 | self.instructions[index].is_branch_instruction = True 201 | 202 | if model_utils._instruction_is_global_fetch( 203 | instruction.opcode_flags, instruction.extended_value 204 | ): 205 | self.instructions[index].is_global_fetch_instruction = True 206 | 207 | if model_utils._instruction_is_function_call(instruction.opcode_name): 208 | self.instructions[index].is_function_call_instruction = True 209 | self.instructions[ 210 | index 211 | ].variables_used_at_function_call_args = model_utils._variables_used_at_function_call_args( # noqa: E501 212 | function=self, instruction_index=index 213 | ) 214 | self.instructions[ 215 | index 216 | ].function_call_args = model_utils._function_call_args( 217 | function=self, instruction_index=index 218 | ) 219 | 220 | self.instructions[ 221 | index 222 | ].variables_used_here = model_utils._variables_used_at_instruction( 223 | function=self, 224 | instruction_index=index, 225 | is_function_call_instruction=self.instructions[ 226 | index 227 | ].is_function_call_instruction, 228 | ) 229 | 230 | self.instructions[ 231 | index 232 | ].ssa_variables_used_here = model_utils._ssa_variables_used_at_instruction( 233 | function=self, instruction_index=index 234 | ) 235 | 236 | try: 237 | self.instructions[index].source_line = self.source_lines[ 238 | self.instructions[index].lineno - 1 239 | ] 240 | except IndexError: 241 | # if file is empty, set to emtpy string 242 | if len(self.source_lines) == 0: 243 | self.instructions[index].source_line = "" 244 | else: 245 | self.instructions[index].source_line = self.source_lines[ 246 | len(self.source_lines) - 1 247 | ] 248 | 249 | for block_index, block in enumerate(self.cfg.blocks): 250 | block_instructions = model_utils._get_block_instructions(block) 251 | for instruction_index in block_instructions: 252 | self.instructions[instruction_index].basicblock_index = block_index 253 | 254 | self.cfg.blocks[block_index].block_instructions = block_instructions 255 | 256 | if block.successors_count == 1: 257 | self.cfg.blocks[block_index].block_edges.append( 258 | self.cfg.blocks[block.successors_storage[0]].start 259 | ) 260 | 261 | elif block.successors_count == 2: 262 | self.cfg.blocks[block_index].block_edges.append( 263 | self.cfg.blocks[block.successors_storage[0]].start 264 | ) 265 | self.cfg.blocks[block_index].block_edges.append( 266 | self.cfg.blocks[block.successors_storage[1]].start 267 | ) 268 | 269 | return self 270 | -------------------------------------------------------------------------------- /src/php_bytecode_api/requirements.txt: -------------------------------------------------------------------------------- 1 | typing_extensions==4.7.1 2 | pydantic==1.9.0 3 | -------------------------------------------------------------------------------- /src/php_bytecode_api/setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | from setuptools import find_packages, setup 3 | import os 4 | 5 | _THIS_DIR = os.path.dirname(os.path.realpath(__file__)) 6 | 7 | PYTHON_REQUIRES = ">=3.8" 8 | 9 | 10 | if int(setuptools.__version__.split(".", 1)[0]) < 18: 11 | raise AssertionError("setuptools >= 18 must be installed") 12 | 13 | 14 | def main(): 15 | with open(os.path.join(_THIS_DIR, "requirements.txt")) as f: 16 | requirements = f.readlines() 17 | 18 | setup( 19 | name="php_bytecode_api", 20 | version="0.0.1", 21 | author="finixbit", 22 | author_email="samuelasirifi1@gmail.com", 23 | description="A high-level Python API for `src/php_to_bytecode_converter`, intended for the conversion of PHP files into a defined PHP Bytecode Pydantic model. This includes additional functions to facilitate the analysis of PHP bytecode. ", # noqa: E501 24 | packages=find_packages(), 25 | include_package_data=True, 26 | install_requires=requirements, 27 | python_requires=PYTHON_REQUIRES, 28 | ) 29 | 30 | 31 | if __name__ == "__main__": 32 | main() 33 | -------------------------------------------------------------------------------- /src/php_to_bytecode_converter/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | import os 3 | 4 | 5 | def get_php_include_dirs(): 6 | dirs = [] 7 | try: 8 | dirs = os.popen("php-config --includes").readline().strip().split(" ") 9 | except Exception as e: 10 | print("Error getting php include directories") 11 | raise e 12 | return [d[2:] for d in dirs] 13 | 14 | 15 | phpmodule = Extension( 16 | "php_to_bytecode_converter", 17 | define_macros=[("MAJOR_VERSION", "1"), ("MINOR_VERSION", "0")], 18 | include_dirs=get_php_include_dirs() 19 | + ["/app/src/php_to_bytecode_converter/src/includes"], 20 | libraries=["php7"], 21 | library_dirs=["/usr/local/lib"], 22 | sources=[ 23 | "src/main.c", 24 | "src/libs/php_embed_ex.c", 25 | "src/libs/php_cfg.c", 26 | "src/libs/php_dfg.c", 27 | "src/libs/php_ssa.c", 28 | ], 29 | extra_compile_args=["-Wmaybe-uninitialized", "-Wreturn-type", "-ggdb"], 30 | ) 31 | 32 | 33 | def main(): 34 | setup( 35 | name="php_to_bytecode_converter", 36 | version="0.0.1", 37 | description="A Python module is written in `C` to convert PHP source files to Zend bytecode (op_arrays), CFG, DFG, and SSA Python objects.", # noqa: E501 38 | author="finixbit", 39 | author_email="samuelasirifi1@gmail.com", 40 | ext_modules=[phpmodule], 41 | ) 42 | 43 | 44 | if __name__ == "__main__": 45 | main() 46 | -------------------------------------------------------------------------------- /src/php_to_bytecode_converter/src/includes/php_cfg.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Zend Engine, CFG - Control Flow Graph | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1998-2018 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Authors: Dmitry Stogov | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifndef ZEND_CFG_EX_H 20 | #define ZEND_CFG_EX_H 21 | 22 | #include 23 | #include "php.h" 24 | #include "zend_compile.h" 25 | #include "zend_worklist_ex.h" 26 | 27 | /* zend_basic_bloc.flags */ 28 | #define ZEND_BB_START (1<<0) /* fist block */ 29 | #define ZEND_BB_FOLLOW (1<<1) /* follows the next block */ 30 | #define ZEND_BB_TARGET (1<<2) /* jump taget */ 31 | #define ZEND_BB_EXIT (1<<3) /* without successors */ 32 | #define ZEND_BB_ENTRY (1<<4) /* stackless entry */ 33 | #define ZEND_BB_TRY (1<<5) /* start of try block */ 34 | #define ZEND_BB_CATCH (1<<6) /* start of catch block */ 35 | #define ZEND_BB_FINALLY (1<<7) /* start of finally block */ 36 | #define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */ 37 | #define ZEND_BB_GEN_VAR (1<<9) /* start of live range */ 38 | #define ZEND_BB_KILL_VAR (1<<10) /* end of live range */ 39 | #define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */ 40 | #define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */ 41 | 42 | #define ZEND_BB_LOOP_HEADER (1<<16) 43 | #define ZEND_BB_IRREDUCIBLE_LOOP (1<<17) 44 | 45 | #define ZEND_BB_REACHABLE (1<<31) 46 | 47 | #define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_GEN_VAR|ZEND_BB_KILL_VAR) 48 | 49 | 50 | /* func flags */ 51 | #define ZEND_FUNC_INDIRECT_VAR_ACCESS (1<<0) /* accesses variables by name */ 52 | #define ZEND_FUNC_HAS_CALLS (1<<1) 53 | #define ZEND_FUNC_VARARG (1<<2) /* uses func_get_args() */ 54 | #define ZEND_FUNC_NO_LOOPS (1<<3) 55 | #define ZEND_FUNC_IRREDUCIBLE (1<<4) 56 | #define ZEND_FUNC_RECURSIVE (1<<7) 57 | #define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8) 58 | #define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9) 59 | #define ZEND_FUNC_HAS_EXTENDED_INFO (1<<10) 60 | 61 | 62 | typedef struct _zend_basic_block { 63 | int *successors; /* successor block indices */ 64 | uint32_t flags; 65 | uint32_t start; /* first opcode number */ 66 | uint32_t len; /* number of opcodes */ 67 | int successors_count; /* number of successors */ 68 | int predecessors_count; /* number of predecessors */ 69 | int predecessor_offset; /* offset of 1-st predecessor */ 70 | int idom; /* immediate dominator block */ 71 | int loop_header; /* closest loop header, or -1 */ 72 | int level; /* steps away from the entry in the dom. tree */ 73 | int children; /* list of dominated blocks */ 74 | int next_child; /* next dominated block */ 75 | int successors_storage[2]; /* up to 2 successor blocks */ 76 | } zend_basic_block; 77 | 78 | /* 79 | +------------+---+---+---+---+---+ 80 | | |OP1|OP2|EXT| 0 | 1 | 81 | +------------+---+---+---+---+---+ 82 | |JMP |ADR| | |OP1| - | 83 | |JMPZ | |ADR| |OP2|FOL| 84 | |JMPNZ | |ADR| |OP2|FOL| 85 | |JMPZNZ | |ADR|ADR|OP2|EXT| 86 | |JMPZ_EX | |ADR| |OP2|FOL| 87 | |JMPNZ_EX | |ADR| |OP2|FOL| 88 | |JMP_SET | |ADR| |OP2|FOL| 89 | |COALESCE | |ADR| |OP2|FOL| 90 | |ASSERT_CHK | |ADR| |OP2|FOL| 91 | |NEW | |ADR| |OP2|FOL| 92 | |DCL_ANON* |ADR| | |OP1|FOL| 93 | |FE_RESET_* | |ADR| |OP2|FOL| 94 | |FE_FETCH_* | | |ADR|EXT|FOL| 95 | |CATCH | | |ADR|EXT|FOL| 96 | |FAST_CALL |ADR| | |OP1|FOL| 97 | |FAST_RET | | | | - | - | 98 | |RETURN* | | | | - | - | 99 | |EXIT | | | | - | - | 100 | |THROW | | | | - | - | 101 | |* | | | |FOL| - | 102 | +------------+---+---+---+---+---+ 103 | */ 104 | 105 | typedef struct _zend_cfg { 106 | int blocks_count; /* number of basic blocks */ 107 | int edges_count; /* number of edges */ 108 | zend_basic_block *blocks; /* array of basic blocks */ 109 | int *predecessors; 110 | uint32_t *map; 111 | uint32_t flags; 112 | } zend_cfg; 113 | 114 | /* Build Flags */ 115 | #define ZEND_RT_CONSTANTS (1<<31) 116 | #define ZEND_CFG_STACKLESS (1<<30) 117 | #define ZEND_SSA_DEBUG_LIVENESS (1<<29) 118 | #define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28) 119 | #define ZEND_SSA_RC_INFERENCE (1<<27) 120 | #define ZEND_CFG_SPLIT_AT_LIVE_RANGES (1<<26) 121 | #define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25) 122 | #define ZEND_CFG_RECV_ENTRY (1<<24) 123 | #define ZEND_CALL_TREE (1<<23) 124 | #define ZEND_SSA_USE_CV_RESULTS (1<<22) 125 | 126 | #define CRT_CONSTANT_EX(op_array, opline, node, rt_constants) \ 127 | ((rt_constants) ? \ 128 | RT_CONSTANT(opline, (node)) \ 129 | : \ 130 | CT_CONSTANT_EX(op_array, (node).constant) \ 131 | ) 132 | 133 | #define CRT_CONSTANT(node) \ 134 | CRT_CONSTANT_EX(op_array, opline, node, (build_flags & ZEND_RT_CONSTANTS)) 135 | 136 | #define RETURN_VALUE_USED(opline) \ 137 | ((opline)->result_type != IS_UNUSED) 138 | 139 | BEGIN_EXTERN_C() 140 | 141 | int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg); 142 | int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg); 143 | int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg); 144 | int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg); 145 | 146 | END_EXTERN_C() 147 | 148 | #endif /* ZEND_CFG_EX_H */ 149 | 150 | /* 151 | * Local variables: 152 | * tab-width: 4 153 | * c-basic-offset: 4 154 | * indent-tabs-mode: t 155 | * End: 156 | */ 157 | -------------------------------------------------------------------------------- /src/php_to_bytecode_converter/src/includes/php_dfg.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Zend Engine, DFG - Data Flow Graph | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1998-2018 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Authors: Dmitry Stogov | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifndef ZEND_DFG_EX_H 20 | #define ZEND_DFG_EX_H 21 | 22 | #include 23 | #include "php.h" 24 | #include "zend_compile.h" 25 | #include "zend_worklist_ex.h" 26 | #include "zend_bitset.h" 27 | #include "php_cfg.h" 28 | 29 | typedef struct _zend_dfg { 30 | int vars; 31 | uint32_t size; 32 | zend_bitset tmp; 33 | zend_bitset def; 34 | zend_bitset use; 35 | zend_bitset in; 36 | zend_bitset out; 37 | } zend_dfg; 38 | 39 | #define DFG_BITSET(set, set_size, block_num) \ 40 | ((set) + ((block_num) * (set_size))) 41 | 42 | #define DFG_SET(set, set_size, block_num, var_num) \ 43 | zend_bitset_incl(DFG_BITSET(set, set_size, block_num), (var_num)) 44 | 45 | #define DFG_ISSET(set, set_size, block_num, var_num) \ 46 | zend_bitset_in(DFG_BITSET(set, set_size, block_num), (var_num)) 47 | 48 | BEGIN_EXTERN_C() 49 | 50 | int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags); 51 | 52 | END_EXTERN_C() 53 | 54 | #endif /* ZEND_DFG_EX_H */ 55 | 56 | /* 57 | * Local variables: 58 | * tab-width: 4 59 | * c-basic-offset: 4 60 | * indent-tabs-mode: t 61 | * End: 62 | */ 63 | -------------------------------------------------------------------------------- /src/php_to_bytecode_converter/src/includes/php_embed_ex.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2018 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Edin Kadribasic | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifndef _PHP_EMBED_EX_H_ 20 | #define _PHP_EMBED_EX_H_ 21 | 22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include 28 | 29 | #define PHP_EMBED_START_BLOCK(x,y) { \ 30 | php_embed_init_ex(x, y); \ 31 | zend_first_try { 32 | 33 | #define PHP_EMBED_END_BLOCK() \ 34 | } zend_catch { \ 35 | /* int exit_status = EG(exit_status); */ \ 36 | } zend_end_try(); \ 37 | php_embed_shutdown_ex(); \ 38 | } 39 | 40 | #ifndef PHP_WIN32 41 | #define EMBED_SAPI_API SAPI_API 42 | #else 43 | #define EMBED_SAPI_API 44 | #endif 45 | 46 | #ifdef ZTS 47 | ZEND_TSRMLS_CACHE_EXTERN() 48 | #endif 49 | 50 | BEGIN_EXTERN_C() 51 | EMBED_SAPI_API int php_embed_init_ex(int argc, char **argv); 52 | EMBED_SAPI_API void php_embed_shutdown_ex(void); 53 | extern EMBED_SAPI_API sapi_module_struct php_embed_module; 54 | END_EXTERN_C() 55 | 56 | 57 | #endif /* _PHP_EMBED_H_ */ 58 | -------------------------------------------------------------------------------- /src/php_to_bytecode_converter/src/includes/php_ssa.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Zend Engine, SSA - Static Single Assignment Form | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1998-2018 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Authors: Dmitry Stogov | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifndef ZEND_SSA_EX_H 20 | #define ZEND_SSA_EX_H 21 | 22 | #include 23 | #include "php.h" 24 | #include "zend_compile.h" 25 | #include "zend_worklist_ex.h" 26 | #include "php_cfg.h" 27 | 28 | #define MAY_BE_UNDEF (1 << IS_UNDEF) 29 | #define MAY_BE_NULL (1 << IS_NULL) 30 | #define MAY_BE_FALSE (1 << IS_FALSE) 31 | #define MAY_BE_TRUE (1 << IS_TRUE) 32 | #define MAY_BE_LONG (1 << IS_LONG) 33 | #define MAY_BE_DOUBLE (1 << IS_DOUBLE) 34 | #define MAY_BE_STRING (1 << IS_STRING) 35 | #define MAY_BE_ARRAY (1 << IS_ARRAY) 36 | #define MAY_BE_OBJECT (1 << IS_OBJECT) 37 | #define MAY_BE_RESOURCE (1 << IS_RESOURCE) 38 | #define MAY_BE_ANY (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE) 39 | #define MAY_BE_REF (1 << IS_REFERENCE) /* may be reference */ 40 | 41 | #define MAY_BE_ARRAY_SHIFT (IS_REFERENCE) 42 | 43 | #define MAY_BE_ARRAY_OF_NULL (MAY_BE_NULL << MAY_BE_ARRAY_SHIFT) 44 | #define MAY_BE_ARRAY_OF_FALSE (MAY_BE_FALSE << MAY_BE_ARRAY_SHIFT) 45 | #define MAY_BE_ARRAY_OF_TRUE (MAY_BE_TRUE << MAY_BE_ARRAY_SHIFT) 46 | #define MAY_BE_ARRAY_OF_LONG (MAY_BE_LONG << MAY_BE_ARRAY_SHIFT) 47 | #define MAY_BE_ARRAY_OF_DOUBLE (MAY_BE_DOUBLE << MAY_BE_ARRAY_SHIFT) 48 | #define MAY_BE_ARRAY_OF_STRING (MAY_BE_STRING << MAY_BE_ARRAY_SHIFT) 49 | #define MAY_BE_ARRAY_OF_ARRAY (MAY_BE_ARRAY << MAY_BE_ARRAY_SHIFT) 50 | #define MAY_BE_ARRAY_OF_OBJECT (MAY_BE_OBJECT << MAY_BE_ARRAY_SHIFT) 51 | #define MAY_BE_ARRAY_OF_RESOURCE (MAY_BE_RESOURCE << MAY_BE_ARRAY_SHIFT) 52 | #define MAY_BE_ARRAY_OF_ANY (MAY_BE_ANY << MAY_BE_ARRAY_SHIFT) 53 | #define MAY_BE_ARRAY_OF_REF (MAY_BE_REF << MAY_BE_ARRAY_SHIFT) 54 | 55 | #define MAY_BE_ARRAY_KEY_LONG (1<<21) 56 | #define MAY_BE_ARRAY_KEY_STRING (1<<22) 57 | #define MAY_BE_ARRAY_KEY_ANY (MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_KEY_STRING) 58 | 59 | #define MAY_BE_ERROR (1<<23) 60 | #define MAY_BE_CLASS (1<<24) 61 | 62 | //TODO: remome MAY_BE_RC1, MAY_BE_RCN??? 63 | #define MAY_BE_RC1 (1<<27) /* may be non-reference with refcount == 1 */ 64 | #define MAY_BE_RCN (1<<28) /* may be non-reference with refcount > 1 */ 65 | 66 | #define MAY_BE_IN_REG (1<<25) /* value allocated in CPU register */ 67 | 68 | typedef struct _zend_script { 69 | zend_string *filename; 70 | zend_op_array main_op_array; 71 | HashTable function_table; 72 | HashTable class_table; 73 | uint32_t first_early_binding_opline; /* the linked list of delayed declarations */ 74 | } zend_script; 75 | 76 | typedef struct _zend_optimizer_ctx { 77 | zend_arena *arena; 78 | zend_script *script; 79 | HashTable *constants; 80 | zend_long optimization_level; 81 | zend_long debug_level; 82 | } zend_optimizer_ctx; 83 | 84 | typedef struct _zend_ssa_range { 85 | zend_long min; 86 | zend_long max; 87 | zend_bool underflow; 88 | zend_bool overflow; 89 | } zend_ssa_range; 90 | 91 | typedef enum _zend_ssa_negative_lat { 92 | NEG_NONE = 0, 93 | NEG_INIT = 1, 94 | NEG_INVARIANT = 2, 95 | NEG_USE_LT = 3, 96 | NEG_USE_GT = 4, 97 | NEG_UNKNOWN = 5 98 | } zend_ssa_negative_lat; 99 | 100 | /* Special kind of SSA Phi function used in eSSA */ 101 | typedef struct _zend_ssa_range_constraint { 102 | zend_ssa_range range; /* simple range constraint */ 103 | int min_var; 104 | int max_var; 105 | int min_ssa_var; /* ((min_var>0) ? MIN(ssa_var) : 0) + range.min */ 106 | int max_ssa_var; /* ((max_var>0) ? MAX(ssa_var) : 0) + range.max */ 107 | zend_ssa_negative_lat negative; 108 | } zend_ssa_range_constraint; 109 | 110 | typedef struct _zend_ssa_type_constraint { 111 | uint32_t type_mask; /* Type mask to intersect with */ 112 | zend_class_entry *ce; /* Class entry for instanceof constraints */ 113 | } zend_ssa_type_constraint; 114 | 115 | typedef union _zend_ssa_pi_constraint { 116 | zend_ssa_range_constraint range; 117 | zend_ssa_type_constraint type; 118 | } zend_ssa_pi_constraint; 119 | 120 | /* SSA Phi - ssa_var = Phi(source0, source1, ...sourceN) */ 121 | typedef struct _zend_ssa_phi zend_ssa_phi; 122 | struct _zend_ssa_phi { 123 | zend_ssa_phi *next; /* next Phi in the same BB */ 124 | int pi; /* if >= 0 this is actually a e-SSA Pi */ 125 | zend_ssa_pi_constraint constraint; /* e-SSA Pi constraint */ 126 | int var; /* Original CV, VAR or TMP variable index */ 127 | int ssa_var; /* SSA variable index */ 128 | int block; /* current BB index */ 129 | int visited : 1; /* flag to avoid recursive processing */ 130 | int has_range_constraint : 1; 131 | zend_ssa_phi **use_chains; 132 | zend_ssa_phi *sym_use_chain; 133 | int *sources; /* Array of SSA IDs that produce this var. 134 | As many as this block has 135 | predecessors. */ 136 | }; 137 | 138 | typedef struct _zend_ssa_block { 139 | zend_ssa_phi *phis; 140 | } zend_ssa_block; 141 | 142 | typedef struct _zend_ssa_op { 143 | int op1_use; 144 | int op2_use; 145 | int result_use; 146 | int op1_def; 147 | int op2_def; 148 | int result_def; 149 | int op1_use_chain; 150 | int op2_use_chain; 151 | int res_use_chain; 152 | } zend_ssa_op; 153 | 154 | typedef enum _zend_ssa_alias_kind { 155 | NO_ALIAS, 156 | SYMTABLE_ALIAS, 157 | PHP_ERRORMSG_ALIAS, 158 | HTTP_RESPONSE_HEADER_ALIAS 159 | } zend_ssa_alias_kind; 160 | 161 | typedef enum _zend_ssa_escape_state { 162 | ESCAPE_STATE_UNKNOWN, 163 | ESCAPE_STATE_NO_ESCAPE, 164 | ESCAPE_STATE_FUNCTION_ESCAPE, 165 | ESCAPE_STATE_GLOBAL_ESCAPE 166 | } zend_ssa_escape_state; 167 | 168 | typedef struct _zend_ssa_var { 169 | int var; /* original var number; op.var for CVs and following numbers for VARs and TMP_VARs */ 170 | int scc; /* strongly connected component */ 171 | int definition; /* opcode that defines this value */ 172 | zend_ssa_phi *definition_phi; /* phi that defines this value */ 173 | int use_chain; /* uses of this value, linked through opN_use_chain */ 174 | zend_ssa_phi *phi_use_chain; /* uses of this value in Phi, linked through use_chain */ 175 | zend_ssa_phi *sym_use_chain; /* uses of this value in Pi constraints */ 176 | unsigned int no_val : 1; /* value doesn't mater (used as op1 in ZEND_ASSIGN) */ 177 | unsigned int scc_entry : 1; 178 | unsigned int alias : 2; /* value may be changed indirectly */ 179 | unsigned int escape_state : 2; 180 | } zend_ssa_var; 181 | 182 | typedef struct _zend_ssa_var_info { 183 | uint32_t type; /* inferred type (see zend_inference.h) */ 184 | zend_ssa_range range; 185 | zend_class_entry *ce; 186 | unsigned int has_range : 1; 187 | unsigned int is_instanceof : 1; /* 0 - class == "ce", 1 - may be child of "ce" */ 188 | unsigned int recursive : 1; 189 | unsigned int use_as_double : 1; 190 | } zend_ssa_var_info; 191 | 192 | typedef struct _zend_ssa { 193 | zend_cfg cfg; /* control flow graph */ 194 | int rt_constants; /* run-time or compile-time */ 195 | int vars_count; /* number of SSA variables */ 196 | zend_ssa_block *blocks; /* array of SSA blocks */ 197 | zend_ssa_op *ops; /* array of SSA instructions */ 198 | zend_ssa_var *vars; /* use/def chain of SSA variables */ 199 | int sccs; /* number of SCCs */ 200 | zend_ssa_var_info *var_info; 201 | } zend_ssa; 202 | 203 | BEGIN_EXTERN_C() 204 | 205 | int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa); 206 | int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa); 207 | 208 | 209 | #define NUM_PHI_SOURCES(phi) \ 210 | ((phi)->pi >= 0 ? 1 : (ssa->cfg.blocks[(phi)->block].predecessors_count)) 211 | 212 | /* FOREACH_USE and FOREACH_PHI_USE explicitly support "continue" 213 | * and changing the use chain of the current element */ 214 | #define FOREACH_USE(var, use) do { \ 215 | int _var_num = (var) - ssa->vars, next; \ 216 | for (use = (var)->use_chain; use >= 0; use = next) { \ 217 | next = zend_ssa_next_use(ssa->ops, _var_num, use); 218 | #define FOREACH_USE_END() \ 219 | } \ 220 | } while (0) 221 | 222 | #define FOREACH_PHI_USE(var, phi) do { \ 223 | int _var_num = (var) - ssa->vars; \ 224 | zend_ssa_phi *next_phi; \ 225 | for (phi = (var)->phi_use_chain; phi; phi = next_phi) { \ 226 | next_phi = zend_ssa_next_use_phi(ssa, _var_num, phi); 227 | #define FOREACH_PHI_USE_END() \ 228 | } \ 229 | } while (0) 230 | 231 | #define FOREACH_PHI_SOURCE(phi, source) do { \ 232 | zend_ssa_phi *_phi = (phi); \ 233 | int _i, _end = NUM_PHI_SOURCES(phi); \ 234 | for (_i = 0; _i < _end; _i++) { \ 235 | ZEND_ASSERT(_phi->sources[_i] >= 0); \ 236 | source = _phi->sources[_i]; 237 | #define FOREACH_PHI_SOURCE_END() \ 238 | } \ 239 | } while (0) 240 | 241 | #define FOREACH_PHI(phi) do { \ 242 | int _i; \ 243 | for (_i = 0; _i < ssa->cfg.blocks_count; _i++) { \ 244 | phi = ssa->blocks[_i].phis; \ 245 | for (; phi; phi = phi->next) { 246 | #define FOREACH_PHI_END() \ 247 | } \ 248 | } \ 249 | } while (0) 250 | 251 | #define FOREACH_BLOCK(block) do { \ 252 | int _i; \ 253 | for (_i = 0; _i < ssa->cfg.blocks_count; _i++) { \ 254 | (block) = &ssa->cfg.blocks[_i]; \ 255 | if (!((block)->flags & ZEND_BB_REACHABLE)) { \ 256 | continue; \ 257 | } 258 | #define FOREACH_BLOCK_END() \ 259 | } \ 260 | } while (0) 261 | 262 | /* Does not support "break" */ 263 | #define FOREACH_INSTR_NUM(i) do { \ 264 | zend_basic_block *_block; \ 265 | FOREACH_BLOCK(_block) { \ 266 | uint32_t _end = _block->start + _block->len; \ 267 | for ((i) = _block->start; (i) < _end; (i)++) { 268 | #define FOREACH_INSTR_NUM_END() \ 269 | } \ 270 | } FOREACH_BLOCK_END(); \ 271 | } while (0) 272 | 273 | #endif /* ZEND_SSA_H */ 274 | 275 | /* 276 | * Local variables: 277 | * tab-width: 4 278 | * c-basic-offset: 4 279 | * indent-tabs-mode: t 280 | * End: 281 | */ 282 | -------------------------------------------------------------------------------- /src/php_to_bytecode_converter/src/includes/zend_worklist_ex.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Zend Engine | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1998-2018 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Authors: Andy Wingo | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifndef _ZEND_WORKLIST_H_ 20 | #define _ZEND_WORKLIST_H_ 21 | 22 | #include "zend_arena.h" 23 | #include "zend_bitset.h" 24 | 25 | typedef struct _zend_worklist_stack { 26 | int *buf; 27 | int len; 28 | int capacity; 29 | } zend_worklist_stack; 30 | 31 | #define ZEND_WORKLIST_STACK_ALLOCA(s, _len, use_heap) do { \ 32 | (s)->buf = (int*)do_alloca(sizeof(int) * _len, use_heap); \ 33 | (s)->len = 0; \ 34 | (s)->capacity = _len; \ 35 | } while (0) 36 | 37 | #define ZEND_WORKLIST_STACK_FREE_ALLOCA(s, use_heap) \ 38 | free_alloca((s)->buf, use_heap) 39 | 40 | static inline int zend_worklist_stack_prepare(zend_arena **arena, zend_worklist_stack *stack, int len) 41 | { 42 | ZEND_ASSERT(len >= 0); 43 | 44 | stack->buf = (int*)zend_arena_calloc(arena, sizeof(*stack->buf), len); 45 | stack->len = 0; 46 | stack->capacity = len; 47 | 48 | return SUCCESS; 49 | } 50 | 51 | static inline void zend_worklist_stack_push(zend_worklist_stack *stack, int i) 52 | { 53 | ZEND_ASSERT(stack->len < stack->capacity); 54 | stack->buf[stack->len++] = i; 55 | } 56 | 57 | static inline int zend_worklist_stack_peek(zend_worklist_stack *stack) 58 | { 59 | ZEND_ASSERT(stack->len); 60 | return stack->buf[stack->len - 1]; 61 | } 62 | 63 | static inline int zend_worklist_stack_pop(zend_worklist_stack *stack) 64 | { 65 | ZEND_ASSERT(stack->len); 66 | return stack->buf[--stack->len]; 67 | } 68 | 69 | typedef struct _zend_worklist { 70 | zend_bitset visited; 71 | zend_worklist_stack stack; 72 | } zend_worklist; 73 | 74 | #define ZEND_WORKLIST_ALLOCA(w, _len, use_heap) do { \ 75 | (w)->stack.buf = (int*)do_alloca(ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len) + sizeof(zend_ulong) * zend_bitset_len(_len), use_heap); \ 76 | (w)->stack.len = 0; \ 77 | (w)->stack.capacity = _len; \ 78 | (w)->visited = (zend_bitset)((char*)(w)->stack.buf + ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len)); \ 79 | memset((w)->visited, 0, sizeof(zend_ulong) * zend_bitset_len(_len)); \ 80 | } while (0) 81 | 82 | #define ZEND_WORKLIST_FREE_ALLOCA(w, use_heap) \ 83 | free_alloca((w)->stack.buf, use_heap) 84 | 85 | static inline int zend_worklist_prepare(zend_arena **arena, zend_worklist *worklist, int len) 86 | { 87 | ZEND_ASSERT(len >= 0); 88 | worklist->visited = (zend_bitset)zend_arena_calloc(arena, sizeof(zend_ulong), zend_bitset_len(len)); 89 | return zend_worklist_stack_prepare(arena, &worklist->stack, len); 90 | } 91 | 92 | static inline int zend_worklist_len(zend_worklist *worklist) 93 | { 94 | return worklist->stack.len; 95 | } 96 | 97 | static inline int zend_worklist_push(zend_worklist *worklist, int i) 98 | { 99 | ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity); 100 | 101 | if (zend_bitset_in(worklist->visited, i)) { 102 | return 0; 103 | } 104 | 105 | zend_bitset_incl(worklist->visited, i); 106 | zend_worklist_stack_push(&worklist->stack, i); 107 | return 1; 108 | } 109 | 110 | static inline int zend_worklist_peek(zend_worklist *worklist) 111 | { 112 | return zend_worklist_stack_peek(&worklist->stack); 113 | } 114 | 115 | static inline int zend_worklist_pop(zend_worklist *worklist) 116 | { 117 | /* Does not clear visited flag */ 118 | return zend_worklist_stack_pop(&worklist->stack); 119 | } 120 | 121 | #endif /* _ZEND_WORKLIST_H_ */ 122 | 123 | /* 124 | * Local variables: 125 | * tab-width: 4 126 | * c-basic-offset: 4 127 | * indent-tabs-mode: t 128 | * End: 129 | */ 130 | -------------------------------------------------------------------------------- /src/php_to_bytecode_converter/src/libs/php_dfg.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Zend Engine, DFG - Data Flow Graph | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1998-2018 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Authors: Dmitry Stogov | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #include "php_dfg.h" 20 | #include "php.h" 21 | #include "zend_compile.h" 22 | 23 | int 24 | zend_build_dfg (const zend_op_array *op_array, const zend_cfg *cfg, 25 | zend_dfg *dfg, uint32_t build_flags) /* {{{ */ 26 | { 27 | int set_size; 28 | zend_basic_block *blocks = cfg->blocks; 29 | int blocks_count = cfg->blocks_count; 30 | zend_bitset tmp, def, use, in, out; 31 | int k; 32 | uint32_t var_num; 33 | int j; 34 | 35 | set_size = dfg->size; 36 | tmp = dfg->tmp; 37 | def = dfg->def; 38 | use = dfg->use; 39 | in = dfg->in; 40 | out = dfg->out; 41 | 42 | /* Collect "def" and "use" sets */ 43 | for (j = 0; j < blocks_count; j++) 44 | { 45 | zend_op *opline, *end; 46 | if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) 47 | { 48 | continue; 49 | } 50 | 51 | opline = op_array->opcodes + blocks[j].start; 52 | end = opline + blocks[j].len; 53 | for (; opline < end; opline++) 54 | { 55 | if (opline->opcode != ZEND_OP_DATA) 56 | { 57 | zend_op *next = opline + 1; 58 | if (next < end && next->opcode == ZEND_OP_DATA) 59 | { 60 | if (next->op1_type 61 | & (IS_CV | IS_VAR | IS_TMP_VAR)) 62 | { 63 | var_num 64 | = EX_VAR_TO_NUM (next->op1.var); 65 | if (!DFG_ISSET (def, set_size, j, 66 | var_num)) 67 | { 68 | DFG_SET (use, set_size, j, 69 | var_num); 70 | } 71 | } 72 | if (next->op2_type 73 | & (IS_CV | IS_VAR | IS_TMP_VAR)) 74 | { 75 | var_num 76 | = EX_VAR_TO_NUM (next->op2.var); 77 | if (!DFG_ISSET (def, set_size, j, 78 | var_num)) 79 | { 80 | DFG_SET (use, set_size, j, 81 | var_num); 82 | } 83 | } 84 | } 85 | if (opline->op1_type == IS_CV) 86 | { 87 | var_num = EX_VAR_TO_NUM (opline->op1.var); 88 | switch (opline->opcode) 89 | { 90 | case ZEND_ADD_ARRAY_ELEMENT: 91 | case ZEND_INIT_ARRAY: 92 | if ((build_flags 93 | & ZEND_SSA_RC_INFERENCE) 94 | || (opline->extended_value 95 | & ZEND_ARRAY_ELEMENT_REF)) 96 | { 97 | goto op1_def; 98 | } 99 | goto op1_use; 100 | case ZEND_FE_RESET_R: 101 | case ZEND_SEND_VAR: 102 | case ZEND_CAST: 103 | case ZEND_QM_ASSIGN: 104 | case ZEND_JMP_SET: 105 | case ZEND_COALESCE: 106 | if (build_flags 107 | & ZEND_SSA_RC_INFERENCE) 108 | { 109 | goto op1_def; 110 | } 111 | goto op1_use; 112 | case ZEND_YIELD: 113 | if ((build_flags 114 | & ZEND_SSA_RC_INFERENCE) 115 | || (op_array->fn_flags 116 | & ZEND_ACC_RETURN_REFERENCE)) 117 | { 118 | goto op1_def; 119 | } 120 | goto op1_use; 121 | case ZEND_UNSET_CV: 122 | case ZEND_ASSIGN: 123 | case ZEND_ASSIGN_REF: 124 | case ZEND_BIND_GLOBAL: 125 | case ZEND_BIND_STATIC: 126 | case ZEND_SEND_VAR_EX: 127 | case ZEND_SEND_FUNC_ARG: 128 | case ZEND_SEND_REF: 129 | case ZEND_SEND_VAR_NO_REF: 130 | case ZEND_SEND_VAR_NO_REF_EX: 131 | case ZEND_FE_RESET_RW: 132 | case ZEND_ASSIGN_ADD: 133 | case ZEND_ASSIGN_SUB: 134 | case ZEND_ASSIGN_MUL: 135 | case ZEND_ASSIGN_DIV: 136 | case ZEND_ASSIGN_MOD: 137 | case ZEND_ASSIGN_SL: 138 | case ZEND_ASSIGN_SR: 139 | case ZEND_ASSIGN_CONCAT: 140 | case ZEND_ASSIGN_BW_OR: 141 | case ZEND_ASSIGN_BW_AND: 142 | case ZEND_ASSIGN_BW_XOR: 143 | case ZEND_ASSIGN_POW: 144 | case ZEND_PRE_INC: 145 | case ZEND_PRE_DEC: 146 | case ZEND_POST_INC: 147 | case ZEND_POST_DEC: 148 | case ZEND_ASSIGN_DIM: 149 | case ZEND_ASSIGN_OBJ: 150 | case ZEND_UNSET_DIM: 151 | case ZEND_UNSET_OBJ: 152 | case ZEND_FETCH_DIM_W: 153 | case ZEND_FETCH_DIM_RW: 154 | case ZEND_FETCH_DIM_FUNC_ARG: 155 | case ZEND_FETCH_DIM_UNSET: 156 | case ZEND_FETCH_OBJ_W: 157 | case ZEND_FETCH_OBJ_RW: 158 | case ZEND_FETCH_OBJ_FUNC_ARG: 159 | case ZEND_FETCH_OBJ_UNSET: 160 | case ZEND_FETCH_LIST_W: 161 | case ZEND_VERIFY_RETURN_TYPE: 162 | case ZEND_PRE_INC_OBJ: 163 | case ZEND_PRE_DEC_OBJ: 164 | case ZEND_POST_INC_OBJ: 165 | case ZEND_POST_DEC_OBJ: 166 | op1_def: 167 | /* `def` always come along with dtor 168 | * or separation, thus the origin 169 | * var info might be also `use`d in 170 | * the feature(CG) */ 171 | DFG_SET (use, set_size, j, var_num); 172 | DFG_SET (def, set_size, j, var_num); 173 | break; 174 | default: 175 | op1_use: 176 | if (!DFG_ISSET (def, set_size, j, 177 | var_num)) 178 | { 179 | DFG_SET (use, set_size, j, 180 | var_num); 181 | } 182 | } 183 | } 184 | else if (opline->op1_type & (IS_VAR | IS_TMP_VAR)) 185 | { 186 | var_num = EX_VAR_TO_NUM (opline->op1.var); 187 | if (!DFG_ISSET (def, set_size, j, var_num)) 188 | { 189 | DFG_SET (use, set_size, j, var_num); 190 | } 191 | if (opline->opcode 192 | == ZEND_VERIFY_RETURN_TYPE) 193 | { 194 | DFG_SET (def, set_size, j, var_num); 195 | } 196 | } 197 | if (opline->op2_type == IS_CV) 198 | { 199 | var_num = EX_VAR_TO_NUM (opline->op2.var); 200 | switch (opline->opcode) 201 | { 202 | case ZEND_ASSIGN: 203 | if (build_flags 204 | & ZEND_SSA_RC_INFERENCE) 205 | { 206 | goto op2_def; 207 | } 208 | goto op2_use; 209 | case ZEND_BIND_LEXICAL: 210 | if ((build_flags 211 | & ZEND_SSA_RC_INFERENCE) 212 | || (opline->extended_value 213 | & ZEND_BIND_REF)) 214 | { 215 | goto op2_def; 216 | } 217 | goto op2_use; 218 | case ZEND_ASSIGN_REF: 219 | case ZEND_FE_FETCH_R: 220 | case ZEND_FE_FETCH_RW: 221 | op2_def: 222 | // FIXME: include into "use" too 223 | // ...? 224 | DFG_SET (use, set_size, j, var_num); 225 | DFG_SET (def, set_size, j, var_num); 226 | break; 227 | default: 228 | op2_use: 229 | if (!DFG_ISSET (def, set_size, j, 230 | var_num)) 231 | { 232 | DFG_SET (use, set_size, j, 233 | var_num); 234 | } 235 | break; 236 | } 237 | } 238 | else if (opline->op2_type & (IS_VAR | IS_TMP_VAR)) 239 | { 240 | var_num = EX_VAR_TO_NUM (opline->op2.var); 241 | if (opline->opcode == ZEND_FE_FETCH_R 242 | || opline->opcode == ZEND_FE_FETCH_RW) 243 | { 244 | DFG_SET (def, set_size, j, var_num); 245 | } 246 | else 247 | { 248 | if (!DFG_ISSET (def, set_size, j, 249 | var_num)) 250 | { 251 | DFG_SET (use, set_size, j, 252 | var_num); 253 | } 254 | } 255 | } 256 | if (opline->result_type 257 | & (IS_CV | IS_VAR | IS_TMP_VAR)) 258 | { 259 | var_num 260 | = EX_VAR_TO_NUM (opline->result.var); 261 | if ((build_flags & ZEND_SSA_USE_CV_RESULTS) 262 | && opline->result_type == IS_CV) 263 | { 264 | DFG_SET (use, set_size, j, var_num); 265 | } 266 | DFG_SET (def, set_size, j, var_num); 267 | } 268 | } 269 | } 270 | } 271 | 272 | /* Calculate "in" and "out" sets */ 273 | { 274 | uint32_t worklist_len = zend_bitset_len (blocks_count); 275 | zend_bitset worklist; 276 | ALLOCA_FLAG (use_heap); 277 | worklist = ZEND_BITSET_ALLOCA (worklist_len, use_heap); 278 | memset (worklist, 0, worklist_len * ZEND_BITSET_ELM_SIZE); 279 | for (j = 0; j < blocks_count; j++) 280 | { 281 | zend_bitset_incl (worklist, j); 282 | } 283 | while (!zend_bitset_empty (worklist, worklist_len)) 284 | { 285 | /* We use the last block on the worklist, because predecessors 286 | * tend to be located before the succeeding block, so this 287 | * converges faster. */ 288 | j = zend_bitset_last (worklist, worklist_len); 289 | zend_bitset_excl (worklist, j); 290 | 291 | if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) 292 | { 293 | continue; 294 | } 295 | if (blocks[j].successors_count != 0) 296 | { 297 | zend_bitset_copy ( 298 | DFG_BITSET (out, set_size, j), 299 | DFG_BITSET (in, set_size, blocks[j].successors[0]), 300 | set_size); 301 | for (k = 1; k < blocks[j].successors_count; k++) 302 | { 303 | zend_bitset_union ( 304 | DFG_BITSET (out, set_size, j), 305 | DFG_BITSET (in, set_size, 306 | blocks[j].successors[k]), 307 | set_size); 308 | } 309 | } 310 | else 311 | { 312 | zend_bitset_clear (DFG_BITSET (out, set_size, j), 313 | set_size); 314 | } 315 | zend_bitset_union_with_difference ( 316 | tmp, DFG_BITSET (use, set_size, j), 317 | DFG_BITSET (out, set_size, j), 318 | DFG_BITSET (def, set_size, j), set_size); 319 | if (!zend_bitset_equal (DFG_BITSET (in, set_size, j), tmp, 320 | set_size)) 321 | { 322 | zend_bitset_copy (DFG_BITSET (in, set_size, j), tmp, 323 | set_size); 324 | 325 | /* Add predecessors of changed block to worklist */ 326 | { 327 | int *predecessors 328 | = &cfg->predecessors[blocks[j] 329 | .predecessor_offset]; 330 | for (k = 0; k < blocks[j].predecessors_count; k++) 331 | { 332 | zend_bitset_incl (worklist, 333 | predecessors[k]); 334 | } 335 | } 336 | } 337 | } 338 | 339 | free_alloca (worklist, use_heap); 340 | } 341 | 342 | return SUCCESS; 343 | } 344 | /* }}} */ 345 | 346 | /* 347 | * Local variables: 348 | * tab-width: 4 349 | * c-basic-offset: 4 350 | * indent-tabs-mode: t 351 | * End: 352 | */ 353 | -------------------------------------------------------------------------------- /src/php_to_bytecode_converter/src/libs/php_embed_ex.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2018 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Edin Kadribasic | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #include "php_embed_ex.h" 20 | #include "ext/standard/php_standard.h" 21 | 22 | #ifdef PHP_WIN32 23 | #include 24 | #include 25 | #endif 26 | 27 | const char HARDCODED_INI[] = 28 | "html_errors=0\n" 29 | "display_errors=Off\n" 30 | "log_errors=Off\n" 31 | "register_argc_argv=1\n" 32 | "implicit_flush=1\n" 33 | "output_buffering=0\n" 34 | "max_execution_time=0\n" 35 | "max_input_time=-1\n\0"; 36 | 37 | #if defined(PHP_WIN32) && defined(ZTS) 38 | ZEND_TSRMLS_CACHE_DEFINE() 39 | #endif 40 | 41 | static char* php_embed_read_cookies(void) 42 | { 43 | return NULL; 44 | } 45 | 46 | static int php_embed_deactivate(void) 47 | { 48 | fflush(stdout); 49 | return SUCCESS; 50 | } 51 | 52 | static inline size_t php_embed_single_write(const char *str, size_t str_length) 53 | { 54 | #ifdef PHP_WRITE_STDOUT 55 | zend_long ret; 56 | 57 | ret = write(STDOUT_FILENO, str, str_length); 58 | if (ret <= 0) return 0; 59 | return ret; 60 | #else 61 | size_t ret; 62 | 63 | ret = fwrite(str, 1, MIN(str_length, 16384), stdout); 64 | return ret; 65 | #endif 66 | } 67 | 68 | 69 | static size_t php_embed_ub_write(const char *str, size_t str_length) 70 | { 71 | const char *ptr = str; 72 | size_t remaining = str_length; 73 | size_t ret; 74 | 75 | while (remaining > 0) { 76 | ret = php_embed_single_write(ptr, remaining); 77 | if (!ret) { 78 | php_handle_aborted_connection(); 79 | } 80 | ptr += ret; 81 | remaining -= ret; 82 | } 83 | 84 | return str_length; 85 | } 86 | 87 | static void php_embed_flush(void *server_context) 88 | { 89 | if (fflush(stdout)==EOF) { 90 | php_handle_aborted_connection(); 91 | } 92 | } 93 | 94 | static void php_embed_send_header(sapi_header_struct *sapi_header, void *server_context) 95 | { 96 | } 97 | 98 | static void php_embed_log_message(char *message, int syslog_type_int) 99 | { 100 | // fprintf (stderr, "%s\n", message); 101 | } 102 | 103 | static void php_embed_register_variables(zval *track_vars_array) 104 | { 105 | php_import_environment_variables(track_vars_array); 106 | } 107 | 108 | static int php_embed_startup(sapi_module_struct *sapi_module) 109 | { 110 | if (php_module_startup(sapi_module, NULL, 0)==FAILURE) { 111 | return FAILURE; 112 | } 113 | return SUCCESS; 114 | } 115 | 116 | EMBED_SAPI_API sapi_module_struct php_embed_module = { 117 | "embed", /* name */ 118 | "PHP Embedded Library", /* pretty name */ 119 | 120 | php_embed_startup, /* startup */ 121 | php_module_shutdown_wrapper, /* shutdown */ 122 | 123 | NULL, /* activate */ 124 | php_embed_deactivate, /* deactivate */ 125 | 126 | php_embed_ub_write, /* unbuffered write */ 127 | php_embed_flush, /* flush */ 128 | NULL, /* get uid */ 129 | NULL, /* getenv */ 130 | 131 | php_error, /* error handler */ 132 | 133 | NULL, /* header handler */ 134 | NULL, /* send headers handler */ 135 | php_embed_send_header, /* send header handler */ 136 | 137 | NULL, /* read POST data */ 138 | php_embed_read_cookies, /* read Cookies */ 139 | 140 | php_embed_register_variables, /* register server variables */ 141 | php_embed_log_message, /* Log message */ 142 | NULL, /* Get request time */ 143 | NULL, /* Child terminate */ 144 | 145 | STANDARD_SAPI_MODULE_PROPERTIES 146 | }; 147 | /* }}} */ 148 | 149 | /* {{{ arginfo ext/standard/dl.c */ 150 | ZEND_BEGIN_ARG_INFO(arginfo_dl, 0) 151 | ZEND_ARG_INFO(0, extension_filename) 152 | ZEND_END_ARG_INFO() 153 | /* }}} */ 154 | 155 | static const zend_function_entry additional_functions[] = { 156 | ZEND_FE(dl, arginfo_dl) 157 | {NULL, NULL, NULL} 158 | }; 159 | 160 | EMBED_SAPI_API int php_embed_init_ex(int argc, char **argv) 161 | { 162 | zend_llist global_vars; 163 | 164 | #ifdef HAVE_SIGNAL_H 165 | #if defined(SIGPIPE) && defined(SIG_IGN) 166 | signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so 167 | that sockets created via fsockopen() 168 | don't kill PHP if the remote site 169 | closes it. in apache|apxs mode apache 170 | does that for us! thies@thieso.net 171 | 20000419 */ 172 | #endif 173 | #endif 174 | 175 | #ifdef ZTS 176 | tsrm_startup(1, 1, 0, NULL); 177 | (void)ts_resource(0); 178 | ZEND_TSRMLS_CACHE_UPDATE(); 179 | #endif 180 | 181 | zend_signal_startup(); 182 | 183 | sapi_startup(&php_embed_module); 184 | 185 | #ifdef PHP_WIN32 186 | _fmode = _O_BINARY; /*sets default for file streams to binary */ 187 | setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */ 188 | setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */ 189 | setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */ 190 | #endif 191 | 192 | php_embed_module.ini_entries = malloc(sizeof(HARDCODED_INI)); 193 | memcpy(php_embed_module.ini_entries, HARDCODED_INI, sizeof(HARDCODED_INI)); 194 | 195 | php_embed_module.additional_functions = additional_functions; 196 | 197 | if (argv) { 198 | php_embed_module.executable_location = argv[0]; 199 | } 200 | 201 | if (php_embed_module.startup(&php_embed_module)==FAILURE) { 202 | return FAILURE; 203 | } 204 | 205 | zend_llist_init(&global_vars, sizeof(char *), NULL, 0); 206 | 207 | /* Set some Embedded PHP defaults */ 208 | SG(options) |= SAPI_OPTION_NO_CHDIR; 209 | SG(request_info).argc=argc; 210 | SG(request_info).argv=argv; 211 | 212 | if (php_request_startup()==FAILURE) { 213 | php_module_shutdown(); 214 | return FAILURE; 215 | } 216 | 217 | SG(headers_sent) = 1; 218 | SG(request_info).no_headers = 1; 219 | php_register_variable("PHP_SELF", "-", NULL); 220 | 221 | return SUCCESS; 222 | } 223 | 224 | EMBED_SAPI_API void php_embed_shutdown_ex(void) 225 | { 226 | php_request_shutdown((void *) 0); 227 | php_module_shutdown(); 228 | sapi_shutdown(); 229 | #ifdef ZTS 230 | tsrm_shutdown(); 231 | #endif 232 | if (php_embed_module.ini_entries) { 233 | free(php_embed_module.ini_entries); 234 | php_embed_module.ini_entries = NULL; 235 | } 236 | } 237 | 238 | /* 239 | * Local variables: 240 | * tab-width: 4 241 | * c-basic-offset: 4 242 | * End: 243 | * vim600: sw=4 ts=4 fdm=marker 244 | * vim<600: sw=4 ts=4 245 | */ 246 | -------------------------------------------------------------------------------- /src/php_to_bytecode_converter/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "zend.h" 6 | #include "php_embed_ex.h" 7 | #include "php_cfg.h" 8 | #include "php_dfg.h" 9 | #include "php_ssa.h" 10 | 11 | void zend_accel_move_user_functions (HashTable *src, HashTable *dst); 12 | 13 | static bool generate_function_object (const char *class_name, 14 | const char *function_name, 15 | const zend_op_array *op_array, 16 | zend_file_handle *file_handle, 17 | PyObject *fn_pobject); 18 | 19 | static bool generate_basic_info (const zend_op_array *op_array, 20 | PyObject *fn_pobject); 21 | 22 | static bool generate_extra_info (const zend_op_array *op_array, 23 | PyObject *fn_pobject); 24 | 25 | static bool generate_instructions_info (const zend_op_array *op_array, 26 | PyObject *fn_pobject); 27 | 28 | static bool generate_cfg_info (const zend_op_array *op_array, 29 | zend_file_handle *file_handle, 30 | PyObject *fn_pobject); 31 | 32 | static bool generate_dfg_info (const zend_op_array *op_array, zend_cfg *cfg, 33 | PyObject *fn_pobject); 34 | 35 | static bool generate_ssa_info (const zend_op_array *op_array, zend_cfg *cfg, 36 | zend_file_handle *file_handle, 37 | PyObject *fn_pobject); 38 | 39 | static PyObject *zval_to_python_object (zval *val); 40 | 41 | static PyObject * 42 | method_convert (PyObject *self, PyObject *args) 43 | { 44 | char *filename = NULL; 45 | PyObject *zend_functions_pyobject = PyList_New (0); 46 | 47 | /* Parse arguments */ 48 | if (!PyArg_ParseTuple (args, "s", &filename)) 49 | { 50 | return zend_functions_pyobject; 51 | } 52 | 53 | zend_script script; 54 | zend_op_array *op_array = NULL; 55 | int php_embed_argc = 0; 56 | char **php_embed_argv = NULL; 57 | 58 | PHP_EMBED_START_BLOCK (php_embed_argc, php_embed_argv); 59 | 60 | zend_file_handle file_handle; 61 | file_handle.filename = filename; 62 | file_handle.free_filename = 0; 63 | file_handle.type = ZEND_HANDLE_FILENAME; 64 | file_handle.opened_path = NULL; 65 | 66 | // initialize function_table & class_table 67 | zend_hash_init (&script.function_table, 128, NULL, ZEND_FUNCTION_DTOR, 0); 68 | zend_hash_init (&script.class_table, 16, NULL, ZEND_CLASS_DTOR, 0); 69 | 70 | op_array = zend_compile_file (&file_handle, ZEND_INCLUDE TSRMLS_CC); 71 | if (!op_array) 72 | { 73 | printf ("Error parsing php script file: %s\n", filename); 74 | goto cleanup_e; 75 | } 76 | 77 | PyObject *fn_pobject = PyDict_New (); 78 | bool status = generate_function_object ("", "main", op_array, &file_handle, 79 | fn_pobject); 80 | if (status == true) 81 | { 82 | PyList_Append (zend_functions_pyobject, fn_pobject); 83 | } 84 | 85 | zend_accel_move_user_functions (CG (function_table), 86 | &script.function_table); 87 | zend_op_array *iter_op_array; 88 | ZEND_HASH_FOREACH_PTR (&script.function_table, iter_op_array) 89 | { 90 | PyObject *fn_pobject = PyDict_New (); 91 | bool status = generate_function_object ( 92 | "", ZSTR_VAL (iter_op_array->function_name), iter_op_array, 93 | &file_handle, fn_pobject); 94 | if (status == true) 95 | { 96 | PyList_Append (zend_functions_pyobject, fn_pobject); 97 | } 98 | } 99 | ZEND_HASH_FOREACH_END (); 100 | 101 | EG (class_table) = CG (class_table); 102 | zend_class_entry *ce; 103 | zend_string *name; 104 | ZEND_HASH_FOREACH_PTR (EG (class_table), ce) 105 | { 106 | if (ce->type == ZEND_USER_CLASS) 107 | { 108 | ZEND_HASH_FOREACH_STR_KEY_PTR (&ce->function_table, name, 109 | iter_op_array) 110 | { 111 | if (iter_op_array->scope == ce) 112 | { 113 | PyObject *fn_pobject = PyDict_New (); 114 | bool status = generate_function_object ( 115 | ZSTR_VAL (ce->name), 116 | ZSTR_VAL (iter_op_array->function_name), 117 | iter_op_array, &file_handle, fn_pobject); 118 | if (status == true) 119 | { 120 | PyList_Append (zend_functions_pyobject, 121 | fn_pobject); 122 | } 123 | } 124 | } 125 | ZEND_HASH_FOREACH_END (); 126 | } 127 | } 128 | ZEND_HASH_FOREACH_END (); 129 | 130 | cleanup_e: 131 | zend_hash_destroy (&script.function_table); 132 | zend_hash_destroy (&script.class_table); 133 | 134 | if (op_array) 135 | { 136 | destroy_op_array (op_array TSRMLS_CC); 137 | efree (op_array); 138 | } 139 | 140 | PHP_EMBED_END_BLOCK (); 141 | return zend_functions_pyobject; 142 | } 143 | 144 | void 145 | zend_accel_move_user_functions (HashTable *src, HashTable *dst) 146 | { 147 | Bucket *p; 148 | dtor_func_t orig_dtor = src->pDestructor; 149 | 150 | src->pDestructor = NULL; 151 | zend_hash_extend (dst, dst->nNumUsed + src->nNumUsed, 0); 152 | ZEND_HASH_REVERSE_FOREACH_BUCKET (src, p) 153 | { 154 | zend_function *function = Z_PTR (p->val); 155 | 156 | if (EXPECTED (function->type == ZEND_USER_FUNCTION)) 157 | { 158 | _zend_hash_append_ptr (dst, p->key, function); 159 | zend_hash_del_bucket (src, p); 160 | } 161 | else 162 | { 163 | break; 164 | } 165 | } 166 | ZEND_HASH_FOREACH_END (); 167 | src->pDestructor = orig_dtor; 168 | } 169 | 170 | static bool 171 | generate_function_object (const char *class_name, const char *function_name, 172 | const zend_op_array *op_array, 173 | zend_file_handle *file_handle, PyObject *fn_pobject) 174 | { 175 | bool status = true; 176 | 177 | PyDict_SetItem (fn_pobject, Py_BuildValue ("s", "class_name"), 178 | Py_BuildValue ("s", class_name)); 179 | 180 | PyDict_SetItem (fn_pobject, Py_BuildValue ("s", "function_name"), 181 | Py_BuildValue ("s", function_name)); 182 | 183 | status = generate_basic_info (op_array, fn_pobject); 184 | if (status == false) 185 | { 186 | return false; 187 | } 188 | 189 | status = generate_extra_info (op_array, fn_pobject); 190 | if (status == false) 191 | { 192 | return false; 193 | } 194 | 195 | status = generate_instructions_info (op_array, fn_pobject); 196 | if (status == false) 197 | { 198 | return false; 199 | } 200 | 201 | // printf("\n"); 202 | status = generate_cfg_info (op_array, file_handle, fn_pobject); 203 | if (status == false) 204 | { 205 | return false; 206 | } 207 | return status; 208 | } 209 | 210 | static bool 211 | generate_basic_info (const zend_op_array *op_array, PyObject *fn_pobject) 212 | { 213 | // op_array->num_args 214 | PyDict_SetItem (fn_pobject, Py_BuildValue ("s", "num_args"), 215 | Py_BuildValue ("i", op_array->num_args)); 216 | 217 | // op_array->required_num_args 218 | PyDict_SetItem (fn_pobject, Py_BuildValue ("s", "required_num_args"), 219 | Py_BuildValue ("i", op_array->required_num_args)); 220 | 221 | // op_array->last 222 | PyDict_SetItem (fn_pobject, Py_BuildValue ("s", "number_of_instructions"), 223 | Py_BuildValue ("i", op_array->last)); 224 | 225 | // op_array->filename 226 | if (op_array->filename) 227 | PyDict_SetItem (fn_pobject, Py_BuildValue ("s", "filename"), 228 | Py_BuildValue ("s", ZSTR_VAL (op_array->filename))); 229 | else 230 | PyDict_SetItem (fn_pobject, Py_BuildValue ("s", "filename"), 231 | Py_BuildValue ("s", "")); 232 | return true; 233 | } 234 | 235 | static bool 236 | generate_extra_info (const zend_op_array *op_array, PyObject *fn_pobject) 237 | { 238 | PyObject *fn_extra_pobject = PyDict_New (); 239 | 240 | // op_array->type 241 | PyDict_SetItem (fn_extra_pobject, Py_BuildValue ("s", "type"), 242 | Py_BuildValue ("i", op_array->type)); 243 | 244 | // op_array->cache_size 245 | PyDict_SetItem (fn_extra_pobject, Py_BuildValue ("s", "cache_size"), 246 | Py_BuildValue ("i", op_array->cache_size)); 247 | 248 | // op_array->last_var 249 | PyDict_SetItem (fn_extra_pobject, 250 | Py_BuildValue ("s", "number_of_cv_variables"), 251 | Py_BuildValue ("i", op_array->last_var)); 252 | 253 | // op_array->T 254 | PyDict_SetItem (fn_extra_pobject, 255 | Py_BuildValue ("s", "number_of_tmp_variables"), 256 | Py_BuildValue ("i", op_array->T)); 257 | 258 | // op_array->last_live_range 259 | PyDict_SetItem (fn_extra_pobject, Py_BuildValue ("s", "last_live_range"), 260 | Py_BuildValue ("i", op_array->last_live_range)); 261 | 262 | // op_array->last_try_catch 263 | PyDict_SetItem (fn_extra_pobject, Py_BuildValue ("s", "last_try_catch"), 264 | Py_BuildValue ("i", op_array->last_try_catch)); 265 | 266 | // op_array->arg_flags 267 | PyObject *arg_flags = PyList_New (0); 268 | PyList_Append (arg_flags, Py_BuildValue ("i", op_array->arg_flags[0])); 269 | 270 | PyList_Append (arg_flags, Py_BuildValue ("i", op_array->arg_flags[1])); 271 | 272 | PyList_Append (arg_flags, Py_BuildValue ("i", op_array->arg_flags[2])); 273 | 274 | PyDict_SetItem (fn_extra_pobject, Py_BuildValue ("s", "arg_flags"), 275 | arg_flags); 276 | 277 | // op_array->last_literal 278 | PyDict_SetItem (fn_extra_pobject, Py_BuildValue ("s", "last_literal"), 279 | Py_BuildValue ("i", op_array->last_literal)); 280 | 281 | // op_array->literals 282 | PyObject *literals_array_pyobject = PyList_New (0); 283 | if (op_array->literals) 284 | { 285 | int j; 286 | for (j = 0; j < op_array->last_literal; j++) 287 | { 288 | zval *literal_val = &op_array->literals[j]; 289 | PyList_Append (literals_array_pyobject, 290 | zval_to_python_object (literal_val)); 291 | } 292 | } 293 | PyDict_SetItem (fn_extra_pobject, Py_BuildValue ("s", "literals"), 294 | literals_array_pyobject); 295 | 296 | PyDict_SetItem (fn_pobject, Py_BuildValue ("s", "extra"), fn_extra_pobject); 297 | return true; 298 | } 299 | 300 | static int 301 | zval_string_is_ascii (zval *val) 302 | { 303 | int status = 1; 304 | for (int i = 0; i < Z_STRLEN_P (val); ++i) 305 | { 306 | if ((!isascii (Z_STRVAL_P (val)[i]) 307 | && (!isprint (Z_STRVAL_P (val)[i])))) 308 | { 309 | status = 0; 310 | break; 311 | } 312 | } 313 | return status; 314 | } 315 | 316 | static PyObject * 317 | zval_to_python_object (zval *val) 318 | { 319 | PyObject *zval_pyobject = PyDict_New (); 320 | char *decode = NULL; 321 | 322 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "type"), 323 | Py_BuildValue ("s", "unknown")); 324 | 325 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "value"), 326 | Py_BuildValue ("s", "")); 327 | 328 | // types defined in Zend/zend_types.h 329 | switch (Z_TYPE_P (val)) 330 | { 331 | case IS_STRING: 332 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "type"), 333 | Py_BuildValue ("s", "string")); 334 | 335 | // printf("==== %ld, %s %d\n", 336 | // Z_STRLEN_P(val), 337 | // Z_STRVAL_P(val), 338 | // zval_string_is_ascii(val)); 339 | 340 | if (zval_string_is_ascii (val)) 341 | { 342 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "value"), 343 | Py_BuildValue ("s", Z_STRVAL_P (val))); 344 | } 345 | else 346 | { 347 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "value"), 348 | Py_BuildValue ("s", "")); 349 | } 350 | 351 | break; 352 | case IS_DOUBLE: 353 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "type"), 354 | Py_BuildValue ("s", "integer")); 355 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "value"), 356 | Py_BuildValue ("i", Z_DVAL_P (val))); 357 | break; 358 | case IS_LONG: 359 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "type"), 360 | Py_BuildValue ("s", "integer")); 361 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "value"), 362 | Py_BuildValue ("i", Z_LVAL_P (val))); 363 | break; 364 | case IS_TRUE: 365 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "type"), 366 | Py_BuildValue ("s", "boolean")); 367 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "value"), 368 | Py_BuildValue ("s", "true")); 369 | break; 370 | case IS_FALSE: 371 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "type"), 372 | Py_BuildValue ("s", "boolean")); 373 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "value"), 374 | Py_BuildValue ("s", "false")); 375 | break; 376 | case IS_UNDEF: 377 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "type"), 378 | Py_BuildValue ("s", "undef")); 379 | break; 380 | case IS_RESOURCE: 381 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "type"), 382 | Py_BuildValue ("s", "resource")); 383 | 384 | spprintf (&decode, 0, "Rsrc #%d", Z_RES_HANDLE_P (val)); 385 | 386 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "value"), 387 | Py_BuildValue ("s", decode)); 388 | break; 389 | case IS_REFERENCE: 390 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "type"), 391 | Py_BuildValue ("s", "reference")); 392 | break; 393 | case IS_OBJECT: 394 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "type"), 395 | Py_BuildValue ("s", "object")); 396 | 397 | zend_string *str = Z_OBJCE_P (val)->name; 398 | spprintf (&decode, 0, "%s", ZSTR_VAL (str)); 399 | 400 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "value"), 401 | Py_BuildValue ("s", decode)); 402 | break; 403 | case IS_ARRAY: 404 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "type"), 405 | Py_BuildValue ("s", "array")); 406 | 407 | spprintf (&decode, 0, "array(%d)", 408 | zend_hash_num_elements (Z_ARR_P (val))); 409 | 410 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "value"), 411 | Py_BuildValue ("s", decode)); 412 | break; 413 | case IS_NULL: 414 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "type"), 415 | Py_BuildValue ("s", "null")); 416 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "value"), 417 | Py_BuildValue ("s", estrdup ("null"))); 418 | break; 419 | default: 420 | spprintf (&decode, 0, "unknown type: %d", Z_TYPE_P (val)); 421 | 422 | PyDict_SetItem (zval_pyobject, Py_BuildValue ("s", "value"), 423 | Py_BuildValue ("s", decode)); 424 | break; 425 | } 426 | return zval_pyobject; 427 | } 428 | 429 | static PyObject * 430 | get_znode_operand (const zend_op_array *op_array, const zend_op *opline, 431 | znode_op op, zend_uchar op_type, uint32_t flags) 432 | { 433 | /*************************************** 434 | typedef union _znode_op { 435 | uint32_t constant; 436 | uint32_t var; 437 | uint32_t num; 438 | uint32_t opline_num; 439 | #if ZEND_USE_ABS_JMP_ADDR 440 | zend_op *jmp_addr; 441 | #else 442 | uint32_t jmp_offset; 443 | #endif 444 | #if ZEND_USE_ABS_CONST_ADDR 445 | zval *zv; 446 | #endif 447 | } znode_op; 448 | ***************************************/ 449 | 450 | PyObject *znode_pyobject = PyDict_New (); 451 | 452 | PyDict_SetItem (znode_pyobject, Py_BuildValue ("s", "type"), 453 | Py_BuildValue ("s", "UNKNOWN")); 454 | 455 | PyDict_SetItem (znode_pyobject, Py_BuildValue ("s", "value"), 456 | Py_BuildValue ("s", "")); 457 | 458 | PyDict_SetItem (znode_pyobject, Py_BuildValue ("s", "variable_number"), 459 | Py_BuildValue ("i", EX_VAR_TO_NUM (op.var))); 460 | 461 | if (op_type == IS_UNUSED) 462 | { 463 | char *decode = NULL; 464 | 465 | if (ZEND_VM_OP_JMP_ADDR == (flags & ZEND_VM_OP_MASK)) 466 | { 467 | // spprintf(&decode, 0, "J%td", OP_JMP_ADDR(opline, op) - 468 | // op_array->opcodes); 469 | } 470 | else if (ZEND_VM_OP_NUM == (flags & ZEND_VM_OP_MASK)) 471 | { 472 | spprintf (&decode, 0, "%" PRIu32, op.num); 473 | } 474 | else if (ZEND_VM_OP_TRY_CATCH == (flags & ZEND_VM_OP_MASK)) 475 | { 476 | if (op.num != (uint32_t)-1) 477 | { 478 | spprintf (&decode, 0, "try-catch(%" PRIu32 ")", 479 | op.num); 480 | } 481 | } 482 | else if (ZEND_VM_OP_THIS == (flags & ZEND_VM_OP_MASK)) 483 | { 484 | decode = estrdup ("THIS"); 485 | } 486 | else if (ZEND_VM_OP_NEXT == (flags & ZEND_VM_OP_MASK)) 487 | { 488 | decode = estrdup ("NEXT"); 489 | } 490 | else if (ZEND_VM_OP_CLASS_FETCH == (flags & ZEND_VM_OP_MASK)) 491 | { 492 | } 493 | else if (ZEND_VM_OP_CONSTRUCTOR == (flags & ZEND_VM_OP_MASK)) 494 | { 495 | decode = estrdup ("CONSTRUCTOR"); 496 | } 497 | 498 | PyDict_SetItem (znode_pyobject, Py_BuildValue ("s", "type"), 499 | Py_BuildValue ("s", "IS_UNUSED")); 500 | 501 | PyDict_SetItem (znode_pyobject, Py_BuildValue ("s", "value"), 502 | Py_BuildValue ("s", decode)); 503 | } 504 | 505 | if (op_type == IS_CONST) 506 | { 507 | PyDict_SetItem (znode_pyobject, Py_BuildValue ("s", "type"), 508 | Py_BuildValue ("s", "IS_CONST")); 509 | 510 | PyDict_SetItem (znode_pyobject, Py_BuildValue ("s", "value"), 511 | Py_BuildValue ("s", "")); 512 | 513 | zval *const_literal = RT_CONSTANT (opline, op); 514 | if (const_literal) 515 | { 516 | PyObject *const_literal_pyobject 517 | = zval_to_python_object (const_literal); 518 | 519 | PyDict_SetItem ( 520 | znode_pyobject, Py_BuildValue ("s", "value"), 521 | PyDict_GetItem (const_literal_pyobject, 522 | Py_BuildValue ("s", "value"))); 523 | } 524 | } 525 | 526 | if (op_type == IS_TMP_VAR) 527 | { 528 | char *decode = NULL; 529 | spprintf (&decode, 0, "T%u", EX_VAR_TO_NUM (op.var)); 530 | 531 | PyDict_SetItem (znode_pyobject, Py_BuildValue ("s", "type"), 532 | Py_BuildValue ("s", "IS_TMP_VAR")); 533 | 534 | PyDict_SetItem (znode_pyobject, Py_BuildValue ("s", "value"), 535 | Py_BuildValue ("s", decode)); 536 | } 537 | 538 | if (op_type == IS_VAR) 539 | { 540 | char *decode = NULL; 541 | spprintf (&decode, 0, "@%u", EX_VAR_TO_NUM (op.var)); 542 | 543 | PyDict_SetItem (znode_pyobject, Py_BuildValue ("s", "type"), 544 | Py_BuildValue ("s", "IS_VAR")); 545 | 546 | PyDict_SetItem (znode_pyobject, Py_BuildValue ("s", "value"), 547 | Py_BuildValue ("s", decode)); 548 | } 549 | 550 | if (op_type == IS_CV) 551 | { 552 | char *decode = NULL; 553 | zend_string *var = op_array->vars[EX_VAR_TO_NUM (op.var)]; 554 | 555 | spprintf (&decode, 0, "$%.*s%c", 556 | ZSTR_LEN (var) <= 19 ? (int)ZSTR_LEN (var) : 18, 557 | ZSTR_VAL (var), ZSTR_LEN (var) <= 19 ? 0 : '+'); 558 | 559 | PyDict_SetItem (znode_pyobject, Py_BuildValue ("s", "type"), 560 | Py_BuildValue ("s", "IS_CV")); 561 | 562 | PyDict_SetItem (znode_pyobject, Py_BuildValue ("s", "value"), 563 | Py_BuildValue ("s", decode)); 564 | } 565 | 566 | return znode_pyobject; 567 | } 568 | 569 | static bool 570 | generate_instructions_info (const zend_op_array *op_array, PyObject *fn_pobject) 571 | { 572 | PyObject *insns_pyobject = PyList_New (0); 573 | for (int i = 0; i < op_array->last; i++) 574 | { 575 | zend_op *op = &op_array->opcodes[i]; 576 | uint32_t flags = zend_get_opcode_flags (op->opcode); 577 | 578 | /*************************************************************************** 579 | // php-7.3.31 580 | struct _zend_op { 581 | const void *handler; 582 | znode_op op1; // input operand 583 | znode_op op2; // input operand 584 | znode_op result; // one output operand result 585 | uint32_t extended_value; 586 | uint32_t lineno; 587 | zend_uchar opcode; // determining the instruction type 588 | zend_uchar op1_type; 589 | zend_uchar op2_type; 590 | zend_uchar result_type; 591 | }; 592 | *****************************************************************************/ 593 | PyObject *dict = PyDict_New (); 594 | 595 | // op->op1 596 | PyDict_SetItem (dict, Py_BuildValue ("s", "op1"), 597 | get_znode_operand (op_array, op, op->op1, 598 | op->op1_type, 599 | ZEND_VM_OP1_FLAGS (flags))); 600 | 601 | // op->op2 602 | PyDict_SetItem (dict, Py_BuildValue ("s", "op2"), 603 | get_znode_operand (op_array, op, op->op2, 604 | op->op2_type, 605 | ZEND_VM_OP2_FLAGS (flags))); 606 | 607 | // op->result 608 | switch (op->opcode) 609 | { 610 | case ZEND_CATCH: 611 | if (op->extended_value & ZEND_LAST_CATCH) 612 | { 613 | PyObject *znode_pyobject = PyDict_New (); 614 | PyDict_SetItem (dict, Py_BuildValue ("s", "op2"), 615 | znode_pyobject); 616 | } 617 | PyDict_SetItem (dict, Py_BuildValue ("s", "result"), 618 | get_znode_operand (op_array, op, op->result, 619 | op->result_type, -1)); 620 | break; 621 | default: 622 | PyDict_SetItem (dict, Py_BuildValue ("s", "result"), 623 | get_znode_operand (op_array, op, op->result, 624 | op->result_type, -1)); 625 | break; 626 | } 627 | 628 | // num 629 | PyDict_SetItem (dict, Py_BuildValue ("s", "num"), 630 | Py_BuildValue ("i", i)); 631 | 632 | // op->extended_value 633 | PyDict_SetItem (dict, Py_BuildValue ("s", "extended_value"), 634 | Py_BuildValue ("i", op->extended_value)); 635 | 636 | // op->lineno 637 | PyDict_SetItem (dict, Py_BuildValue ("s", "lineno"), 638 | Py_BuildValue ("i", op->lineno)); 639 | 640 | // op->opcode 641 | PyDict_SetItem (dict, Py_BuildValue ("s", "opcode"), 642 | Py_BuildValue ("i", op->opcode)); 643 | 644 | // op->opcode_name 645 | const char *opcode_name = zend_get_opcode_name (op->opcode); 646 | PyDict_SetItem (dict, Py_BuildValue ("s", "opcode_name"), 647 | Py_BuildValue ("s", "")); 648 | 649 | if (opcode_name) 650 | { 651 | PyDict_SetItem (dict, Py_BuildValue ("s", "opcode_name"), 652 | Py_BuildValue ("s", opcode_name)); 653 | } 654 | 655 | // op->opcode_flags 656 | PyDict_SetItem ( 657 | dict, Py_BuildValue ("s", "opcode_flags"), 658 | Py_BuildValue ("i", zend_get_opcode_flags (op->opcode))); 659 | 660 | // op->op1_type 661 | PyDict_SetItem (dict, Py_BuildValue ("s", "op1_type"), 662 | Py_BuildValue ("i", op->op1_type)); 663 | 664 | // op->op2_type 665 | PyDict_SetItem (dict, Py_BuildValue ("s", "op2_type"), 666 | Py_BuildValue ("i", op->op2_type)); 667 | 668 | // op->result_type 669 | PyDict_SetItem (dict, Py_BuildValue ("s", "result_type"), 670 | Py_BuildValue ("i", op->result_type)); 671 | 672 | PyList_Append (insns_pyobject, dict); 673 | } 674 | 675 | PyDict_SetItem (fn_pobject, Py_BuildValue ("s", "instructions"), 676 | insns_pyobject); 677 | return true; 678 | } 679 | 680 | static bool 681 | generate_cfg_info (const zend_op_array *op_array, zend_file_handle *file_handle, 682 | PyObject *fn_pobject) 683 | { 684 | PyObject *cfg_pyobject = PyDict_New (); 685 | 686 | zend_cfg cfg; 687 | zend_arena *arena = zend_arena_create (64 * 1024); 688 | void *checkpoint = zend_arena_checkpoint (arena); 689 | 690 | if (zend_build_cfg (&arena, op_array, ZEND_CFG_SPLIT_AT_LIVE_RANGES, &cfg) 691 | != SUCCESS) 692 | { 693 | zend_arena_release (&arena, checkpoint); 694 | printf ("Error building cfg\n"); 695 | return false; 696 | } 697 | 698 | if (zend_cfg_build_predecessors (&arena, &cfg) != SUCCESS) 699 | { 700 | printf ("Error building cfg predecessors\n"); 701 | return false; 702 | } 703 | 704 | PyDict_SetItem (cfg_pyobject, Py_BuildValue ("s", "blocks_count"), 705 | Py_BuildValue ("i", cfg.blocks_count)); 706 | 707 | PyDict_SetItem (cfg_pyobject, Py_BuildValue ("s", "edges_count"), 708 | Py_BuildValue ("i", cfg.edges_count)); 709 | 710 | PyObject *cfg_blocks_pyobject = PyList_New (0); 711 | if (cfg.blocks) 712 | { 713 | int k; 714 | for (k = 0; k < cfg.blocks_count; k++) 715 | { 716 | PyObject *block_pyobject = PyDict_New (); 717 | zend_basic_block *block = &cfg.blocks[k]; 718 | 719 | // first opcode number 720 | PyDict_SetItem (block_pyobject, 721 | Py_BuildValue ("s", "start"), 722 | Py_BuildValue ("i", block->start)); 723 | 724 | // number of opcodes 725 | PyDict_SetItem (block_pyobject, Py_BuildValue ("s", "len"), 726 | Py_BuildValue ("i", block->len)); 727 | 728 | // number of successors 729 | PyDict_SetItem ( 730 | block_pyobject, Py_BuildValue ("s", "successors_count"), 731 | Py_BuildValue ("i", block->successors_count)); 732 | 733 | // number of predecessors 734 | PyDict_SetItem ( 735 | block_pyobject, 736 | Py_BuildValue ("s", "predecessors_count"), 737 | Py_BuildValue ("i", block->predecessors_count)); 738 | 739 | // offset of 1-st predecessor 740 | PyDict_SetItem ( 741 | block_pyobject, 742 | Py_BuildValue ("s", "predecessor_offset"), 743 | Py_BuildValue ("i", block->predecessor_offset)); 744 | 745 | // immediate dominator block 746 | PyDict_SetItem (block_pyobject, Py_BuildValue ("s", "idom"), 747 | Py_BuildValue ("i", block->idom)); 748 | 749 | // closest loop header, or -1 750 | PyDict_SetItem (block_pyobject, 751 | Py_BuildValue ("s", "loop_header"), 752 | Py_BuildValue ("i", block->loop_header)); 753 | 754 | // steps away from the entry in the dom. tree 755 | PyDict_SetItem (block_pyobject, 756 | Py_BuildValue ("s", "level"), 757 | Py_BuildValue ("i", block->level)); 758 | 759 | // list of dominated blocks 760 | PyDict_SetItem (block_pyobject, 761 | Py_BuildValue ("s", "children"), 762 | Py_BuildValue ("i", block->children)); 763 | 764 | // next dominated block 765 | PyDict_SetItem (block_pyobject, 766 | Py_BuildValue ("s", "next_child"), 767 | Py_BuildValue ("i", block->next_child)); 768 | 769 | // up to 2 successor blocks 770 | PyObject *sstorage_pyobject = PyList_New (0); 771 | PyList_Append ( 772 | sstorage_pyobject, 773 | Py_BuildValue ("i", block->successors_storage[0])); 774 | PyList_Append ( 775 | sstorage_pyobject, 776 | Py_BuildValue ("i", block->successors_storage[1])); 777 | PyDict_SetItem (block_pyobject, 778 | Py_BuildValue ("s", "successors_storage"), 779 | sstorage_pyobject); 780 | 781 | // successor block indices 782 | PyObject *successor_block_indices_pyobject = PyList_New (0); 783 | if (block->successors_count > 0) 784 | { 785 | for (int i = 0; i < block->successors_count; i++) 786 | { 787 | PyList_Append ( 788 | successor_block_indices_pyobject, 789 | Py_BuildValue ("i", 790 | block->successors[i])); 791 | } 792 | } 793 | PyDict_SetItem (block_pyobject, 794 | Py_BuildValue ("s", "successors"), 795 | successor_block_indices_pyobject); 796 | PyList_Append (cfg_blocks_pyobject, block_pyobject); 797 | } 798 | } 799 | PyDict_SetItem (cfg_pyobject, Py_BuildValue ("s", "blocks"), 800 | cfg_blocks_pyobject); 801 | 802 | PyObject *predecessors_pyobject = PyList_New (0); 803 | if (cfg.predecessors) 804 | { 805 | int k; 806 | for (k = 0; k < cfg.edges_count; k++) 807 | { 808 | int predecessor = cfg.predecessors[k]; 809 | PyList_Append (predecessors_pyobject, 810 | Py_BuildValue ("i", predecessor)); 811 | } 812 | } 813 | PyDict_SetItem (cfg_pyobject, Py_BuildValue ("s", "predecessors"), 814 | predecessors_pyobject); 815 | 816 | PyDict_SetItem (fn_pobject, Py_BuildValue ("s", "cfg"), cfg_pyobject); 817 | 818 | bool dfg_status = generate_dfg_info (op_array, &cfg, fn_pobject); 819 | if (dfg_status == false) 820 | { 821 | return false; 822 | } 823 | 824 | bool ssa_status 825 | = generate_ssa_info (op_array, &cfg, file_handle, fn_pobject); 826 | if (ssa_status == false) 827 | { 828 | return false; 829 | } 830 | 831 | zend_arena_release (&arena, checkpoint); 832 | zend_arena_destroy (arena); 833 | return true; 834 | } 835 | 836 | static void 837 | zend_dump_variable (const zend_op_array *op_array, int var_num, 838 | PyObject *var_pyobject) 839 | { 840 | PyDict_SetItem (var_pyobject, Py_BuildValue ("s", "var_num"), 841 | Py_BuildValue ("i", var_num)); 842 | 843 | if (var_num < op_array->last_var) 844 | { 845 | PyDict_SetItem (var_pyobject, Py_BuildValue ("s", "var_name"), 846 | Py_BuildValue ("s", op_array->vars[var_num]->val)); 847 | } 848 | else 849 | { 850 | PyDict_SetItem (var_pyobject, Py_BuildValue ("s", "var_name"), 851 | Py_BuildValue ("s", "")); 852 | } 853 | } 854 | 855 | static PyObject * 856 | zend_dump_var_set (const zend_op_array *op_array, const char *name, 857 | zend_bitset set) 858 | { 859 | int first = 1; 860 | uint32_t i; 861 | PyObject *var_set_pyobject_vars = PyList_New (0); 862 | 863 | for (i = 0; i < op_array->last_var + op_array->T; i++) 864 | { 865 | if (zend_bitset_in (set, i)) 866 | { 867 | if (first) 868 | { 869 | first = 0; 870 | } 871 | PyObject *var_value = PyDict_New (); 872 | zend_dump_variable (op_array, i, var_value); 873 | PyList_Append (var_set_pyobject_vars, var_value); 874 | } 875 | } 876 | return var_set_pyobject_vars; 877 | } 878 | 879 | static bool 880 | generate_dfg_info (const zend_op_array *op_array, zend_cfg *cfg, 881 | PyObject *fn_pobject) 882 | { 883 | zend_dfg dfg; 884 | int blocks_count = cfg->blocks_count; 885 | uint32_t set_size; 886 | uint32_t build_flags = 0; 887 | ALLOCA_FLAG (dfg_use_heap) 888 | 889 | dfg.vars = op_array->last_var + op_array->T; 890 | dfg.size = set_size = zend_bitset_len (dfg.vars); 891 | dfg.tmp 892 | = do_alloca ((set_size * sizeof (zend_ulong)) * (blocks_count * 4 + 1), 893 | dfg_use_heap); 894 | memset (dfg.tmp, 0, 895 | (set_size * sizeof (zend_ulong)) * (blocks_count * 4 + 1)); 896 | dfg.def = dfg.tmp + set_size; 897 | dfg.use = dfg.def + set_size * blocks_count; 898 | dfg.in = dfg.use + set_size * blocks_count; 899 | dfg.out = dfg.in + set_size * blocks_count; 900 | 901 | if (zend_build_dfg (op_array, cfg, &dfg, build_flags) != SUCCESS) 902 | { 903 | free_alloca (dfg.tmp, dfg_use_heap); 904 | return false; 905 | } 906 | 907 | PyObject *dfg_pyobject = PyList_New (0); 908 | if (cfg->blocks) 909 | { 910 | for (int j = 0; j < cfg->blocks_count; j++) 911 | { 912 | 913 | PyObject *var_set_value_def = zend_dump_var_set ( 914 | op_array, "def", DFG_BITSET (dfg.def, dfg.size, j)); 915 | PyObject *var_set_value_use = zend_dump_var_set ( 916 | op_array, "use", DFG_BITSET (dfg.use, dfg.size, j)); 917 | PyObject *var_set_value_in = zend_dump_var_set ( 918 | op_array, "in ", DFG_BITSET (dfg.in, dfg.size, j)); 919 | PyObject *var_set_value_out = zend_dump_var_set ( 920 | op_array, "out", DFG_BITSET (dfg.out, dfg.size, j)); 921 | PyObject *var_set_value_tmp = zend_dump_var_set ( 922 | op_array, "tmp", DFG_BITSET (dfg.tmp, dfg.size, j)); 923 | 924 | PyObject *var_set_values_pyobject = PyDict_New (); 925 | 926 | PyDict_SetItem (var_set_values_pyobject, 927 | Py_BuildValue ("s", "block_index"), 928 | Py_BuildValue ("i", j)); 929 | 930 | PyDict_SetItem (var_set_values_pyobject, 931 | Py_BuildValue ("s", "var_def"), 932 | var_set_value_def); 933 | 934 | PyDict_SetItem (var_set_values_pyobject, 935 | Py_BuildValue ("s", "var_use"), 936 | var_set_value_use); 937 | 938 | PyDict_SetItem (var_set_values_pyobject, 939 | Py_BuildValue ("s", "var_in"), 940 | var_set_value_in); 941 | 942 | PyDict_SetItem (var_set_values_pyobject, 943 | Py_BuildValue ("s", "var_out"), 944 | var_set_value_out); 945 | 946 | PyDict_SetItem (var_set_values_pyobject, 947 | Py_BuildValue ("s", "var_tmp"), 948 | var_set_value_tmp); 949 | 950 | PyList_Append (dfg_pyobject, var_set_values_pyobject); 951 | } 952 | } 953 | 954 | PyDict_SetItem (fn_pobject, Py_BuildValue ("s", "dfg"), dfg_pyobject); 955 | return true; 956 | } 957 | 958 | static void 959 | zend_ssa_pi_constraint_to_pyobject (zend_ssa_pi_constraint *constraint, 960 | PyObject *constraint_pyobject) 961 | { 962 | PyDict_SetItem (constraint_pyobject, Py_BuildValue ("s", "range_range_min"), 963 | Py_BuildValue ("i", constraint->range.range.min)); 964 | 965 | PyDict_SetItem (constraint_pyobject, Py_BuildValue ("s", "range_range_max"), 966 | Py_BuildValue ("i", constraint->range.range.max)); 967 | 968 | PyDict_SetItem (constraint_pyobject, 969 | Py_BuildValue ("s", "range_range_underflow"), 970 | Py_BuildValue ("i", constraint->range.range.underflow)); 971 | 972 | PyDict_SetItem (constraint_pyobject, 973 | Py_BuildValue ("s", "range_range_overflow"), 974 | Py_BuildValue ("i", constraint->range.range.overflow)); 975 | 976 | PyDict_SetItem (constraint_pyobject, Py_BuildValue ("s", "range_min_var"), 977 | Py_BuildValue ("i", constraint->range.min_var)); 978 | 979 | PyDict_SetItem (constraint_pyobject, Py_BuildValue ("s", "range_max_var"), 980 | Py_BuildValue ("i", constraint->range.max_var)); 981 | 982 | // ((min_var>0) ? MIN(ssa_var) : 0) + range.min 983 | PyDict_SetItem (constraint_pyobject, 984 | Py_BuildValue ("s", "range_min_ssa_var"), 985 | Py_BuildValue ("i", constraint->range.min_ssa_var)); 986 | 987 | // ((max_var>0) ? MAX(ssa_var) : 0) + range.max 988 | PyDict_SetItem (constraint_pyobject, 989 | Py_BuildValue ("s", "range_max_ssa_var"), 990 | Py_BuildValue ("i", constraint->range.max_ssa_var)); 991 | 992 | // zend_ssa_negative_lat check = POS_INF; 993 | switch (constraint->range.negative) 994 | { 995 | case NEG_NONE: 996 | PyDict_SetItem (constraint_pyobject, 997 | Py_BuildValue ("s", "range_negative"), 998 | Py_BuildValue ("s", "NEG_NONE")); 999 | break; 1000 | case NEG_INIT: 1001 | PyDict_SetItem (constraint_pyobject, 1002 | Py_BuildValue ("s", "range_negative"), 1003 | Py_BuildValue ("s", "NEG_INIT")); 1004 | break; 1005 | case NEG_INVARIANT: 1006 | PyDict_SetItem (constraint_pyobject, 1007 | Py_BuildValue ("s", "range_negative"), 1008 | Py_BuildValue ("s", "NEG_INVARIANT")); 1009 | break; 1010 | case NEG_USE_LT: 1011 | PyDict_SetItem (constraint_pyobject, 1012 | Py_BuildValue ("s", "range_negative"), 1013 | Py_BuildValue ("s", "NEG_USE_LT")); 1014 | break; 1015 | case NEG_USE_GT: 1016 | PyDict_SetItem (constraint_pyobject, 1017 | Py_BuildValue ("s", "range_negative"), 1018 | Py_BuildValue ("s", "NEG_USE_GT")); 1019 | break; 1020 | default: 1021 | PyDict_SetItem (constraint_pyobject, 1022 | Py_BuildValue ("s", "range_negative"), 1023 | Py_BuildValue ("s", "NEG_UNKNOWN")); 1024 | break; 1025 | } 1026 | } 1027 | 1028 | static void 1029 | zend_ssa_phi_to_pyobject (const zend_op_array *op_array, const zend_ssa *ssa, 1030 | zend_ssa_phi *phi, PyObject *phi_pyobject) 1031 | { 1032 | // if >= 0 this is actually a e-SSA Pi 1033 | PyDict_SetItem (phi_pyobject, Py_BuildValue ("s", "pi"), 1034 | Py_BuildValue ("i", phi->pi)); 1035 | 1036 | // Original CV, VAR or TMP variable index 1037 | PyDict_SetItem (phi_pyobject, Py_BuildValue ("s", "variable_index"), 1038 | Py_BuildValue ("i", phi->var)); 1039 | 1040 | PyObject *var_pyobject = PyDict_New (); 1041 | if (phi->var >= 0) 1042 | { 1043 | zend_dump_variable (op_array, phi->var, var_pyobject); 1044 | } 1045 | PyDict_SetItem (phi_pyobject, Py_BuildValue ("s", "variable"), 1046 | var_pyobject); 1047 | 1048 | // SSA variable index 1049 | PyDict_SetItem (phi_pyobject, Py_BuildValue ("s", "ssa_variable_index"), 1050 | Py_BuildValue ("i", phi->ssa_var)); 1051 | 1052 | // current BB index 1053 | PyDict_SetItem (phi_pyobject, Py_BuildValue ("s", "current_block_index"), 1054 | Py_BuildValue ("i", phi->block)); 1055 | 1056 | // flag to avoid recursive processing 1057 | PyDict_SetItem (phi_pyobject, Py_BuildValue ("s", "visited"), 1058 | Py_BuildValue ("i", phi->visited)); 1059 | 1060 | PyDict_SetItem (phi_pyobject, Py_BuildValue ("s", "has_range_constraint"), 1061 | Py_BuildValue ("i", phi->has_range_constraint)); 1062 | 1063 | PyObject *constraint_pyobject = PyDict_New (); 1064 | if (phi->has_range_constraint) 1065 | { 1066 | zend_ssa_pi_constraint_to_pyobject (&phi->constraint, 1067 | constraint_pyobject); 1068 | } 1069 | PyDict_SetItem (phi_pyobject, Py_BuildValue ("s", "constraint"), 1070 | constraint_pyobject); 1071 | 1072 | PyObject *sources_pyobject = PyList_New (0); 1073 | if (phi->sources) 1074 | { 1075 | int current_block_index = phi->block; 1076 | for (int i = 0; 1077 | i < ssa->cfg.blocks[current_block_index].predecessors_count; 1078 | ++i) 1079 | { 1080 | PyList_Append (sources_pyobject, 1081 | Py_BuildValue ("i", phi->sources[i])); 1082 | } 1083 | } 1084 | PyDict_SetItem (phi_pyobject, Py_BuildValue ("s", "sources"), 1085 | sources_pyobject); 1086 | } 1087 | 1088 | static void 1089 | zend_dump_ssa_variable (const zend_op_array *op_array, const zend_ssa *ssa, 1090 | int ssa_var_num, int var_num, PyObject *var_pyobject) 1091 | { 1092 | PyDict_SetItem (var_pyobject, Py_BuildValue ("s", "ssa_var_num"), 1093 | Py_BuildValue ("i", -1)); 1094 | 1095 | if (ssa_var_num >= 0) 1096 | PyDict_SetItem (var_pyobject, Py_BuildValue ("s", "ssa_var_num"), 1097 | Py_BuildValue ("i", ssa_var_num)); 1098 | 1099 | zend_dump_variable (op_array, var_num, var_pyobject); 1100 | 1101 | // opcode that defines this value 1102 | PyDict_SetItem (var_pyobject, Py_BuildValue ("s", "definition"), 1103 | Py_BuildValue ("i", ssa->vars[ssa_var_num].definition)); 1104 | 1105 | // phi that defines this value 1106 | PyObject *definition_phi_pyobject = PyDict_New (); 1107 | if (ssa->vars[ssa_var_num].definition_phi) 1108 | { 1109 | zend_ssa_phi *phi = ssa->vars[ssa_var_num].definition_phi; 1110 | zend_ssa_phi_to_pyobject (op_array, ssa, phi, 1111 | definition_phi_pyobject); 1112 | } 1113 | PyDict_SetItem (var_pyobject, Py_BuildValue ("s", "definition_phi"), 1114 | definition_phi_pyobject); 1115 | 1116 | // value doesn't mater (used as op1 in ZEND_ASSIGN) 1117 | PyDict_SetItem (var_pyobject, Py_BuildValue ("s", "no_val"), 1118 | Py_BuildValue ("i", ssa->vars[ssa_var_num].no_val)); 1119 | 1120 | // uses of this value, linked through opN_use_chain 1121 | PyDict_SetItem (var_pyobject, Py_BuildValue ("s", "use_chain"), 1122 | Py_BuildValue ("i", ssa->vars[ssa_var_num].use_chain)); 1123 | 1124 | PyDict_SetItem (var_pyobject, Py_BuildValue ("s", "escape_state"), 1125 | Py_BuildValue ("i", ssa->vars[ssa_var_num].escape_state)); 1126 | } 1127 | 1128 | static PyObject * 1129 | zend_dump_ssa_variables (const zend_op_array *op_array, const zend_ssa *ssa) 1130 | { 1131 | PyObject *ssa_variables_pyobject = PyList_New (0); 1132 | 1133 | if (ssa->vars) 1134 | { 1135 | for (int ssa_var_num = 0; ssa_var_num < ssa->vars_count; 1136 | ssa_var_num++) 1137 | { 1138 | PyObject *var_pyobject = PyDict_New (); 1139 | zend_dump_ssa_variable (op_array, ssa, ssa_var_num, 1140 | ssa->vars[ssa_var_num].var, 1141 | var_pyobject); 1142 | 1143 | // set scc 1144 | PyDict_SetItem ( 1145 | var_pyobject, 1146 | Py_BuildValue ("s", "strongly_connected_component"), 1147 | Py_BuildValue ("i", ssa->vars[ssa_var_num].scc)); 1148 | 1149 | // set default to false 1150 | PyDict_SetItem ( 1151 | var_pyobject, 1152 | Py_BuildValue ("s", 1153 | "strongly_connected_component_entry"), 1154 | Py_BuildValue ("i", 1)); 1155 | 1156 | if (ssa->vars[ssa_var_num].scc >= 0) 1157 | { 1158 | if (ssa->vars[ssa_var_num].scc_entry) 1159 | { 1160 | // set to true 1161 | PyDict_SetItem ( 1162 | var_pyobject, 1163 | Py_BuildValue ("s", 1164 | "strongly_connected_" 1165 | "component_entry"), 1166 | Py_BuildValue ("i", 0)); 1167 | } 1168 | } 1169 | 1170 | PyList_Append (ssa_variables_pyobject, var_pyobject); 1171 | } 1172 | } 1173 | return ssa_variables_pyobject; 1174 | } 1175 | 1176 | static PyObject * 1177 | zend_dump_ssa_instructions (const zend_op_array *op_array, const zend_ssa *ssa) 1178 | { 1179 | PyObject *ssa_instructions_pyobject = PyList_New (0); 1180 | 1181 | /* 1182 | op1_use -> op1_def 1183 | op2_use -> op2_def 1184 | result_use -> result_def 1185 | 1186 | takes *_use 1187 | *_def: maps to def if greater than -1. creates a def of that variable stored 1188 | in _def 1189 | 1190 | */ 1191 | 1192 | if (ssa->ops) 1193 | { 1194 | for (int i = op_array->last - 1; i >= 0; i--) 1195 | { 1196 | zend_ssa_op *op = ssa->ops + i; 1197 | PyObject *op_pyobject = PyDict_New (); 1198 | 1199 | PyDict_SetItem (op_pyobject, Py_BuildValue ("s", "op1_use"), 1200 | Py_BuildValue ("i", op->op1_use)); 1201 | 1202 | PyDict_SetItem (op_pyobject, Py_BuildValue ("s", "op2_use"), 1203 | Py_BuildValue ("i", op->op2_use)); 1204 | 1205 | PyDict_SetItem (op_pyobject, 1206 | Py_BuildValue ("s", "result_use"), 1207 | Py_BuildValue ("i", op->result_use)); 1208 | 1209 | PyDict_SetItem (op_pyobject, Py_BuildValue ("s", "op1_def"), 1210 | Py_BuildValue ("i", op->op1_def)); 1211 | 1212 | PyDict_SetItem (op_pyobject, Py_BuildValue ("s", "op2_def"), 1213 | Py_BuildValue ("i", op->op2_def)); 1214 | 1215 | PyDict_SetItem (op_pyobject, 1216 | Py_BuildValue ("s", "result_def"), 1217 | Py_BuildValue ("i", op->result_def)); 1218 | 1219 | PyDict_SetItem (op_pyobject, 1220 | Py_BuildValue ("s", "op1_use_chain"), 1221 | Py_BuildValue ("i", op->op1_use_chain)); 1222 | 1223 | PyDict_SetItem (op_pyobject, 1224 | Py_BuildValue ("s", "op2_use_chain"), 1225 | Py_BuildValue ("i", op->op2_use_chain)); 1226 | 1227 | PyDict_SetItem (op_pyobject, 1228 | Py_BuildValue ("s", "res_use_chain"), 1229 | Py_BuildValue ("i", op->res_use_chain)); 1230 | 1231 | PyList_Append (ssa_instructions_pyobject, op_pyobject); 1232 | } 1233 | } 1234 | return ssa_instructions_pyobject; 1235 | } 1236 | 1237 | static PyObject * 1238 | zend_dump_ssa_blocks (const zend_op_array *op_array, const zend_ssa *ssa) 1239 | { 1240 | PyObject *ssa_blocks_pyobject = PyList_New (0); 1241 | 1242 | if (ssa->blocks) 1243 | { 1244 | for (int i = 0; i < ssa->cfg.blocks_count; i++) 1245 | { 1246 | PyObject *block_pyobject = PyDict_New (); 1247 | 1248 | PyDict_SetItem (block_pyobject, 1249 | Py_BuildValue ("s", "block_index"), 1250 | Py_BuildValue ("i", i)); 1251 | 1252 | PyObject *phis_pyobject = PyList_New (0); 1253 | zend_ssa_phi *phi = ssa->blocks[i].phis; 1254 | while (phi) 1255 | { 1256 | PyObject *phi_pyobject = PyDict_New (); 1257 | zend_ssa_phi_to_pyobject (op_array, ssa, phi, 1258 | phi_pyobject); 1259 | 1260 | PyDict_SetItem (block_pyobject, 1261 | Py_BuildValue ("s", "phis"), 1262 | phi_pyobject); 1263 | 1264 | PyList_Append (phis_pyobject, phi_pyobject); 1265 | phi = phi->next; 1266 | } 1267 | 1268 | PyDict_SetItem (block_pyobject, Py_BuildValue ("s", "phis"), 1269 | phis_pyobject); 1270 | 1271 | PyList_Append (ssa_blocks_pyobject, block_pyobject); 1272 | } 1273 | } 1274 | return ssa_blocks_pyobject; 1275 | } 1276 | 1277 | static PyObject * 1278 | zend_dump_zend_ssa (const zend_op_array *op_array, const zend_ssa *ssa) 1279 | { 1280 | PyObject *ssa_pyobject = PyDict_New (); 1281 | 1282 | // number of SCCs 1283 | PyDict_SetItem (ssa_pyobject, Py_BuildValue ("s", "number_of_sccs"), 1284 | Py_BuildValue ("i", ssa->sccs)); 1285 | 1286 | // number of SSA variables 1287 | PyDict_SetItem (ssa_pyobject, 1288 | Py_BuildValue ("s", "number_of_ssa_variables"), 1289 | Py_BuildValue ("i", ssa->vars_count)); 1290 | 1291 | // dump ssa vars (ssa->vars) 1292 | PyDict_SetItem (ssa_pyobject, Py_BuildValue ("s", "ssa_variables"), 1293 | zend_dump_ssa_variables (op_array, ssa)); 1294 | 1295 | // dump array of SSA instructions (ssa->ops) 1296 | PyDict_SetItem (ssa_pyobject, Py_BuildValue ("s", "ssa_instructions"), 1297 | zend_dump_ssa_instructions (op_array, ssa)); 1298 | 1299 | // dump array of SSA blocks (ssa->blocks) 1300 | PyDict_SetItem (ssa_pyobject, Py_BuildValue ("s", "ssa_blocks"), 1301 | zend_dump_ssa_blocks (op_array, ssa)); 1302 | 1303 | return ssa_pyobject; 1304 | } 1305 | 1306 | static bool 1307 | generate_ssa_info (const zend_op_array *op_array, zend_cfg *cfg, 1308 | zend_file_handle *file_handle, PyObject *fn_pobject) 1309 | { 1310 | zend_ssa ssa; 1311 | zend_script script; 1312 | uint32_t build_flags = 0; 1313 | 1314 | script.first_early_binding_opline 1315 | = (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) 1316 | ? zend_build_delayed_early_binding_list (op_array) 1317 | : (uint32_t)-1; 1318 | script.main_op_array = *op_array; 1319 | 1320 | if (file_handle->opened_path) 1321 | { 1322 | script.filename = zend_string_copy (file_handle->opened_path); 1323 | } 1324 | else 1325 | { 1326 | script.filename = zend_string_init ( 1327 | file_handle->filename, strlen (file_handle->filename), 0); 1328 | } 1329 | zend_string_hash_val (script.filename); 1330 | 1331 | /* Build SSA */ 1332 | memset (&ssa, 0, sizeof (zend_ssa)); 1333 | 1334 | zend_arena *arena = zend_arena_create (64 * 1024); 1335 | void *checkpoint = zend_arena_checkpoint (arena); 1336 | 1337 | if (zend_build_cfg (&arena, op_array, ZEND_CFG_SPLIT_AT_LIVE_RANGES, 1338 | &ssa.cfg) 1339 | != SUCCESS) 1340 | { 1341 | printf ("Error building ssa cfg\n"); 1342 | zend_arena_release (&arena, checkpoint); 1343 | return false; 1344 | } 1345 | 1346 | if (zend_cfg_build_predecessors (&arena, &ssa.cfg) != SUCCESS) 1347 | { 1348 | printf ("Error building ssa cfg predecessors\n"); 1349 | return false; 1350 | } 1351 | 1352 | if (zend_cfg_compute_dominators_tree (op_array, &ssa.cfg) != SUCCESS) 1353 | { 1354 | printf ("Error building ssa cfg dominators tree\n"); 1355 | zend_arena_release (&arena, checkpoint); 1356 | return false; 1357 | } 1358 | 1359 | if (zend_build_ssa (&arena, &script, op_array, build_flags, &ssa) 1360 | != SUCCESS) 1361 | { 1362 | printf ("Error building ssa\n"); 1363 | zend_arena_release (&arena, checkpoint); 1364 | return false; 1365 | } 1366 | 1367 | if (zend_ssa_compute_use_def_chains (&arena, op_array, &ssa) != SUCCESS) 1368 | { 1369 | printf ("Error building ssa cfg use_def_chains\n"); 1370 | zend_arena_release (&arena, checkpoint); 1371 | return false; 1372 | } 1373 | 1374 | PyObject *ssa_pyobject = zend_dump_zend_ssa (op_array, &ssa); 1375 | PyDict_SetItem (fn_pobject, Py_BuildValue ("s", "ssa"), ssa_pyobject); 1376 | 1377 | zend_arena_release (&arena, checkpoint); 1378 | zend_arena_destroy (arena); 1379 | return true; 1380 | } 1381 | 1382 | static PyMethodDef OpArrayMethods[] 1383 | = { { "convert", method_convert, METH_VARARGS, 1384 | "Compile and Converts PHP code/script to Bytecode, CFG, DFG, SSA" }, 1385 | { NULL, NULL, 0, NULL } }; 1386 | 1387 | 1388 | static struct PyModuleDef OpArrayDefinition 1389 | = { PyModuleDef_HEAD_INIT, "convert", 1390 | "Compile and Convert PHP code/script to Bytecode, CFG, DFG, SSA", -1, 1391 | OpArrayMethods }; 1392 | 1393 | PyMODINIT_FUNC 1394 | PyInit_php_to_bytecode_converter (void) 1395 | { 1396 | Py_Initialize (); 1397 | return PyModule_Create (&OpArrayDefinition); 1398 | } -------------------------------------------------------------------------------- /vulnerabilities/vulnerable_web_apps.md: -------------------------------------------------------------------------------- 1 | # List of vulnerabilties for `Vulnerable Web applications Test cases` 2 | Before running any of the test cases below, you need to set the docker container by following the [Setup In README.md](README.md#setting-up) 3 | 4 | ## Testing with `DVWA` 5 | `Damn Vulnerable Web Application (DVWA)` 6 | 7 | ### Testing 8 | ```sh 9 | cd /app 10 | git clone https://github.com/digininja/DVWA targets/DVWA 11 | python3 detectors/code_injection.py 12 | ``` 13 | 14 | ### Reports 15 | ```sh 16 | [*] ---------------------------------------------------------------------------------------------------- 17 | [*] ---------------------------------------------------------------------------------------------------- 18 | [*] TOTAL_VULNS = 21 19 | [*] 20 | [*] ---------------------------------------------------------------------------------------------------- 21 | [*] ---------------------------------------------------------------------------------------------------- 22 | [*] INDEX = 1 23 | [*] IMPACT = HIGH 24 | [*] VULNERABILTY TYPE = code_injection 25 | [*] VISITED FUNCTION CALLS = 26 | [*] FILE = /app/targets/DVWA/vulnerabilities/view_help.php::main 27 | [*] 28 | [*] --- SOURCE PATH TO VULNERABILTY --- 29 | [*] lineno=14 ------ $id = $_GET[ 'id' ]; 30 | [*] lineno=20 ------ eval( '?>' . file_get_contents( DVWA_WEB_PAGE_TO_ROOT . "vulnerabilities/{$id}/help/help.php" ) . ' 83 | [*] lineno=32 ------ 84 | [*] 85 | [*] 86 | [*] ---------------------------------------------------------------------------------------------------- 87 | [*] ---------------------------------------------------------------------------------------------------- 88 | [*] INDEX = 6 89 | [*] IMPACT = HIGH 90 | [*] VULNERABILTY TYPE = code_injection 91 | [*] VISITED FUNCTION CALLS = 92 | [*] FILE = /app/targets/DVWA/vulnerabilities/captcha/source/low.php::main 93 | [*] 94 | [*] --- SOURCE PATH TO VULNERABILTY --- 95 | [*] lineno=51 ------ $pass_conf = $_POST[ 'password_conf' ]; 96 | [*] lineno=33 ------ 97 | [*] 98 | [*] 99 | [*] ---------------------------------------------------------------------------------------------------- 100 | [*] ---------------------------------------------------------------------------------------------------- 101 | [*] INDEX = 7 102 | [*] IMPACT = HIGH 103 | [*] VULNERABILTY TYPE = code_injection 104 | [*] VISITED FUNCTION CALLS = 105 | [*] FILE = /app/targets/DVWA/vulnerabilities/captcha/source/medium.php::main 106 | [*] 107 | [*] --- SOURCE PATH TO VULNERABILTY --- 108 | [*] lineno=52 ------ $pass_conf = $_POST[ 'password_conf' ]; 109 | [*] lineno=33 ------ 110 | [*] lineno=32 ------ 111 | [*] 112 | [*] 113 | [*] ---------------------------------------------------------------------------------------------------- 114 | [*] ---------------------------------------------------------------------------------------------------- 115 | [*] INDEX = 8 116 | [*] IMPACT = HIGH 117 | [*] VULNERABILTY TYPE = code_injection 118 | [*] VISITED FUNCTION CALLS = 119 | [*] FILE = /app/targets/DVWA/vulnerabilities/captcha/source/medium.php::main 120 | [*] 121 | [*] --- SOURCE PATH TO VULNERABILTY --- 122 | [*] lineno=52 ------ $pass_conf = $_POST[ 'password_conf' ]; 123 | [*] lineno=33 ------ 124 | [*] 125 | [*] 126 | [*] ---------------------------------------------------------------------------------------------------- 127 | [*] ---------------------------------------------------------------------------------------------------- 128 | [*] INDEX = 9 129 | [*] IMPACT = HIGH 130 | [*] VULNERABILTY TYPE = code_injection 131 | [*] VISITED FUNCTION CALLS = 132 | [*] FILE = /app/targets/DVWA/vulnerabilities/csp/source/high.php::main 133 | [*] 134 | [*] --- SOURCE PATH TO VULNERABILTY --- 135 | [*] lineno=10 ------ " . $_POST['include'] . " 136 | [*] 137 | [*] 138 | [*] ---------------------------------------------------------------------------------------------------- 139 | [*] ---------------------------------------------------------------------------------------------------- 140 | [*] INDEX = 10 141 | [*] IMPACT = HIGH 142 | [*] VULNERABILTY TYPE = code_injection 143 | [*] VISITED FUNCTION CALLS = 144 | [*] FILE = /app/targets/DVWA/vulnerabilities/csp/source/impossible.php::main 145 | [*] 146 | [*] --- SOURCE PATH TO VULNERABILTY --- 147 | [*] lineno=11 ------ " . $_POST['include'] . " 148 | [*] 149 | [*] 150 | [*] ---------------------------------------------------------------------------------------------------- 151 | [*] ---------------------------------------------------------------------------------------------------- 152 | [*] INDEX = 11 153 | [*] IMPACT = HIGH 154 | [*] VULNERABILTY TYPE = code_injection 155 | [*] VISITED FUNCTION CALLS = 156 | [*] FILE = /app/targets/DVWA/vulnerabilities/csp/source/jsonp.php::main 157 | [*] 158 | [*] --- SOURCE PATH TO VULNERABILTY --- 159 | [*] lineno=5 ------ $callback = $_GET['callback']; 160 | [*] lineno=12 ------ echo $callback . "(".json_encode($outp).")"; 161 | [*] 162 | [*] 163 | [*] ---------------------------------------------------------------------------------------------------- 164 | [*] ---------------------------------------------------------------------------------------------------- 165 | [*] INDEX = 12 166 | [*] IMPACT = HIGH 167 | [*] VULNERABILTY TYPE = code_injection 168 | [*] VISITED FUNCTION CALLS = 169 | [*] FILE = /app/targets/DVWA/vulnerabilities/csp/source/low.php::main 170 | [*] 171 | [*] --- SOURCE PATH TO VULNERABILTY --- 172 | [*] lineno=15 ------ 173 | [*] 174 | [*] 175 | [*] ---------------------------------------------------------------------------------------------------- 176 | [*] ---------------------------------------------------------------------------------------------------- 177 | [*] INDEX = 13 178 | [*] IMPACT = HIGH 179 | [*] VULNERABILTY TYPE = code_injection 180 | [*] VISITED FUNCTION CALLS = 181 | [*] FILE = /app/targets/DVWA/vulnerabilities/csp/source/medium.php::main 182 | [*] 183 | [*] --- SOURCE PATH TO VULNERABILTY --- 184 | [*] lineno=16 ------ " . $_POST['include'] . " 185 | [*] 186 | [*] 187 | [*] ---------------------------------------------------------------------------------------------------- 188 | [*] ---------------------------------------------------------------------------------------------------- 189 | [*] INDEX = 14 190 | [*] IMPACT = HIGH 191 | [*] VULNERABILTY TYPE = code_injection 192 | [*] VISITED FUNCTION CALLS = 193 | [*] FILE = /app/targets/DVWA/vulnerabilities/exec/source/low.php::main 194 | [*] 195 | [*] --- SOURCE PATH TO VULNERABILTY --- 196 | [*] lineno=5 ------ $target = $_REQUEST[ 'ip' ]; 197 | [*] lineno=10 ------ $cmd = shell_exec( 'ping ' . $target ); 198 | [*] 199 | [*] 200 | [*] ---------------------------------------------------------------------------------------------------- 201 | [*] ---------------------------------------------------------------------------------------------------- 202 | [*] INDEX = 15 203 | [*] IMPACT = HIGH 204 | [*] VULNERABILTY TYPE = code_injection 205 | [*] VISITED FUNCTION CALLS = 206 | [*] FILE = /app/targets/DVWA/vulnerabilities/open_redirect/source/high.php::main 207 | [*] 208 | [*] --- SOURCE PATH TO VULNERABILTY --- 209 | [*] lineno=5 ------ header ("location: " . $_GET['redirect']); 210 | [*] 211 | [*] 212 | [*] ---------------------------------------------------------------------------------------------------- 213 | [*] ---------------------------------------------------------------------------------------------------- 214 | [*] INDEX = 16 215 | [*] IMPACT = HIGH 216 | [*] VULNERABILTY TYPE = code_injection 217 | [*] VISITED FUNCTION CALLS = 218 | [*] FILE = /app/targets/DVWA/vulnerabilities/open_redirect/source/low.php::main 219 | [*] 220 | [*] --- SOURCE PATH TO VULNERABILTY --- 221 | [*] lineno=4 ------ header ("location: " . $_GET['redirect']); 222 | [*] 223 | [*] 224 | [*] ---------------------------------------------------------------------------------------------------- 225 | [*] ---------------------------------------------------------------------------------------------------- 226 | [*] INDEX = 17 227 | [*] IMPACT = HIGH 228 | [*] VULNERABILTY TYPE = code_injection 229 | [*] VISITED FUNCTION CALLS = 230 | [*] FILE = /app/targets/DVWA/vulnerabilities/open_redirect/source/medium.php::main 231 | [*] 232 | [*] --- SOURCE PATH TO VULNERABILTY --- 233 | [*] lineno=11 ------ header ("location: " . $_GET['redirect']); 234 | [*] 235 | [*] 236 | [*] ---------------------------------------------------------------------------------------------------- 237 | [*] ---------------------------------------------------------------------------------------------------- 238 | [*] INDEX = 18 239 | [*] IMPACT = HIGH 240 | [*] VULNERABILTY TYPE = code_injection 241 | [*] VISITED FUNCTION CALLS = 242 | [*] FILE = /app/targets/DVWA/vulnerabilities/sqli/source/low.php::main 243 | [*] 244 | [*] --- SOURCE PATH TO VULNERABILTY --- 245 | [*] lineno=5 ------ $id = $_REQUEST[ 'id' ]; 246 | [*] lineno=10 ------ $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; 247 | [*] 248 | [*] 249 | [*] ---------------------------------------------------------------------------------------------------- 250 | [*] ---------------------------------------------------------------------------------------------------- 251 | [*] INDEX = 19 252 | [*] IMPACT = HIGH 253 | [*] VULNERABILTY TYPE = code_injection 254 | [*] VISITED FUNCTION CALLS = 255 | [*] FILE = /app/targets/DVWA/vulnerabilities/sqli_blind/source/high.php::main 256 | [*] 257 | [*] --- SOURCE PATH TO VULNERABILTY --- 258 | [*] lineno=5 ------ $id = $_COOKIE[ 'id' ]; 259 | [*] lineno=11 ------ $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; 260 | [*] 261 | [*] 262 | [*] ---------------------------------------------------------------------------------------------------- 263 | [*] ---------------------------------------------------------------------------------------------------- 264 | [*] INDEX = 20 265 | [*] IMPACT = HIGH 266 | [*] VULNERABILTY TYPE = code_injection 267 | [*] VISITED FUNCTION CALLS = 268 | [*] FILE = /app/targets/DVWA/vulnerabilities/sqli_blind/source/low.php::main 269 | [*] 270 | [*] --- SOURCE PATH TO VULNERABILTY --- 271 | [*] lineno=5 ------ $id = $_GET[ 'id' ]; 272 | [*] lineno=11 ------ $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; 273 | [*] 274 | [*] 275 | [*] ---------------------------------------------------------------------------------------------------- 276 | [*] ---------------------------------------------------------------------------------------------------- 277 | [*] INDEX = 21 278 | [*] IMPACT = HIGH 279 | [*] VULNERABILTY TYPE = code_injection 280 | [*] VISITED FUNCTION CALLS = 281 | [*] FILE = /app/targets/DVWA/vulnerabilities/xss_r/source/low.php::main 282 | [*] 283 | [*] --- SOURCE PATH TO VULNERABILTY --- 284 | [*] lineno=8 ------ $html .= '
Hello ' . $_GET[ 'name' ] . '
'; 285 | [*] 286 | [*] 287 | [*] ---------------------------------------------------------------------------------------------------- 288 | [*] ---------------------------------------------------------------------------------------------------- 289 | [*] TOTAL_VULNS = 21 290 | [*] 291 | ``` 292 | 293 | ### Fixes 294 | ``` 295 | N/A 296 | ``` 297 | 298 | --- 299 | 300 | ## Testing with `OWASP-vuln-web-app` 301 | `OWASP Vulnerable Web Application Project` 302 | 303 | ### Testing 304 | ```sh 305 | cd /app 306 | git clone https://github.com/OWASP/Vulnerable-Web-Application targets/Vulnerable-Web-Application 307 | python3 detectors/code_injection.py 308 | ``` 309 | 310 | ### Reports 311 | ```sh 312 | [*] ---------------------------------------------------------------------------------------------------- 313 | [*] ---------------------------------------------------------------------------------------------------- 314 | [*] TOTAL_VULNS = 10 315 | [*] 316 | [*] ---------------------------------------------------------------------------------------------------- 317 | [*] ---------------------------------------------------------------------------------------------------- 318 | [*] INDEX = 1 319 | [*] IMPACT = HIGH 320 | [*] VULNERABILTY TYPE = code_injection 321 | [*] VISITED FUNCTION CALLS = 322 | [*] FILE = /app/targets/Vulnerable-Web-Application/FileInclusion/pages/lvl1.php::main 323 | [*] 324 | [*] --- SOURCE PATH TO VULNERABILTY --- 325 | [*] lineno=25 ------ @include($_GET[ 'file' ]); 326 | [*] 327 | [*] 328 | [*] ---------------------------------------------------------------------------------------------------- 329 | [*] ---------------------------------------------------------------------------------------------------- 330 | [*] INDEX = 2 331 | [*] IMPACT = HIGH 332 | [*] VULNERABILTY TYPE = code_injection 333 | [*] VISITED FUNCTION CALLS = 334 | [*] FILE = /app/targets/Vulnerable-Web-Application/FileInclusion/pages/lvl1.php::main 335 | [*] 336 | [*] --- SOURCE PATH TO VULNERABILTY --- 337 | [*] lineno=26 ------ echo"
".$_GET[ 'file' ]."
"; 338 | [*] 339 | [*] 340 | [*] ---------------------------------------------------------------------------------------------------- 341 | [*] ---------------------------------------------------------------------------------------------------- 342 | [*] INDEX = 3 343 | [*] IMPACT = HIGH 344 | [*] VULNERABILTY TYPE = code_injection 345 | [*] VISITED FUNCTION CALLS = 346 | [*] FILE = /app/targets/Vulnerable-Web-Application/FileUpload/fileupload1.php::main 347 | [*] 348 | [*] --- SOURCE PATH TO VULNERABILTY --- 349 | [*] lineno=28 ------ echo "File uploaded /uploads/".$_FILES["file"]["name"]; 350 | [*] 351 | [*] 352 | [*] ---------------------------------------------------------------------------------------------------- 353 | [*] ---------------------------------------------------------------------------------------------------- 354 | [*] INDEX = 4 355 | [*] IMPACT = HIGH 356 | [*] VULNERABILTY TYPE = code_injection 357 | [*] VISITED FUNCTION CALLS = 358 | [*] FILE = /app/targets/Vulnerable-Web-Application/FileUpload/fileupload2.php::main 359 | [*] 360 | [*] --- SOURCE PATH TO VULNERABILTY --- 361 | [*] lineno=37 ------ echo "File uploaded /uploads/".$_FILES["file"]["name"]; 362 | [*] 363 | [*] 364 | [*] ---------------------------------------------------------------------------------------------------- 365 | [*] ---------------------------------------------------------------------------------------------------- 366 | [*] INDEX = 5 367 | [*] IMPACT = HIGH 368 | [*] VULNERABILTY TYPE = code_injection 369 | [*] VISITED FUNCTION CALLS = 370 | [*] FILE = /app/targets/Vulnerable-Web-Application/FileUpload/fileupload3.php::main 371 | [*] 372 | [*] --- SOURCE PATH TO VULNERABILTY --- 373 | [*] lineno=39 ------ echo "File uploaded /uploads/".$_FILES["file"]["name"]; 374 | [*] 375 | [*] 376 | [*] ---------------------------------------------------------------------------------------------------- 377 | [*] ---------------------------------------------------------------------------------------------------- 378 | [*] INDEX = 6 379 | [*] IMPACT = HIGH 380 | [*] VULNERABILTY TYPE = code_injection 381 | [*] VISITED FUNCTION CALLS = 382 | [*] FILE = /app/targets/Vulnerable-Web-Application/SQL/sql1.php::main 383 | [*] 384 | [*] --- SOURCE PATH TO VULNERABILTY --- 385 | [*] lineno=39 ------ $firstname = $_POST["firstname"]; 386 | [*] lineno=40 ------ $sql = "SELECT lastname FROM users WHERE firstname='$firstname'";//String 387 | [*] 388 | [*] 389 | [*] ---------------------------------------------------------------------------------------------------- 390 | [*] ---------------------------------------------------------------------------------------------------- 391 | [*] INDEX = 7 392 | [*] IMPACT = HIGH 393 | [*] VULNERABILTY TYPE = code_injection 394 | [*] VISITED FUNCTION CALLS = 395 | [*] FILE = /app/targets/Vulnerable-Web-Application/SQL/sql2.php::main 396 | [*] 397 | [*] --- SOURCE PATH TO VULNERABILTY --- 398 | [*] lineno=37 ------ $number = $_POST['number']; 399 | [*] lineno=38 ------ $query = "SELECT bookname,authorname FROM books WHERE number = $number"; //Int 400 | [*] 401 | [*] 402 | [*] ---------------------------------------------------------------------------------------------------- 403 | [*] ---------------------------------------------------------------------------------------------------- 404 | [*] INDEX = 8 405 | [*] IMPACT = HIGH 406 | [*] VULNERABILTY TYPE = code_injection 407 | [*] VISITED FUNCTION CALLS = 408 | [*] FILE = /app/targets/Vulnerable-Web-Application/SQL/sql3.php::main 409 | [*] 410 | [*] --- SOURCE PATH TO VULNERABILTY --- 411 | [*] lineno=38 ------ $number = $_POST['number']; 412 | [*] lineno=39 ------ $query = "SELECT bookname,authorname FROM books WHERE number = '$number'"; //Is this same with the level 2? 413 | [*] 414 | [*] 415 | [*] ---------------------------------------------------------------------------------------------------- 416 | [*] ---------------------------------------------------------------------------------------------------- 417 | [*] INDEX = 9 418 | [*] IMPACT = HIGH 419 | [*] VULNERABILTY TYPE = code_injection 420 | [*] VISITED FUNCTION CALLS = 421 | [*] FILE = /app/targets/Vulnerable-Web-Application/SQL/sql6.php::main 422 | [*] 423 | [*] --- SOURCE PATH TO VULNERABILTY --- 424 | [*] lineno=35 ------ $number = $_GET['number']; 425 | [*] lineno=36 ------ $query = "SELECT bookname,authorname FROM books WHERE number = '$number'"; 426 | [*] 427 | [*] 428 | [*] ---------------------------------------------------------------------------------------------------- 429 | [*] ---------------------------------------------------------------------------------------------------- 430 | [*] INDEX = 10 431 | [*] IMPACT = HIGH 432 | [*] VULNERABILTY TYPE = code_injection 433 | [*] VISITED FUNCTION CALLS = 434 | [*] FILE = /app/targets/Vulnerable-Web-Application/XSS/XSS_level1.php::main 435 | [*] 436 | [*] --- SOURCE PATH TO VULNERABILTY --- 437 | [*] lineno=22 ------ echo("Your name is ".$_GET["username"])?> 438 | [*] 439 | [*] 440 | [*] ---------------------------------------------------------------------------------------------------- 441 | [*] ---------------------------------------------------------------------------------------------------- 442 | [*] TOTAL_VULNS = 10 443 | [*] 444 | ``` 445 | 446 | ### Fixes 447 | ``` 448 | N/A 449 | ``` 450 | 451 | --- 452 | 453 | ## Testing with `sqlinjection-training-app` 454 | `Simple SQL Injection Training App` 455 | 456 | ### Testing 457 | ```sh 458 | cd /app 459 | git clone https://github.com/appsecco/sqlinjection-training-app targets/sqlinjection-training-app 460 | python3 detectors/code_injection.py 461 | ``` 462 | 463 | ### Reports 464 | ```sh 465 | [*] ---------------------------------------------------------------------------------------------------- 466 | [*] ---------------------------------------------------------------------------------------------------- 467 | [*] TOTAL_VULNS = 15 468 | [*] 469 | [*] ---------------------------------------------------------------------------------------------------- 470 | [*] ---------------------------------------------------------------------------------------------------- 471 | [*] INDEX = 1 472 | [*] IMPACT = HIGH 473 | [*] VULNERABILTY TYPE = code_injection 474 | [*] VISITED FUNCTION CALLS = 475 | [*] FILE = /app/targets/sqlinjection-training-app/www/blindsqli.php::main 476 | [*] 477 | [*] --- SOURCE PATH TO VULNERABILTY --- 478 | [*] lineno=29 ------ $user = $_GET["user"]; 479 | [*] lineno=30 ------ $q = "Select * from users where username = '".$user."'"; 480 | [*] 481 | [*] 482 | [*] ---------------------------------------------------------------------------------------------------- 483 | [*] ---------------------------------------------------------------------------------------------------- 484 | [*] INDEX = 2 485 | [*] IMPACT = HIGH 486 | [*] VULNERABILTY TYPE = code_injection 487 | [*] VISITED FUNCTION CALLS = 488 | [*] FILE = /app/targets/sqlinjection-training-app/www/login1.php::main 489 | [*] 490 | [*] --- SOURCE PATH TO VULNERABILTY --- 491 | [*] lineno=64 ------ $username = ($_REQUEST['uid']); 492 | [*] lineno=67 ------ $q = "SELECT * FROM users where username='".$username."' AND password = '".md5($pass)."'" ; 493 | [*] 494 | [*] 495 | [*] ---------------------------------------------------------------------------------------------------- 496 | [*] ---------------------------------------------------------------------------------------------------- 497 | [*] INDEX = 3 498 | [*] IMPACT = HIGH 499 | [*] VULNERABILTY TYPE = code_injection 500 | [*] VISITED FUNCTION CALLS = 501 | [*] FILE = /app/targets/sqlinjection-training-app/www/login2.php::main 502 | [*] 503 | [*] --- SOURCE PATH TO VULNERABILTY --- 504 | [*] lineno=65 ------ $username = ($_REQUEST['uid']); 505 | [*] lineno=68 ------ $q = "SELECT * FROM users where (username='".$username."') AND (password = '".md5($pass)."')" ; 506 | [*] 507 | [*] 508 | [*] ---------------------------------------------------------------------------------------------------- 509 | [*] ---------------------------------------------------------------------------------------------------- 510 | [*] INDEX = 4 511 | [*] IMPACT = HIGH 512 | [*] VULNERABILTY TYPE = code_injection 513 | [*] VISITED FUNCTION CALLS = 514 | [*] FILE = /app/targets/sqlinjection-training-app/www/os_sqli.php::main 515 | [*] 516 | [*] --- SOURCE PATH TO VULNERABILTY --- 517 | [*] lineno=30 ------ $user = $_GET["user"]; 518 | [*] lineno=31 ------ $q = "Select * from users where username = '".$user."'"; 519 | [*] 520 | [*] 521 | [*] ---------------------------------------------------------------------------------------------------- 522 | [*] ---------------------------------------------------------------------------------------------------- 523 | [*] INDEX = 5 524 | [*] IMPACT = HIGH 525 | [*] VULNERABILTY TYPE = code_injection 526 | [*] VISITED FUNCTION CALLS = 527 | [*] FILE = /app/targets/sqlinjection-training-app/www/register.php::main 528 | [*] 529 | [*] --- SOURCE PATH TO VULNERABILTY --- 530 | [*] lineno=93 ------ $username = $_REQUEST['uid']; 531 | [*] lineno=98 ------ $q = "INSERT INTO users (username, password, fname, description) values ('".$username."','".md5($pass)."','".$fname."','".$descr."')" ; 532 | [*] 533 | [*] 534 | [*] ---------------------------------------------------------------------------------------------------- 535 | [*] ---------------------------------------------------------------------------------------------------- 536 | [*] INDEX = 6 537 | [*] IMPACT = HIGH 538 | [*] VULNERABILTY TYPE = code_injection 539 | [*] VISITED FUNCTION CALLS = 540 | [*] FILE = /app/targets/sqlinjection-training-app/www/register.php::main 541 | [*] 542 | [*] --- SOURCE PATH TO VULNERABILTY --- 543 | [*] lineno=95 ------ $fname = $_REQUEST['name']; 544 | [*] lineno=98 ------ $q = "INSERT INTO users (username, password, fname, description) values ('".$username."','".md5($pass)."','".$fname."','".$descr."')" ; 545 | [*] 546 | [*] 547 | [*] ---------------------------------------------------------------------------------------------------- 548 | [*] ---------------------------------------------------------------------------------------------------- 549 | [*] INDEX = 7 550 | [*] IMPACT = HIGH 551 | [*] VULNERABILTY TYPE = code_injection 552 | [*] VISITED FUNCTION CALLS = md5 553 | [*] FILE = /app/targets/sqlinjection-training-app/www/register.php::main 554 | [*] 555 | [*] --- SOURCE PATH TO VULNERABILTY --- 556 | [*] lineno=95 ------ $fname = $_REQUEST['name']; 557 | [*] lineno=98 ------ $q = "INSERT INTO users (username, password, fname, description) values ('".$username."','".md5($pass)."','".$fname."','".$descr."')" ; 558 | [*] lineno=93 ------ $username = $_REQUEST['uid']; 559 | [*] 560 | [*] 561 | [*] ---------------------------------------------------------------------------------------------------- 562 | [*] ---------------------------------------------------------------------------------------------------- 563 | [*] INDEX = 8 564 | [*] IMPACT = HIGH 565 | [*] VULNERABILTY TYPE = code_injection 566 | [*] VISITED FUNCTION CALLS = 567 | [*] FILE = /app/targets/sqlinjection-training-app/www/register.php::main 568 | [*] 569 | [*] --- SOURCE PATH TO VULNERABILTY --- 570 | [*] lineno=96 ------ $descr = $_REQUEST['descr']; 571 | [*] lineno=98 ------ $q = "INSERT INTO users (username, password, fname, description) values ('".$username."','".md5($pass)."','".$fname."','".$descr."')" ; 572 | [*] 573 | [*] 574 | [*] ---------------------------------------------------------------------------------------------------- 575 | [*] ---------------------------------------------------------------------------------------------------- 576 | [*] INDEX = 9 577 | [*] IMPACT = HIGH 578 | [*] VULNERABILTY TYPE = code_injection 579 | [*] VISITED FUNCTION CALLS = md5 580 | [*] FILE = /app/targets/sqlinjection-training-app/www/register.php::main 581 | [*] 582 | [*] --- SOURCE PATH TO VULNERABILTY --- 583 | [*] lineno=96 ------ $descr = $_REQUEST['descr']; 584 | [*] lineno=95 ------ $fname = $_REQUEST['name']; 585 | [*] lineno=98 ------ $q = "INSERT INTO users (username, password, fname, description) values ('".$username."','".md5($pass)."','".$fname."','".$descr."')" ; 586 | [*] lineno=93 ------ $username = $_REQUEST['uid']; 587 | [*] 588 | [*] 589 | [*] ---------------------------------------------------------------------------------------------------- 590 | [*] ---------------------------------------------------------------------------------------------------- 591 | [*] INDEX = 10 592 | [*] IMPACT = HIGH 593 | [*] VULNERABILTY TYPE = code_injection 594 | [*] VISITED FUNCTION CALLS = 595 | [*] FILE = /app/targets/sqlinjection-training-app/www/searchproducts.php::main 596 | [*] 597 | [*] --- SOURCE PATH TO VULNERABILTY --- 598 | [*] lineno=56 ------ $q = "Select * from products where product_name like '".$_POST["searchitem"]."%'"; 599 | [*] 600 | [*] 601 | [*] ---------------------------------------------------------------------------------------------------- 602 | [*] ---------------------------------------------------------------------------------------------------- 603 | [*] INDEX = 11 604 | [*] IMPACT = HIGH 605 | [*] VULNERABILTY TYPE = code_injection 606 | [*] VISITED FUNCTION CALLS = 607 | [*] FILE = /app/targets/sqlinjection-training-app/www/secondorder_home.php::main 608 | [*] 609 | [*] --- SOURCE PATH TO VULNERABILTY --- 610 | [*] lineno=117 ------ $username = $_REQUEST['uid']; 611 | [*] lineno=122 ------ $q = "INSERT INTO users (username, password, fname, description) values ('".$username."','".md5($pass)."','".$fname."','".$descr."')" ; 612 | [*] 613 | [*] 614 | [*] ---------------------------------------------------------------------------------------------------- 615 | [*] ---------------------------------------------------------------------------------------------------- 616 | [*] INDEX = 12 617 | [*] IMPACT = HIGH 618 | [*] VULNERABILTY TYPE = code_injection 619 | [*] VISITED FUNCTION CALLS = 620 | [*] FILE = /app/targets/sqlinjection-training-app/www/secondorder_home.php::main 621 | [*] 622 | [*] --- SOURCE PATH TO VULNERABILTY --- 623 | [*] lineno=119 ------ $fname = $_REQUEST['name']; 624 | [*] lineno=122 ------ $q = "INSERT INTO users (username, password, fname, description) values ('".$username."','".md5($pass)."','".$fname."','".$descr."')" ; 625 | [*] 626 | [*] 627 | [*] ---------------------------------------------------------------------------------------------------- 628 | [*] ---------------------------------------------------------------------------------------------------- 629 | [*] INDEX = 13 630 | [*] IMPACT = HIGH 631 | [*] VULNERABILTY TYPE = code_injection 632 | [*] VISITED FUNCTION CALLS = md5 633 | [*] FILE = /app/targets/sqlinjection-training-app/www/secondorder_home.php::main 634 | [*] 635 | [*] --- SOURCE PATH TO VULNERABILTY --- 636 | [*] lineno=119 ------ $fname = $_REQUEST['name']; 637 | [*] lineno=122 ------ $q = "INSERT INTO users (username, password, fname, description) values ('".$username."','".md5($pass)."','".$fname."','".$descr."')" ; 638 | [*] lineno=117 ------ $username = $_REQUEST['uid']; 639 | [*] 640 | [*] 641 | [*] ---------------------------------------------------------------------------------------------------- 642 | [*] ---------------------------------------------------------------------------------------------------- 643 | [*] INDEX = 14 644 | [*] IMPACT = HIGH 645 | [*] VULNERABILTY TYPE = code_injection 646 | [*] VISITED FUNCTION CALLS = 647 | [*] FILE = /app/targets/sqlinjection-training-app/www/secondorder_home.php::main 648 | [*] 649 | [*] --- SOURCE PATH TO VULNERABILTY --- 650 | [*] lineno=120 ------ $descr = $_REQUEST['descr']; 651 | [*] lineno=122 ------ $q = "INSERT INTO users (username, password, fname, description) values ('".$username."','".md5($pass)."','".$fname."','".$descr."')" ; 652 | [*] 653 | [*] 654 | [*] ---------------------------------------------------------------------------------------------------- 655 | [*] ---------------------------------------------------------------------------------------------------- 656 | [*] INDEX = 15 657 | [*] IMPACT = HIGH 658 | [*] VULNERABILTY TYPE = code_injection 659 | [*] VISITED FUNCTION CALLS = md5 660 | [*] FILE = /app/targets/sqlinjection-training-app/www/secondorder_home.php::main 661 | [*] 662 | [*] --- SOURCE PATH TO VULNERABILTY --- 663 | [*] lineno=120 ------ $descr = $_REQUEST['descr']; 664 | [*] lineno=119 ------ $fname = $_REQUEST['name']; 665 | [*] lineno=122 ------ $q = "INSERT INTO users (username, password, fname, description) values ('".$username."','".md5($pass)."','".$fname."','".$descr."')" ; 666 | [*] lineno=117 ------ $username = $_REQUEST['uid']; 667 | [*] 668 | [*] 669 | [*] ---------------------------------------------------------------------------------------------------- 670 | [*] ---------------------------------------------------------------------------------------------------- 671 | [*] TOTAL_VULNS = 15 672 | [*] 673 | 674 | ``` 675 | 676 | ### Fixes 677 | ``` 678 | N/A 679 | ``` 680 | 681 | --- 682 | 683 | ## Testing with `OSTE-Vulnerable-Web-Application` 684 | `Vulnerable Web application made with PHP/SQL designed to help new web testers gain some experience and test DAST tools for identifying web vulnerabilities.` 685 | 686 | ### Testing 687 | ```sh 688 | cd /app 689 | git clone https://github.com/OSTEsayed/OSTE-Vulnerable-Web-Application targets/OSTE-Vulnerable-Web-Application 690 | python3 detectors/code_injection.py 691 | ``` 692 | 693 | ### Reports 694 | ```sh 695 | [*] ---------------------------------------------------------------------------------------------------- 696 | [*] ---------------------------------------------------------------------------------------------------- 697 | [*] TOTAL_VULNS = 6 698 | [*] 699 | [*] ---------------------------------------------------------------------------------------------------- 700 | [*] ---------------------------------------------------------------------------------------------------- 701 | [*] INDEX = 1 702 | [*] IMPACT = HIGH 703 | [*] VULNERABILTY TYPE = code_injection 704 | [*] VISITED FUNCTION CALLS = 705 | [*] FILE = /app/targets/OSTE-Vulnerable-Web-Application/SQL/page1.php::main 706 | [*] 707 | [*] --- SOURCE PATH TO VULNERABILTY --- 708 | [*] lineno=210 ------ $passe=$_POST["pswd"]; 709 | [*] lineno=212 ------ $sql = "INSERT INTO user (name,password) VALUES ('$namee','$passe')"; 710 | [*] 711 | [*] 712 | [*] ---------------------------------------------------------------------------------------------------- 713 | [*] ---------------------------------------------------------------------------------------------------- 714 | [*] INDEX = 2 715 | [*] IMPACT = HIGH 716 | [*] VULNERABILTY TYPE = code_injection 717 | [*] VISITED FUNCTION CALLS = 718 | [*] FILE = /app/targets/OSTE-Vulnerable-Web-Application/SQL/page1.php::main 719 | [*] 720 | [*] --- SOURCE PATH TO VULNERABILTY --- 721 | [*] lineno=209 ------ $namee=$_POST["username"]; 722 | [*] lineno=212 ------ $sql = "INSERT INTO user (name,password) VALUES ('$namee','$passe')"; 723 | [*] 724 | [*] 725 | [*] ---------------------------------------------------------------------------------------------------- 726 | [*] ---------------------------------------------------------------------------------------------------- 727 | [*] INDEX = 3 728 | [*] IMPACT = HIGH 729 | [*] VULNERABILTY TYPE = code_injection 730 | [*] VISITED FUNCTION CALLS = 731 | [*] FILE = /app/targets/OSTE-Vulnerable-Web-Application/SQL/page4.php::main 732 | [*] 733 | [*] --- SOURCE PATH TO VULNERABILTY --- 734 | [*] lineno=212 ------ $namee=$_POST["username"]; 735 | [*] lineno=215 ------ $sql3 = "SELECT * FROM books WHERE author='$namee'";//String 736 | [*] 737 | [*] 738 | [*] ---------------------------------------------------------------------------------------------------- 739 | [*] ---------------------------------------------------------------------------------------------------- 740 | [*] INDEX = 4 741 | [*] IMPACT = HIGH 742 | [*] VULNERABILTY TYPE = code_injection 743 | [*] VISITED FUNCTION CALLS = 744 | [*] FILE = /app/targets/OSTE-Vulnerable-Web-Application/SQL/page5.php::main 745 | [*] 746 | [*] --- SOURCE PATH TO VULNERABILTY --- 747 | [*] lineno=212 ------ $namee=$_POST["username"]; 748 | [*] lineno=215 ------ $sql3 = "SELECT * FROM sport WHERE id='$namee'";//String 749 | [*] 750 | [*] 751 | [*] ---------------------------------------------------------------------------------------------------- 752 | [*] ---------------------------------------------------------------------------------------------------- 753 | [*] INDEX = 5 754 | [*] IMPACT = HIGH 755 | [*] VULNERABILTY TYPE = code_injection 756 | [*] VISITED FUNCTION CALLS = 757 | [*] FILE = /app/targets/OSTE-Vulnerable-Web-Application/SQL/page6.php::main 758 | [*] 759 | [*] --- SOURCE PATH TO VULNERABILTY --- 760 | [*] lineno=212 ------ $namee=$_GET["username"]; 761 | [*] lineno=215 ------ $sql3 = "SELECT * FROM sport WHERE id='$namee'";//String 762 | [*] 763 | [*] 764 | [*] ---------------------------------------------------------------------------------------------------- 765 | [*] ---------------------------------------------------------------------------------------------------- 766 | [*] INDEX = 6 767 | [*] IMPACT = HIGH 768 | [*] VULNERABILTY TYPE = code_injection 769 | [*] VISITED FUNCTION CALLS = 770 | [*] FILE = /app/targets/OSTE-Vulnerable-Web-Application/XSS/page1.php::main 771 | [*] 772 | [*] --- SOURCE PATH TO VULNERABILTY --- 773 | [*] lineno=197 ------ echo$_POST['username']; 774 | [*] 775 | [*] 776 | [*] ---------------------------------------------------------------------------------------------------- 777 | [*] ---------------------------------------------------------------------------------------------------- 778 | [*] TOTAL_VULNS = 6 779 | [*] 780 | ``` 781 | 782 | ### Fixes 783 | ``` 784 | N/A 785 | ``` 786 | 787 | --- 788 | 789 | ## Testing with `InsecureTrust_Bank` 790 | `"InsecureTrust_Bank: Educational repo demonstrating web app vulnerabilities like SQL injection & XSS for security awareness. Use responsibly.` 791 | 792 | ### Testing 793 | ```sh 794 | cd /app 795 | git clone https://github.com/Hritikpatel/InsecureTrust_Bank targets/InsecureTrust_Bank 796 | python3 detectors/code_injection.py 797 | ``` 798 | 799 | ### Reports 800 | ```sh 801 | [*] ---------------------------------------------------------------------------------------------------- 802 | [*] ---------------------------------------------------------------------------------------------------- 803 | [*] TOTAL_VULNS = 5 804 | [*] 805 | [*] ---------------------------------------------------------------------------------------------------- 806 | [*] ---------------------------------------------------------------------------------------------------- 807 | [*] INDEX = 1 808 | [*] IMPACT = HIGH 809 | [*] VULNERABILTY TYPE = code_injection 810 | [*] VISITED FUNCTION CALLS = 811 | [*] FILE = /app/targets/InsecureTrust_Bank/assets/php_process/login_process.php::main 812 | [*] 813 | [*] --- SOURCE PATH TO VULNERABILTY --- 814 | [*] lineno=9 ------ $entered_password = $_POST["password"]; 815 | [*] lineno=27 ------ $sql = "SELECT * FROM logininfo WHERE username = '$entered_username' AND password = '$entered_password'"; 816 | [*] 817 | [*] 818 | [*] ---------------------------------------------------------------------------------------------------- 819 | [*] ---------------------------------------------------------------------------------------------------- 820 | [*] INDEX = 2 821 | [*] IMPACT = HIGH 822 | [*] VULNERABILTY TYPE = code_injection 823 | [*] VISITED FUNCTION CALLS = 824 | [*] FILE = /app/targets/InsecureTrust_Bank/assets/php_process/login_process.php::main 825 | [*] 826 | [*] --- SOURCE PATH TO VULNERABILTY --- 827 | [*] lineno=8 ------ $entered_username = $_POST["username"]; 828 | [*] lineno=27 ------ $sql = "SELECT * FROM logininfo WHERE username = '$entered_username' AND password = '$entered_password'"; 829 | [*] 830 | [*] 831 | [*] ---------------------------------------------------------------------------------------------------- 832 | [*] ---------------------------------------------------------------------------------------------------- 833 | [*] INDEX = 3 834 | [*] IMPACT = HIGH 835 | [*] VULNERABILTY TYPE = code_injection 836 | [*] VISITED FUNCTION CALLS = 837 | [*] FILE = /app/targets/InsecureTrust_Bank/public/faq.php::main 838 | [*] 839 | [*] --- SOURCE PATH TO VULNERABILTY --- 840 | [*] lineno=50 ------ $searchQuery = $_GET[ 'search' ]; 841 | [*] lineno=53 ------ $out = '
 Looking for ' . $searchQuery . '
'; 842 | [*] 843 | [*] 844 | [*] ---------------------------------------------------------------------------------------------------- 845 | [*] ---------------------------------------------------------------------------------------------------- 846 | [*] INDEX = 4 847 | [*] IMPACT = HIGH 848 | [*] VULNERABILTY TYPE = code_injection 849 | [*] VISITED FUNCTION CALLS = 850 | [*] FILE = /app/targets/InsecureTrust_Bank/public/faq.php::main 851 | [*] 852 | [*] --- SOURCE PATH TO VULNERABILTY --- 853 | [*] lineno=50 ------ $searchQuery = $_GET[ 'search' ]; 854 | [*] lineno=53 ------ $out = '
 Looking for ' . $searchQuery . '
'; 855 | [*] lineno=54 ------ echo $out; 856 | [*] 857 | [*] 858 | [*] ---------------------------------------------------------------------------------------------------- 859 | [*] ---------------------------------------------------------------------------------------------------- 860 | [*] INDEX = 5 861 | [*] IMPACT = HIGH 862 | [*] VULNERABILTY TYPE = code_injection 863 | [*] VISITED FUNCTION CALLS = 864 | [*] FILE = /app/targets/InsecureTrust_Bank/toolPvt/supportApi.php::main 865 | [*] 866 | [*] --- SOURCE PATH TO VULNERABILTY --- 867 | [*] lineno=69 ------ $number = $_GET["number"]; 868 | [*] lineno=72 ------ $stmt = $pdo->prepare("SELECT * FROM account WHERE Phone == $number"); 869 | [*] 870 | [*] 871 | [*] ---------------------------------------------------------------------------------------------------- 872 | [*] ---------------------------------------------------------------------------------------------------- 873 | [*] TOTAL_VULNS = 5 874 | [*] 875 | ``` 876 | 877 | ### Fixes 878 | ``` 879 | N/A 880 | ``` 881 | 882 | --- --------------------------------------------------------------------------------