{"id":13462591,"url":"https://github.com/fate0/xmark","last_synced_at":"2025-06-22T23:06:35.124Z","repository":{"id":47005388,"uuid":"149120702","full_name":"fate0/xmark","owner":"fate0","description":"A PHP7 extension that can hook most functions/classes and parts of opcodes","archived":false,"fork":false,"pushed_at":"2021-09-17T17:24:55.000Z","size":160,"stargazers_count":243,"open_issues_count":5,"forks_count":28,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-05-07T15:51:46.236Z","etag":null,"topics":["extension","hook","php7"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fate0.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-09-17T12:19:04.000Z","updated_at":"2025-03-23T04:58:08.000Z","dependencies_parsed_at":"2022-09-16T13:30:27.083Z","dependency_job_id":null,"html_url":"https://github.com/fate0/xmark","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/fate0/xmark","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fate0%2Fxmark","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fate0%2Fxmark/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fate0%2Fxmark/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fate0%2Fxmark/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fate0","download_url":"https://codeload.github.com/fate0/xmark/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fate0%2Fxmark/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261380908,"owners_count":23149966,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["extension","hook","php7"],"created_at":"2024-07-31T12:00:53.028Z","updated_at":"2025-06-22T23:06:30.105Z","avatar_url":"https://github.com/fate0.png","language":"C","funding_links":[],"categories":["Uncategorized","安全检查"],"sub_categories":["Uncategorized"],"readme":"# xmark\n\n[![Build Status](https://travis-ci.org/fate0/xmark.svg?branch=master)](https://travis-ci.org/fate0/xmark)\n[![AppVeyor Status](https://ci.appveyor.com/api/projects/status/github/fate0/xmark?branch=master\u0026svg=true)](https://ci.appveyor.com/project/fate0/xmark/)\n![GitHub](https://img.shields.io/github/license/fate0/xmark.svg)\n\n[中文文档](https://github.com/fate0/xmark/blob/master/README.zh-CN.md)\n\n## Table of Contents\n\n* [Introduction](#introduction)\n* [Installation](#installation)\n* [Example](#example)\n* [API](#api)\n* [OPCODE](#opcode)\n* [PHP configuration](#php-configuration)\n* [Note](#note)\n* [Ref](#ref)\n\n\n### Introduction\n\nxmark is a PHP7 extension that provides the following features：\n\n* It can mark string variables\n* It can hook most functions/classes\n* It can Hook parts of the opcodes\n\n### Installation\n\n* linux:\n\n```\nphpize\n./configure\nmake\n```\n\n* windows\n\n[download](https://github.com/fate0/xmark/releases)\n\n### Example\n\nexample：\n``` php\n\u003c?php\n\nfunction var_dump(...$args) {\n    echo \"in custom var_dump\\n\";\n    _var_dump(...$args);\n}\n\nvar_dump(\"test\");\n```\n\nrun：\n\n`php -d \"extension_dir=/tmp/modules/\" -d \"extension=xmark.so\" -d \"xmark.enable=1\" -d \"xmark.rename_functions=var_dump:_var_dump\" test.php`\n\nresult:\n\n\n![var_dump_example](https://raw.githubusercontent.com/fate0/xmark/master/artwork/var_dump_example.png)\n\n\n[More examples](https://github.com/fate0/xmark/tree/master/tests)\n\n### API\n\n* mark string variables\n```\nbool xmark(string \u0026$str);\n```\n\n* check if the string variable is marked\n```\nbool xcheck(string \u0026$str);\n```\n\n* clear tag on string variables\n```\nbool xclear(string \u0026$str);\n```\n\n* change the name of the user function\n```\nbool xrename_function(string $old_name, string $new_name);\n```\n\n* change the name of the user class\n```\nbool xrename_class(string $old_name, string $new_name);\n```\n\n* register opcode callback\n\n```\nbool xregister_opcode_callback(int $opcode, string $callback);\n```\n$callback must be a function name, not a class method, or any other callable。\n\n\n### OPCODE\n\neach opcode callback has a different function description, so let's explain it one by one。\n\n* `XMARK_ECHO`\n```\nvoid echo_handler(string $string)\n```\n\n* `XMARK_EXIT`\n```\nvoid exit_handler(string $string)\n```\n\n* `XMARK_INIT_METHOD_CALL`\n```\nvoid init_method_call_handler(string $funcname)\n```\n\n* `XMARK_INIT_USER_CALL`\n```\nvoid init_user_call_handler(string $funcname)\n```\n\n* `XMARK_INIT_DYNAMIC_CALL`\n```\nvoid init_dynamic_call_handler(string $funcname)\n```\n\n* `XMARK_INCLUDE_OR_EVAL`\n```\nvoid include_or_eval_handler(string $code_or_file)\n```\n\n* `XMARK_CONCAT`\n```\nstring concat_handler(string $param1, string $param2)\n```\n\n* `XMARK_FAST_CONCAT`\n```\nstring fast_concat_handler(string $param1, string $param2)\n```\n\n* `XMARK_ASSIGN_CONCAT`\n```\nstring assign_concat_handler(string $param1, string $param2)\n```\n\n* `XMARK_ROPE_END`\n```\nvoid rope_end_handler(array $params)\n```\n\n* `XMARK_DO_FCALL`\n```\nvoid do_fcall(string $call, array $params)\n```\n\n* `XMARK_DO_ICALL`\n```\nvoid do_icall(string $call, array $params)\n```\n\n* `XMARK_DO_UCALL`\n```\nvoid do_ucall(string $call, array $params)\n```\n\n* `XMARK_DO_FCALL_BY_NAME`\n```\nvoid do_fcall_by_name(string $call, array $params)\n```\n\n### PHP configuration\n\nenable `xmark` extension:\n\n```\nxmark.enable = 1\n```\n\nenable rename PHP user functions/classes (do not enable this in production envri)\n```\nxmark.enable_rename = 1\n```\n\nrename PHP internal functions:\n```\nxmark.rename_functions=\"\n    phpinfo:my_phpinfo,\n    system:my_system\n\"\n```\n\nrename PHP internal classes:\n\n```\nxmark.rename_classes=\"\n    PDO:my_POD,\n    yyy:_yyy\n\"\n```\n\n### Note\n\n1. `str_replace`\n\n`str_replace` function description:\n```\nmixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int \u0026$count ] )\n```\n\n$count is a reference parameter，so the correct way to hook `str_replace` is as follows：\n```php\nfunction str_replace($search, $replace, $subject, \u0026$count=NULL) {\n    return call_user_func_array(\"origin_str_replace\", array($search, $replace, $subject, \u0026$count));\n}\n\nstr_replace(\"a\", \"e\", \"hallo world\", $count);\nvar_dump($count);\n```\n\nhook other function with reference parameters are also done as above.\n\n2. `strval`\n\n`xmark` can not hook `strval` function. because `strval` is optimized directly in the compile stage,\nand does not need to be searched by `EG(function_table)`，\nother similar functions [zend_compile.c](https://github.com/php/php-src/blob/PHP-7.2.10/Zend/zend_compile.c#L3872)\n\n3. `getallheaders`\n\n`xmark` can not hook `getallheaders` function. because `getallheaders` function is inside the `sapi_module`，\n`sapi_module` initialization time is later than  `php_extension` and `zend_extension`,\nso the functions in `sapi_module` can not be hooked with `xmark`，fortunately, there are only a few functions in `sapi_module`.\n\n4. `extract`\n\n`xmark` can hook `extract` function, but it will affect the original function of `extract`.\nbecause `extract` will change its own variable scope,\n`extract` will look up the last user function int the call stack and then modify its variable scope,\nwhen we rename `extract` to another name and write another `extract` function in PHP,\nthe `extract` function we wrote becomes the last user function in the call stack, so `extract` won't work properly.\n\nother similar functions: [Forbid dynamic calls to scope introspection functions](http://php.net/manual/zh/migration71.incompatible.php#migration71.incompatible.forbid-dynamic-calls-to-scope-introspection-functions)\n\nif you just need to monitor the calls to these functions, then I recommend using opcode callback.\n\n5. `array_map`\n\n`xmark` can also hook `array_map` function，but it may also affect the original function of `array_map`.\nbecause when `array_map` calls the callback, it will determine whether the function calling `array_map` has permission to call the corresponding callback.\n\nexample:\n``` php\n\u003c?php\n\nfunction test($callable, $arr) {\n    array_map($callable, $arr);\n}\n\n\nclass A {\n    public function hi() {\n        test(array($this, 'hello'), array(1, 2));\n    }\n\n    private function hello() {\n        echo \"hello\\n\";\n    }\n}\n\n\n$a = new A();\n$a-\u003ehi();\n```\n\nwhen `array_map` is called, `zend_get_executed_scope` will look up the last use function in the call stack and then\ndetermine if the user function has permission to call the callback. so it may cause the the call to `array_map` to fail.\nthe same problem occurs in other internal functions that accept callback parameter.\n\nin summary, if a function depends on or will change the scope of the caller,\nthen you should carefully determine whether the function can still be hooked.\n\n\n### Ref\n\n* [taint](https://github.com/laruence/taint)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffate0%2Fxmark","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffate0%2Fxmark","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffate0%2Fxmark/lists"}